Vue 刷新某个组件

Vue 刷新某个组件

背景

  • 现在有一个很简单的需求,在 Home 页的右边有一列 ProjectList 和 UserList,上面有一个按钮,点击那个按钮就会换一批数据。
  • 以 Project 为例,我们之前已经准备好了一个 ProjectList 组件,而 ProjectList 又由 ProjectItem 组成,在 Home 页,我们只需要把 API 的地址传入 ProjectList ,它就可以自己请求数据。
  • 逻辑非常简单,但是实际上却有很多坑,首先,在点击刷新以后,绑定传入ProjectList 的 API (定义为变量 src)变化,但是 ProjectList 并不会重新 created,也就无法请求新的数据。

网上主流方法 v-if 刷新

  • 网上有一种主流的方法,利用 v-if ,先将 ifShow 设为 false,然后再设置为 true,这样组件就会重新渲染。
  • 亲测有效,但是有很严重的问题,就是在设为 false 的时候,由于隐藏了,下面的 UserList 就会弹上来,然后设为 true 再弹下去,这样页面就会突然闪一下,体验很差,我们需要另外想办法。

父组件调用子组件方法

  • 我们换一个思路,就是在更改 src 以后,父组件取调用子组件的一个 refresh 函数,在 refresh 函数里面重新调用 API,这样就能更改数据。所以我们要学习一下父组件调用子组件的方法。
  • 首先,子组件要设置一下 ref 属性,这样在 ts 代码中才能找到这个组件,代码如下所示:
<ProjectList
	:src="projectListSrc"
	style="margin-top:7.5%"
	ref="ProjectListComponent"
	@noProject="noProject()"
></ProjectList>
  • 我们主要关注我设置的 ref 参数,这样,这个组件就叫 ProjectListComponent 了,如果我们在 ProjectList 组件里面加了 refresh 函数,就可以通过以下代码调用:
(this.$refs.ProjectListComponent as any).refresh();
  • 注意 :网上的写法大多没有这个 as any,因为那些都是 JavaScript,而我们用的时 TypeScript,TypeScript 里面变量是有类型的,所以必须要用 as any 转换成 any,否则会报错。

延时传入问题

  • 这样以后并没有理想的效果,数据不会变化。经过本人细致而又耐心的调试,发现当我重新复制 src 以后,调用子组件的 refresh,新的 src 并没有传进去,而是产生了延迟。简单说,就是 src 改变代码运行以后,新的 src 还没有来得及传入子组件,子组件的 refresh 就被调用了,然后 src 才改变。顺便提一句,上面的 v-if 方法也会这样,如果连续赋值成 false、true,是没有变化的,必须用下面方法在渲染完成以后,再进行后面的代码。
private refreshUser() {
	this.userListPage += 1;
	this.userListSrc =
		"/api/User/GetRecommendUsers?page=" + this.userListPage + "&len=5";
	this.$nextTick(function() {
		(this.$refs.UserListComponent as any).refresh();
	});
}
  • 没错,这个方法就是 $nextTick,这里面的函数会在上面代码渲染完成以后执行。

子组件的 ref 数组

  • 现实是这样仍然无法达到预期目的,因为在 ProjectItem 这个组件,传入 brief 的时候,在 created 里面,我们把 brief 赋给了变量 data,然后前端渲染的数据都是绑定在 data 上的(如果直接绑在 brief 上出现了 bug)。所以和之前一样,我们需要在 ProjectList 的 refresh 里面调用 ProjectItem 的 refresh 函数,把新的 brief 赋给 data。
  • 那么问题就来了,之前只有一个 ProjectList 组件,现在是 v-for 循环的很多 ProjectItem 组件,我们怎么设置 ref 呢?
<div
	class="column"
	v-for="(projectInformation, index) in projectInformation"
	:key="index"
	:class="columnStyle"
>
	<ProjectItem :brief="projectInformation" ref="ProjectItemComponent"></ProjectItem>
</div>
  • 如上所以,我们还是直接设置 ref 为 ProjectItemComponent,然后用 $refs 找到的变量就是一个数组,所有的 component 都在数组里,用循环即可调用所有 ProjectItem,如下所示:
private refresh() {
	// 传入 src 的refresh
	const that = this;
	Axios.get(this.src).then(function(response) {
		if (response.data.length) {
			that.projectInformation = response.data;
			let itemComponent = that.$refs.ProjectItemComponent as any;
			that.$nextTick(function() {
				for (let i = 0; i < itemComponent.length; i++) {
					itemComponent[i].refresh();
				}
			});
		} else {
        	that.$emit("noProject");
      	}
	});
}
  • 经过两层的 refresh ,问题解决。