Vue之重新渲染组件的正确方式
原文:https://michaelnthiessen.com/force-re-render/
以下为翻译篇,如有不正确之处还望留言指正。
当你只想重新渲染某个组件,或者销毁当前DOM并重新开始, 这个时候Vue的响应系统就差点儿意思。那如果是你遇到类似情况的话,会怎么办呢?
最行之有效的办法就是给component设置一个:key,当你需要重新渲染这个组件的时候只需要修改key的值即可。
这难道不是一个相当简单的解决办法吗?
还有一些其他的方法可以实现相同的效果:
- reloading整个页面 【最差的一种方式】
- 使用v-if【一般的方式】
- forceUpdate【比较好的方式】
- key-changing 【最好的方式】
如果你想强制重新加载或者强制更新,下面会可能是比较好的方式。
很有可能你对相面的几件事情比较迷惑:
1、Vue的响应式
2、计算属性
3、监听器
4、v-for未使用:key
这里有几个强制刷新的可用Demo,大部分都可以通过key-changing方式解决,关于技术的原理在文章的末尾。
reloading整个页面
这是一种类似于“你每次想退出app时都要通过重启电脑”的方式。
我猜想这种方式有时也会很有效,但是确实是个比较糟糕的方式,这里就不必多言,不要这么干了,可以看看其他比较好的方式。
v-if
Vue的v-if指令只有当true时才会显示,false则会将其从DOM中剔除,接下来我们来看看怎么做的。
<template>
<my-component v-if="renderComponent"/>
</template>
<script>
export default {
data() {
return {
renderComponent: true,
};
},
methods: {
forceRerender() {
// Remove my-component from the DOM
this.renderComponent = false;
this.$nextTick(() => {
// Add the component back in
this.renderComponent = true;
});
}
}
};
</script>
流程分析:
1、初始化的时候renderComponent值为true,组件渲染
2、当我们调用forceRerender时,renderComponent会立刻变为false,
3、这个时候因为值为false组件就会停止渲染,
4、然后在next tick里面将renderComponent的值重新设置回去,
5、现在组件就会开始重新渲染
上面的流程主要有两个重要的点需要理解:
1、必须要在nextTick以后才能更改,否则会看不到效果
在Vue中,DOM的更新周期即为一个tick,在同一个tick内Vue会搜集变化,然后在tick的最后会根据变化的值去更新节点,如果我们不等到next tick,直接更新变量的值,不会触发节点的更新。
2、当我们重新渲染的时候,Vue将会创建一个新的组件。Vue销毁之前的重新创建新的意味着新的组件会重新走一遍生命周期。
forceUpdate
和前面的方式对比,这种也是官方支持的、比较好的方式。
正常来讲,Vue会根据依赖的值变化去更新界面,当然,如果你调用forceUpdate
,就算依赖的值未变化也会强制更新。
这里有个问题:Vue既然能根据值的变化去自动更新,那为啥我们还需要强制更新呢?
这个是因为有的时候Vue的响应系统会有困惑,我们认为Vue会根据某些属性或变量的值变化去更新,结果并没有,而且还有一些其他的情况是响应系统检测不到的。所以如果你有重新渲染组件的需要时这种方式比较好。
需要注意的是:forceUpdate
只会强制更新页面,不会更新现有的计算属性。
key-changing
在很多种情况下你可能都会有重新渲染组件的需求,为了比较好的解决这种问题,我们将使用key
属性以便Vue能够将数据和组件建立起关系。如果key不变,就不更新。一旦key发生变化,Vue就会剔除旧的创建新的组件。
使用之前我们先看一下为什么要使用key
!
当你明白了这个问题以后你对如果正确的重新渲染组件会有不小的进步。
假设您要呈现的组件列表具有以下一个或多个特点:
- 自有属性
- 初始化处理,比如在
created
或者mounted
钩子函数中 - 通过jQuery或者APIs的非响应式DOM操作
如果你对这个列表进行排序或者更新,你都需要重新渲染列表的部分界面,但是你不想将整个列表重新渲染,而仅仅是将修改的过的进行更新。
为了帮助Vue框架能够监测哪些值发生改变,哪些值未变,我们提供了key
属性,因为在列表的特殊对象和其索引index
没有进行绑定,
这儿有个例子:
const people = [
{ name: 'Evan', age: 34 },
{ name: 'Sarah', age: 98 },
{ name: 'James', age: 45 },
];
如果你想通过索引渲染,可能会这么写:
<ul>
<li v-for="(person, index) in people" :key="index">
{{ person.name }} - {{ index }}
</li>
</ul>
// Outputs
Evan - 0
Sarah - 1
James - 2
如果我们删除Sarah
,会得到如下结果:
Evan - 0
James - 1
虽然James
仍然是James
,但是和其关联的索引已经改变。James
会被重新渲染,虽然我们不想那样。
所以在这里,我们想用某种独特的ID,但是我们最终生成它。
const people = [
{ id: 'this-is-an-id', name: 'Evan', age: 34 },
{ id: 'unique-id', name: 'Sarah', age: 98 },
{ id: 'another-unique-id', name: 'James', age: 45 },
];
<ul>
<li v-for="person in people" :key="person.id">
{{ person.name }} - {{ person.id }}
</li>
</ul>
在我们将 Sarah
从列表中移除前,Vue为了另外两个先删除组件,然后再给James
创建一个组件,现在Vue知道可以保留Evan
和James
组件,唯一需要做的就是删除Sarah
的组件.
如果再加上一个人到列表中,它也知道它可以保持现有的所有组件,并且它只能建立一个新的组件,并将其插入正确的位置。这是非常有用的,并帮助了我们很多的时候,我们有更复杂的组件都有自己的状态,必须初始化逻辑,或做任何类型的DOM操作。
也许这弯路也没那么短。但是,有必要解释Vue是如何工作的很关键。
不管怎么说,让我们与强迫重新渲染的最佳方法!
Key-changing to force re-renders of a component
这里是做的一个非常基本的方法:
<template>
<component-to-re-render :key="componentKey" />
</template>
export default {
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
}