Vue 3 强制刷新组件的五种必会方法总结

A kitten
战场小包
前端实习生
最近更新 2022年03月23日

Vue 3 强制刷新组件的五种方法

Vue 是基于响应式的,自动挡操作,当数据发生改变,与之相对应的视图也会刷新。但开发过程中,我们有时候会遇到一些额外的状况,例如动态赋值后,对应的数据发生了变换,但 DOM 却没有及时更新,因此我们此时就处于一个很尴尬的境地,值已经修改成功,但双向绑定的 DOM 节点我们却获取不到。这时候我们就需要来手动更新组件来更新数据。

经过我日常中的积累,Vue3 中强制刷新组件的方式大致有五种:

  • 重新加载整个页面: 不建议使用
  • 使用 v-if: 不妥的方式
  • 使用内置的 forceUpdate 方法: 较好的方式 (Vue2.x 使用较多)
  • Vue3 中使用 provide / inject 传值的方式
  • 利用 key 值的变化触发组件更新: 最佳方式

另外,这个世界已经悄然发生变化,现在根本无需写任何前端代码,直接使用卡拉云 —— 新一代低代码开发工具帮你搭建后台工具,卡拉云可一键接入常见数据库及 API ,无需懂前端,内置完善的各类前端组件,无需调试,拖拽即用。原来三天的工作量,现在 1 小时搞定,谁用谁知道,用上早下班,详见本文文末。

Vue 强制刷新案例

首先我们来看一个 Vue 响应式无法解决的视图更新案例。具体案例见下图

case

红框部分为 TagKala 组件,内容通过本地模拟请求获取到,当点击切换 tag 按钮时,修改请求参数,获取另一组 tag 信息。

如果我们只是单纯依赖于 Vue 的响应式机制,是无法完成上述要求的。因此本文就提供一种解决方案 —— Vue 强制组件刷新。

但注意上述案例中,强制组件刷新并非是唯一方案。

TagKala.vue 部分代码:

<template>
  <ul class="list-style-none title-list">
    <li
      v-for="item in tags"
      :class="{'active': item.active}"
      :key="item.id"
      @click="activeItem(item)"
    >{{item.name}}</li>
  </ul>
</template>

<script>
import getTag from '../assets/api';
export default {
    props: ['tag'],
    data() {
        return {
            tags: [],
        };
    },
    async created() {
        this.tags = await getTag(this.tag);
    },
};
</script>

App.vue 部分代码:

<template>
  <div class="force-refresh">
    <h1>vue3 组件强制刷新的四种方式</h1>
    <h2>卡拉云——低代码开发工具,1 秒搭建上传后台</h2>
    <tag-kala :tag="tag"></tag-kala>
    <el-button @click="toggleTag" type="primary" class="btn">切换tag</el-button>
  </div>
</template>
<script>
import TagKala from './components/TagKala.vue';
export default {
    components: {
        TagKala,
    },
    data() {
        return {
            tag: 'tag1',
        };
    },
    methods: {
        toggleTag() {
          // 更改请求参数
            this.tag = 'tag2';
        },
    },
};

调前端太费劲?

试用卡拉云,简单拖拽连接 API 和数据库直接生成后台系统,两个月的工期降低至两天

重新加载整个页面

我们以上面的案例来评估一下此方法,假设 App.vueTagKala 组件外,还包含多个组件,而我们的需求只是刷新 TagKala 组件的内容。如果我们此时重新加载整个页面,无疑是一种性能浪费。因此这种方法极力的不推荐使用。

但是这种方法是最容易实现的,我们只需借用路由 route 即可。

route.go(0);

v-if 方法

我们在学习 Vue 基础知识时,Vue 官方文档告诉我们 v-if 是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。因此通常情况下,v-if 具有更高的切换开销,组件每次切换都可能会涉及到页面的重排和重绘,因此这也是一种比较浪费性能的选择,不算最佳实现。

使用 v-if 方法实现组件强制刷新,需要配合 Vue3nextTick 方法。

<template>
  <div class="force-refresh">
    <h1>vue3 组件强制刷新的四种方式</h1>
    <h2>卡拉云——低代码开发工具,1 秒搭建上传后台</h2>
    <tag-kala v-if="renderComponent" :tag="tag"></tag-kala>
    <el-button @click="toggleTag" type="primary" class="btn">切换tag</el-button>
  </div>
</template>

然后再 script 中,使用 nextTick 方法:

import { nextTick } from "vue";
import TagKala from "./components/TagKala.vue";
export default {
  components: {
    TagKala,
  },
  data() {
    return {
      tag: "tag1",
      renderComponent: true,
    };
  },
  methods: {
    forceRerender() {
      this.renderComponent = false;
      nextTick(() => {
        this.renderComponent = true;
      });
    },
    toggleTag() {
      this.tag = this.tag === "tag1" ? "tag2" : "tag1";
      // 在需要强制刷新的时机,调用 forceRender
      this.forceRerender();
    },
  },
};

