Vue之重新渲染组件的正确方式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue之重新渲染组件的正确方式相关的知识,希望对你有一定的参考价值。

参考技术A 原文: https://michaelnthiessen.com/force-re-render/
以下为翻译篇,如有不正确之处还望留言指正。

当你只想重新渲染某个组件,或者销毁当前DOM并重新开始, 这个时候Vue的响应系统就差点儿意思。那如果是你遇到类似情况的话,会怎么办呢?

最行之有效的办法就是给component设置一个:key,当你需要重新渲染这个组件的时候只需要修改key的值即可。

这难道不是一个相当简单的解决办法吗?

还有一些其他的方法可以实现相同的效果:

如果你想强制重新加载或者强制更新,下面会可能是比较好的方式。
很有可能你对相面的几件事情比较迷惑:
1、Vue的响应式
2、计算属性
3、监听器
4、v-for未使用:key

这里有几个强制刷新的可用Demo,大部分都可以通过key-changing方式解决,关于技术的原理在文章的末尾。

这是一种类似于“你每次想退出app时都要通过重启电脑”的方式。
我猜想这种方式有时也会很有效,但是确实是个比较糟糕的方式,这里就不必多言,不要这么干了,可以看看其他比较好的方式。

Vue的v-if指令只有当true时才会显示,false则会将其从DOM中剔除,接下来我们来看看怎么做的。

流程分析:
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销毁之前的重新创建新的意味着新的组件会重新走一遍生命周期。

和前面的方式对比,这种也是官方支持的、比较好的方式。
正常来讲,Vue会根据依赖的值变化去更新界面,当然,如果你调用 forceUpdate ,就算依赖的值未变化也会强制更新。
这里有个问题: Vue既然能根据值的变化去自动更新,那为啥我们还需要强制更新呢? 这个是因为有的时候Vue的响应系统会有困惑,我们认为Vue会根据某些属性或变量的值变化去更新,结果并没有,而且还有一些其他的情况是响应系统检测不到的。所以如果你有重新渲染组件的需要时这种方式比较好。
需要注意的是: forceUpdate 只会强制更新页面,不会更新现有的计算属性。

在很多种情况下你可能都会有重新渲染组件的需求,为了比较好的解决这种问题,我们将使用 key 属性以便Vue能够将数据和组件建立起关系。如果key不变,就不更新。一旦key发生变化,Vue就会剔除旧的创建新的组件。
使用之前我们先看一下为什么要使用 key !
当你明白了这个问题以后你对如果正确的重新渲染组件会有不小的进步。
假设您要呈现的组件列表具有以下一个或多个特点:

如果你对这个列表进行排序或者更新,你都需要重新渲染列表的部分界面,但是你不想将整个列表重新渲染,而仅仅是将修改的过的进行更新。
为了帮助Vue框架能够监测哪些值发生改变,哪些值未变,我们提供了 key 属性,因为在列表的特殊对象和其索引 index 没有进行绑定,
这儿有个例子:

如果你想通过索引渲染,可能会这么写:

如果我们删除 Sarah ,会得到如下结果:

虽然 James 仍然是 James ,但是和其关联的索引已经改变。 James 会被重新渲染,虽然我们不想那样。
所以在这里,我们想用某种独特的ID,但是我们最终生成它。

在我们将 Sarah 从列表中移除前,Vue为了另外两个先删除组件,然后再给 James 创建一个组件,现在Vue知道可以保留 Evan 和 James 组件,唯一需要做的就是删除 Sarah 的组件.

如果再加上一个人到列表中,它也知道它可以保持现有的所有组件,并且它只能建立一个新的组件,并将其插入正确的位置。这是非常有用的,并帮助了我们很多的时候,我们有更复杂的组件都有自己的状态,必须初始化逻辑,或做任何类型的DOM操作。

也许这弯路也没那么短。但是,有必要解释Vue是如何工作的很关键。

不管怎么说,让我们与强迫重新渲染的最佳方法!
Key-changing to force re-renders of a component

这里是做的一个非常基本的方法:

很多人不知道可以使用这种 key 的方式来对 Vue 组件时行重新渲染

在某些情况下,我们必须强制Vue重新渲染组件,如果没有,那可能,你做的业务还不够负责,反正我是经常需要重新渲染组件,哈哈。

虽然Vue不会自动更新这种情况是相对比较少,但是知道如何在出现这个问题时修复它还是很有用的。

