强制Vue重新渲染组件的正确方法

2021-02-20  乐帮网

vuejs

有时Vue的自动刷新还不够用,您或许需要重新渲染组件。也就是重新构建DOM元素。那么,如何使用Vue重新渲染组件呢?
强制Vue重新渲染组件的最佳方法是在组件上设置a :key。当您需要重新渲染组件时,只需更改键的值,Vue就会重新渲染组件。这是一个非常简单的解决方案,对吧?

您会可能还想知道有没有其他方法可以做到这一点:
♥最暴力的方法:重新加载整个页面
♥比较麻烦的方法:使用v-if hack
♥好一点的方法:使用Vue的内置forceUpdate方法
♥最好的方法:在组件上进行键更改
♥除此之外,恐怕再也没有其它的办法了。

如果您确实需要强制加载或强制更新,上面的方法大概是全部方法。
但是您可能会遇到下棘手的事情:

1、Vue的依赖反应链。
2、计算组件属性。
3、Watched 方法中属性依赖。
4、没有:key属性的场景比如嵌套在v-for中。

以上场景都应该在在刷新渲染时考虑到,本文的最底部提供的方法是最好的解决方案。

1)暴力的方式:刷新整个页面
这相当于您每次要关闭应用程序时都重新启动计算机。它是能达到我们的目的,但这是一个非常糟糕的解决方案,关于这一点,实际上没有太多要说的。让我们寻找更好的方法。

2)改进方案:v-if hack
Vue带有v-if指令,该指令仅在组件为true时才呈现。如果为假,则该组件在DOM中根本不存在。这是我们为v-if 设定工作的方式。在您的template中,您可以使用v-if指令:
 

<template>
  <my-component v-if="renderComponent" />
</template>

在您的script中添加使用以下方法nextTick:

<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>

使用该方案的过程是这样工作的:

♥最初renderComponent设置为true,因此my-component呈现
♥当我们调用forceRerender时立即设置renderComponent为false
♥我们停止渲染my-component ,因为该v-if指令现在求值为false
♥在下次显示会触发renderComponent上设置回true
♥所以再一次v-if指令的计算结果为true,因此我们my-component再次开始渲染

在该方案的工作的过程中,有两个很重要的部分。

首先,我们必须等待next tick,否则我们将看不到任何更改。

在Vue中,tick是一个DOM更新周期。Vue将收集在tick中所做的所有更新,并且在tick结束时,它将基于这些更新来更新渲染到DOM中的内容。如果我们不等到下一个tick,我们对于renderComponent的更新将会自动取消,最终什么也不会发生。

其次,当我们第二次渲染时,Vue将创建一个全新的组件。Vue将销毁第一个,并创建一个新的。这意味着我们的新产品my-component将像正常情况一样经历其所有生命周期- created,mounted等等。

附带说明一下,nextTick可以以promises的方式使用如下:

forceRerender() {
  // Remove my-component from the DOM
  this.renderComponent = false;

  // If you like promises better you can
  // also use nextTick this way
  this.$nextTick().then(() => {
    // Add the component back in
    this.renderComponent = true;
  });
}

显然,这并不是一个很好的解决方案。我之所以称其为hack,是因为我们正在利用Vue的机制做一些像黑客一样的事情。
所以才有下面的方法!

3)比较可行的方案:forceUpdate

这是两种最佳方法之一,它和后者均得到Vue的官方支持。通常,Vue将通过更新视图来响应依赖项的变化。但是,当您调用forceUpdate时,即使实际上没有任何依赖关系发生变化,也可以强制执行该更新。这个也是大多数人用这种方法然后掉到大坑里的最主要原因。想一想如果Vue在发生变化时自动更新,为什么我们还需要强制更新?所以正常来说我们强制更新时不会希望触发其它依赖项。

有时候Vue的依赖链系统可能会令人困惑,当我们认为Vue会对特定属性或变量的更改做出反应,但实际上并不会。实际上在某些特殊情况下,Vue的依赖链系统根本不会检测到任何变化。就像后两种方法一样,如果您使用此方法重新渲染组件,可能会更好。

forceUpdate在组件实例本身以及全局实例上,可以使用两种不同的方法进行调用:

// Globally
import Vue from 'vue';
Vue.forceUpdate();

// Using the component instance
export default {
  methods: {
    methodThatForcesUpdate() {
      // ...
      this.$forceUpdate();  // Notice we have to use a $ here
      // ...
    }
  }
}

最后再次郑重提示:这不会更新您拥有的任何计算属性。调用forceUpdate只会强制重新渲染视图。

4)最好的方法:更改key值 

如果您需要重新渲染组件。为了正确地执行此操作,我们应该提供一个key属性,以便Vue知道特定组件与特定数据相关。如果key保持不变,则不会更改组件,但是如果key发生更改,Vue知道它应该销毁旧组件并创建一个新组件。正是我们需要的!

但是首先,我们应该了解为什么要在Vue中使用key。假设您要呈现具有以下一项或多项内容的组件列表,如果您对该列表进行排序或以任何其他方式对其进行更新,则需要重新渲染列表的某些部分。但是,您不会希望重新渲染列表中的所有内容,而只是重新显示已更改的内容。
为了帮助Vue跟踪已更改和未更改的内容,我们提供了一个key属性。这里使用数组的索引是没有用的,因为索引未绑定到列表中的特定对象。

这是我们的示例列表:

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对应索引是1了,也就是说它也会被重新渲染,事实上我只删除了Sarah - 1。

因此,在这里我们可以使用某种唯一的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删除了Sarah和James的组件,然后为James创建了一个新组件。现在,Vue知道它可以保留Evan和James的两个组件,而要做的就是删除Sarah的组件。

如果我们将一个人添加到列表中,它也知道它可以保留所有现有组件,并且只需要创建一个新组件并将其插入正确的位置即可。这确实很有用,当我们有更复杂的组件时它们保持自己的状态,会保留已有的初始化过的逻辑和已经有DOM本地状态,这对我们有很大帮助。
到这里我已经解释完Vue中的键是如何工作了。

下面让我们继续看一看强制重新渲染的最佳方法吧!
更改key值以强制重新渲染组件,这才是强制Vue重新渲染组件的最佳方法(我认为)。为此你需要提前分配好key,后面你想重新渲染组件,就只需更新key即可。这是一个非常基本的方法

<template>
  <component-to-re-render :key="componentKey" />
</template>

export default {
  data() {
    return {
      componentKey: 0,
    };
  },
  methods: {
    forceRerender() {
      this.componentKey += 1;
    }
  }
}

每次forceRerender被调用时,我们的componentKey都会改变。发生这种情况时,Vue将知道必须销毁该组件并创建一个新组件。您将获得一个子组件,该子组件将重新初始化自身并“重置”其状态。这可真是一个简单而优雅的方法!

我在这里更详细地介绍这种刷新技术:https://michaelnthiessen.com/key-changing-technique/

写在最后,如果您发现需要强制Vue重新渲染组件,这可能并不是使用vue时的最好方案,理论上你应该避免强制更新。但是,如果您确实需要重新渲染,请选择最后一种方法。

原文地址:https://michaelnthiessen.com/force-re-render/

希望你看完后有所提高。

公众号二维码

关注我的微信公众号
在公众号里留言交流
投稿邮箱:1052839972@qq.com

庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。

欧阳修

付款二维码

如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力