v-if

扩展阅读:《Vue 如何安装 axios

provide / inject 传值的方式

上面我们提到了 v-if 配合 nextTick 方法实现组件强制刷新,但此方法为 Vue2 版本的代码,这里将上述方法修改为 Vue3 版本。

我们在 App.vue provide 该方法出去。

setup() {
    // 局部组件刷新
    const isRouterAlive = ref(true);
    const reload = () => {
      // 删除组件
      isRouterAlive.value = false;
      nextTick(() => {
        // 重新渲染组件
        isRouterAlive.value = true;
      });
    };
    provide("reload", reload);
    return {
      // 相当于挂载到 this 上
      // template 中就可以使用了
      isRouterAlive,
    };
  },

TagKala.vue 中通过 inject 获取该方法。

setup() {
    const toggle = inject('reload');
    return {
        toggle,
    };
},

这里稍微解释上面使用到的 Vue3 的新方法。

  1. ref 方法接受一个内部值并返回一个响应式且可变的 ref 对象。(

这里有可能会奇怪? Vue 是基于响应式的,为什么会出现 ref 响应式函数。因为 setup 中是是没有 this 的。

  1. provideinject 可以实现深层父子组件的传值。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据

利用 key 值的变化触发组件更新

Vue 的虚拟 DOM 算法在比对 Vnode 时会用到 key 作为唯一标识,key 值改变该元素就会被重新渲染。修改 key 值也是 Vue 官方支持和推荐的做法,并且实现起来也非常简单。修改 key 值 方法与 v-if 都是切换元素,那为什么会推荐修改 key 值的方法呐? 这里一定要注意到 key 值是作用于虚拟 DOM 的,因此它的性能会远远优于 v-if

于是我们可以这样修改我们的案例,给 tag-kala 添加 key 属性,当调用 toggleTag 方法时,修改 key 属性值。

<tag-kala :tag="tag" ref="tag" :key="keyNum"></tag-kala>
toggleTag() {
  this.tag = this.tag === 'tag1' ? 'tag2' : 'tag1';
  this.keyNum++;
},

key-changing

通过修改 key 的方式来实现刷新,简单高效,因此强烈推荐使用 key-changing 的方法。

拓展阅读:《最棒的 7 个 Laravel admin 后台管理系统推荐

$forceUpdate 方法

最后我们来说一下 $forceUpdate 方法,该方法同样是 Vue 官方提供的,官方文档中指出: 迫使组件实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。

该方法有几个注意事项:

  1. 该方法与 v-if / key-changing 都有所不同,$forceUpdate 只会触发 updated/beforeUpdate 生命周期钩子函数
  2. 该方法仅仅影响实例本身和插入插槽内容的子组件,因此当前方法需要使用在需要刷新视图的组件中。
  3. 该方法在使用时,存在无效的情况,因此要斟酌使用。

Vue 官方为什么会设置此方法呐? Vue2.x 的响应式系统基于 defineProperty ,收集的响应式数据不全,例如: 修改数组长度,删除对象属性等变化都无法检测到,因此有时我们就需要手动调用 $forceUpdate 来刷新视图。但 Vue3.x 响应式系统基于 Proxy ,响应机制比较完善,经过我多次尝试也没有发现需要手动调用 $forceUpdate 的案例。

但为了保险起见,我还是把 $forceUpdate 如何使用罗列在这里,以供大家选择。

// Vue 3.x 我们可以使用组合 API setup
// 可以获取到当前实例
// 在需要的地方调用 instance.ctx.$forceUpdate() 即可
setup() {
    const instance = getCurrentInstance();
    return { instance };
},

instance.ctx.$forceUpdate()

总结与推荐

本文讲述了 Vue3 中五种强制刷新组件的方式,其中我最推荐的方法是利用 key 值的变化触发组件刷新,这种方法是基于虚拟 DOM 层面,刷新消耗性能少,实现起来比较简单,同时也是 Vue 官方所推荐的方法。如果你只是想快速搭建一套自己的工具,并不想处理前端问题,那么推荐使用新一代低代码开发工具 - 卡拉云。卡拉云内置各类前端组件,无需懂任何前端,仅需拖拽即可快速生成。

下图为使用卡拉云搭建的内部广告投放监测系统,仅需拖拽,1小时搞定。

09-kalacloud

卡拉云是新一代低代码开发工具,免安装部署,可一键接入包括 MySQL 在内的常见数据库及 API。可根据自己的工作流,定制开发。无需繁琐的前端开发,只需要简单拖拽,即可快速搭建企业内部工具。原来三天的开发工作量,使用卡拉云后可缩减至 1 小时,欢迎免费试用卡拉云

扩展阅读: