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() {
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");
}
});
}