vlambda博客
学习文章列表

Vue中刷新组件的小妙招,及其背后的原理

如下所示,如果要在Vue中刷新子组件 table-template 应该如何操作?


<template> <div class="wrapper"> <table-template ref="table" :columnData="fieldDetail.column" :listData="fieldDetail.list" /> </div></template>


譬如说,我改变了列表list的值,需要子组件重新刷新一下。


方法1:

在 子组件 table-template 中定义一个 reload 方法,刷新数据

this.$refs['table'].reload()


其实,我是想让整个子组件重新加载一下,重新走一遍生命周期。那么,可以试试 v-if 指令。


方法2:

v-if 指令,在切换时元素及它的数据绑定 / 组件被销毁并重建

<template> <div class="wrapper"> <table-template v-if="showFlag" ref="table" :columnData="fieldDetail.column" :listData="fieldDetail.list" /> </div></template>
this.showFlag = false;this.$nextTick(()=>{ this.showFlag = true;})


小妙招

通过绑定的key值,刷新组件

<template> <div class="wrapper"> <table-template :key="fieldDetail.timer" :columnData="fieldDetail.column" :listData="fieldDetail.list" /> </div></template>
this.fieldDetail.list = res.datathis.fieldDetail.timer = new Date().getTime();


Tips:

key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。


如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。


而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。


有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。

最常见的用例是结合 v-for

<ul> <li v-for="item in items" :key="item.id">...</li></ul>

它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:


1.完整地触发组件的生命周期钩子

2.触发过渡


例如:

<transition> <span :key="text">{{ text }}</span></transition>


当 text 发生改变时,<span> 总是会被替换而不是被修改,因此会触发过渡。


背后的原理

背后的原理,需要从Vue的虚拟DOM的diff算法说起。


虚拟DOM的diff算法的宗旨就是要以最快的速度将每次改动以最快的速度渲染到浏览器的页面上。


譬如,一间房子翻修。

两种方案:

1.拆了重建;

2.精细化对比,哪里需要改动就改哪里。


diff就是精细化对比的意思。


虚拟DOM

React和Vue都采用虚拟DOM,当数据改变时,不用整体重新渲染,局部刷新变化即可。


简短解说,两句话:

1.通过JavaScript创建虚拟的DOM树结构,呈现至页面。

2.数据改变时,引发DOM树结构改变,生成新的虚拟DOM树,和之前的DOM对比,将变化的部分应用到真实的DOM树中,渲染至页面。


那么,虚拟domkey值的关系是怎样的呢?

虚拟DOM与key值的关系

首先,我们来看看Vue中的虚拟dom,源码如下:

export default class VNode { constructor ( tag?: string, data?: VNodeData, children?: ?Array<VNode>, text?: string, elm?: Node, context?: Component, componentOptions?: VNodeComponentOptions, asyncFactory?: Function ) { this.tag = tag /*当前节点的标签名*/ this.data = data /*当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息*/ this.children = children /*当前节点的子节点,是一个数组*/ this.text = text /*当前节点的文本*/ this.elm = elm /*当前虚拟节点对应的真实dom节点*/ this.ns = undefined /*当前节点的名字空间*/ this.context = context /*当前组件节点对应的Vue实例*/ this.fnContext = undefined /*函数式组件对应的Vue实例*/ this.fnOptions = undefined this.fnScopeId = undefined this.key = data && data.key /*节点的key属性,被当作节点的标志,用以优化*/ this.componentOptions = componentOptions /*组件的option选项*/ this.componentInstance = undefined /*当前节点对应的组件的实例*/ this.parent = undefined /*当前节点的父节点*/ this.raw = false /*简而言之就是是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false*/ this.isStatic = false /*静态节点标志*/ this.isRootInsert = true /*是否作为跟节点插入*/ this.isComment = false /*是否为注释节点*/ this.isCloned = false /*是否为克隆节点*/ this.isOnce = false /*是否有v-once指令*/ this.asyncFactory = asyncFactory this.asyncMeta = undefined this.isAsyncPlaceholder = false }
get child (): Component | void { return this.componentInstance }}
this.key = data && data.key /*节点的key属性,被当作节点的标志,用以优化*/


key的作用: 辅助判断新旧vdom节点在逻辑上是不是同一个对象。


使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。key的作用主要是为了高效的更新虚拟DOM


接着提问,为什么要使用虚拟DOM呢?


Vue采用虚拟DOM的目的是什么?

这就要从早期的前端开发说起了。


HTML是结构化的文本文档,每一个元素(element)对应HTML中的一段结构化文本,DOM(Document Object Model,文档对象模型)可以理解为这段结构化文本的抽象。


DOM储存于内存中,提供了操作和修改HTML元素的一系列API。


DOM操作是前端开发的核心,最早流行的一些前端框架、工具库大都以优秀的DOM操作闻名,比如prototype.js和jQuery.js。


与JavaScript逻辑相比,DOM操作的性能消耗非常高,在以静态内容为主的WebPage时代,少量DOM操作的性能损耗基本可以忽略不计。然而对于存在丰富动态内容的WebApp而言,大量、频繁的DOM操作逐渐成为性能瓶颈


对于JavaScript而言,虚拟DOM仅仅是一个拥有丰富属性的对象,所有针对DOM的操作被映射为对JavaScript对象的修改,性能上自然大幅优于直接对DOM的操作。


引入虚拟DOM还带来了以下好处:

1.组件高度抽象化。

2.跨平台能力:可以渲染到 DOM 以外的平台。


参考书籍

《深入实战Vue开发》殷荣桧

《前端技术架构与工程》周俊鹏