在大多数情况下,此问题根源还是我们对 Vue 的响应式理解还是不够到位。 因此,要尽量确保我们要正确使用了Vue。 响应式有时过于棘手,我也经常不知道所措。

这节,我们就来做一些之前很少做过或者没做过的:用 key 来让组件重新渲染。

在这篇文章中,会涉及到这几个知识点:

  • key 是如何改变组件
  • key 如何与多个子组件一起工作
  • 如何强制子组件自己更新

通过改变 key 的值来重新渲染组件

我最喜欢的方法是使用key属性,因为使用key 的方式,Vue 就知道了特定组件与特定数据相关。

如果 key保持不变,则不会更改组件。 但是,如果key发生更改, Vue 知道它应该删除旧组件并创建一个新组件。

下面是一个非常基本的方法:

<template>
  <ComponentToReRender
    :key="componentKey"
  />
</template>

<script>
  export default {
    data() {
      return {
        componentKey: 0,
      };
    },
    methods: {
      forceRerender() {
        this.componentKey += 1;
      }
    }
  }
</script>
复制代码

每次调用forceRerender时,componentKey 的值就会更改。 当componentKey 的值发生改变时,Vue 就知道把ComponentToReRender组件删除并创建一个新组件。

这样ComponentToReRender就会重新渲染并重置里面的状态。nice nice!

强制多个子节点进行更新

同样用这种方式也可以用于多个子组件:

<template>
  <div>
    <Child
      :key="key1"
    />
    <Child
      :key="key2"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        key1: 0,
        key2: 0,
      };
    },
    methods: {
      forceRerender(child) {
        if (child === 1) {
          this.key1 += 1;
        } else if( child === 2) {
          this.key2 += 1;
        }
      }
    }
  }
</script>
复制代码

这里我们使用了两个单独 key 来分别控制每个子组件是否重新渲染。将它们分开是为了其中的一个子组件渲染,不会影响到另外另一个。

但如果希望两个子组件总是一起更新,则可以使用相同的 kye。但是,key必须是唯一的,所以下面这种方式,不能工作:

<template>
  <div>
    <Child
      :key="componentKey"
    />
    <Child
      :key="componentKey"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        componentKey: 0,
      };
    },
    methods: {
      forceRerender(child) {
        this.componentKey += 1;
      }
    }
  }
</script>
复制代码

在这里,仅第一个Child组件会被渲染。 第二个被忽略,因为它具有重复的key 了。

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

为了解决这个问题,我们可以基于componentKey为每个孩子构造一个新key

<template>
  <div>
    <Child
      :key="`${componentKey}-1`"
    />
    <Child
      :key="`${componentKey}-2`"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        componentKey: 0,
      };
    },
    methods: {
      forceRerender(child) {
        this.componentKey += 1;
      }
    }
  }
</script>
复制代码

因为我们每次在componentKey后面添加-1-2,所以这两个key始终是唯一的,现在这两个组件都将被重新渲染。

如果是在列表中,则可以使用如下方式:

<template>
  <div>
    <Child
      v-for="(item, index) in list"
      :key="`${componentKey}-${index}`"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        list: [
          // ...
        ],
        componentKey: 0,
      };
    },
    methods: {
      forceRerender(child) {
        this.componentKey += 1;
      }
    }
  }
</script>
复制代码

在这里,我们将key构造为${componentKey}-${index},因此列表中的每个项目都会获得唯一的key,只要componentKey一改变,列表中的所有组件将同时重新渲染。

当然,还有更简单的方式,就是用div把列表包裹起来,直接对 div重新更新就行了:

<template>
  <div :key="componentKey">
    <Child
      v-for="item in list"
      :key="item.id"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        list: [
          // ...
        ],
        componentKey: 0,
      };
    },
    methods: {
      forceRerender(child) {
        this.componentKey += 1;
      }
    }
  }
</script>

 

以上是关于Vue之重新渲染组件的正确方式的主要内容,如果未能解决你的问题,请参考以下文章

vue 相邻自定义组件渲染错误正确的打开方式

为啥 Vue 渲染组件不正确?

react学习笔记2之正确使用状态

高阶组件状态正在正确更新,但接收道具的包装组件不会在状态更改时重新渲染

如何正确检测 React JS 中的重新渲染?

vue使用v-for渲染完组件,删除数据时总是删除最后一个解决方案