Vue
是基于响应式的,自动挡操作,当数据发生改变,与之相对应的视图也会刷新。但开发过程中,我们有时候会遇到一些额外的状况,例如动态赋值后,对应的数据发生了变换,但 DOM
却没有及时更新,因此我们此时就处于一个很尴尬的境地,值已经修改成功,但双向绑定的 DOM
节点我们却获取不到。这时候我们就需要来手动更新组件来更新数据。
经过我日常中的积累,Vue3
中强制刷新组件的方式大致有五种:
- 重新加载整个页面: 不建议使用
- 使用
v-if
: 不妥的方式 - 使用内置的
forceUpdate
方法: 较好的方式 (Vue2.x
使用较多) Vue3
中使用provide / inject
传值的方式- 利用
key
值的变化触发组件更新: 最佳方式
另外,这个世界已经悄然发生变化,现在根本无需写任何前端代码,直接使用卡拉云 —— 新一代低代码开发工具帮你搭建后台工具,卡拉云可一键接入常见数据库及 API
,无需懂前端,内置完善的各类前端组件,无需调试,拖拽即用。原来三天的工作量,现在 1
小时搞定,谁用谁知道,用上早下班,详见本文文末。
Vue 强制刷新案例
首先我们来看一个 Vue
响应式无法解决的视图更新案例。具体案例见下图
红框部分为 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';
},
},
};

调前端太费劲?
重新加载整个页面
我们以上面的案例来评估一下此方法,假设 App.vue
除 TagKala
组件外,还包含多个组件,而我们的需求只是刷新 TagKala
组件的内容。如果我们此时重新加载整个页面,无疑是一种性能浪费。因此这种方法极力的不推荐使用。
但是这种方法是最容易实现的,我们只需借用路由 route
即可。
route.go(0);
v-if 方法
我们在学习 Vue
基础知识时,Vue
官方文档告诉我们 v-if 是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。因此通常情况下,v-if
具有更高的切换开销,组件每次切换都可能会涉及到页面的重排和重绘,因此这也是一种比较浪费性能的选择,不算最佳实现。
使用 v-if
方法实现组件强制刷新,需要配合 Vue3
的 nextTick
方法。
<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();
},
},
};
扩展阅读:《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
的新方法。
ref
方法接受一个内部值并返回一个响应式且可变的ref
对象。(
这里有可能会奇怪? Vue
是基于响应式的,为什么会出现 ref
响应式函数。因为 setup
中是是没有 this
的。
provide
和inject
可以实现深层父子组件的传值。这个特性有两个部分:父组件有一个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
的方式来实现刷新,简单高效,因此强烈推荐使用 key-changing
的方法。
拓展阅读:《最棒的 7 个 Laravel admin 后台管理系统推荐》
$forceUpdate 方法
最后我们来说一下 $forceUpdate
方法,该方法同样是 Vue
官方提供的,官方文档中指出: 迫使组件实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
该方法有几个注意事项:
- 该方法与
v-if / key-changing
都有所不同,$forceUpdate
只会触发updated/beforeUpdate
生命周期钩子函数 - 该方法仅仅影响实例本身和插入插槽内容的子组件,因此当前方法需要使用在需要刷新视图的组件中。
- 该方法在使用时,存在无效的情况,因此要斟酌使用。
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小时搞定。
卡拉云是新一代低代码开发工具,免安装部署,可一键接入包括 MySQL 在内的常见数据库及 API。可根据自己的工作流,定制开发。无需繁琐的前端开发,只需要简单拖拽,即可快速搭建企业内部工具。原来三天的开发工作量,使用卡拉云后可缩减至 1 小时,欢迎免费试用卡拉云。
扩展阅读: