Vue.js 源码分析(二十) 指令篇 v-once指令详解

分类 : .NET技术

数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值,例如:<p>Message: {{ msg }}</p>以后每当msg属性发生了改变,插值处的内容都会自动更新。

可以给DOM节点添加一个v-once指令,这样模板只会在第一次更新时显示数据,此后再次更新该DOM里面引用的数据时,内容不会自动更新了,例如:

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
    <div id="d" v-once>
        <p>{{message}}</p>
    </div>    
    <script>        Vue.config.productionTip=false;        Vue.config.devtools=false;        var app = new Vue({el:'#d',data:{message:'Hello World!'}})    </script>
</body>
</html>
复制代码

DOM渲染为:

为了验证修改message属性不会触发DOM更新,我们在控制台输入app.message="Hello Vue"来修改message属性

可以发现DOM并未更新,此时app.message等于"Hello Vue!"的,我们打印看看,如下:

可以看到app.message等于Hello Vue!,为什么没有触发更新了,因为Vue内部把模板缓存起来了,把v-once对应的节点当作一个静态节点来看待,而不是一个响应式的数据(没有经过Object.defineproperty处理)


源码分析


在解析模板生成AST节点树对象的时候会通过processOnce尝试去获取v-once指令,如果有定义则在当前AST对象上增加一个once属性,值为true,如下:

function processOnce (el) {     //第9460行 解析v-once属性    var once$$1 = getAndRemoveAttr(el, 'v-once');     //获取v-once属性if (once$$1 != null) {                            //如果存在,则给el增加一个once属性,值为true
    el.once = true;  }}

例子里的模板解析时执行到这里后等于:

接下来在generate生成rendre函数的时候会先判断AST中有无once属性,如果有则调用genOnce函数,genOnce会调用genStatic()去生成一个静态节点

复制代码
function genElement (el, state) {           //第10139行  生成函数字符串if (el.staticRoot && !el.staticProcessed) {    return genStatic(el, state)     } elseif (el.once && !el.onceProcessed) {    //如果有设置了once属性,则调用genOnce()函数return genOnce(el, state)  } elseif (el.for && !el.forProcessed) {  /**/}function genOnce (el, state) {              //第10179行  渲染v-once指令
  el.onceProcessed = true;  if (el.if && !el.ifProcessed) {             //如果有定义了v-if指令return genIf(el, state)  } elseif (el.staticInFor) {                //如果是在v-for环境下var key = '';    var parent = el.parent;    while (parent) {      if (parent.for) {        key = parent.key;        break      }      parent = parent.parent;    }    if (!key) {      "development" !== 'production' && state.warn(        "v-once can only be used inside v-for that is keyed. "      );      return genElement(el, state)    }    return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")")  } else {    return genStatic(el, state)               //否则直接调用genStatic()函数  }}
复制代码

genStatic函数是静态节点渲染时的分支,如下:

复制代码
function genStatic (el, state) {      //第10172行 
  el.staticProcessed = true;  state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}"));           //再次调用genElement(el, state),但是结果保存到state.staticRenderFns里面return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")")   //对于静态节点,返回格式为_m(id),id为staticRenderFns数组属性里的索引,生成Vnode时用于缓存用的
}
复制代码

对于v-once和静态节点来说,渲染后它的render函数是一个_m函数,其中参数是一个索引值,是存储在staticRenderFns数组属性对应的索引,每个值是一个静态DOM,只会渲染一次的

例子里的模板渲染后等于render和staticRenderFns属性如下:

最后执行render函数的时候就会执行_m(0)这个函数,_m等于全局的renderStatic函数,如下:

复制代码
function renderStatic (       //第3869行 渲染静态节点  index,  isInFor) {  var cached = this._staticTrees || (this._staticTrees = []);         //这个是缓存var tree = cached[index];                                           //尝试从缓存中拿到tree// if has already-rendered static tree and not inside v-for,// we can reuse the same tree.if (tree && !isInFor) {                                             //如果该静态AST在缓存中有了,而且不是在v-for环境下return tree                                                         //则直接返回tree即可  }     // otherwise, render a fresh tree.
  tree = cached[index] = this.$options.staticRenderFns[index].call(   //调用$options.staticRenderFns里对应的函数渲染成一个Vnode,并保存到缓存中this._renderProxy,    null,    this// for render fns generated for functional component templates  );        markStatic(tree, ("__static__" + index), false);                    //设置标记return tree}
复制代码

可以看到对于v-once节点来说,如果没有和v-if或v-for配合使用,则它会被当作一个静态节点来对待,经过了第一次渲染后就会把模板缓存起来,以后的更新渲染都只是从缓存中拿出结果而已。


分类: .NET技术  发布: 2019-07-09-星期二   访问() .NET技术    Web前端    JAVA开发    HTML基础    数据库    CSS基础    电脑知识   
CSS教程-web前端教程-免费教程
HTML教程-web前端教程-免费教程

分类

.NET技术 Web前端 JAVA开发 HTML基础 数据库 CSS基础 电脑知识

随机阅读

02-标识符,关键字和保留字
【MySQL】漫谈MySQL体系结构
javascript之标识(zhi)符、关键字与保留字
《MySQL 性能优化》之理解 MySQL 体系结构
MYSQL性能优化之Mysql体系结构,存储引擎
Python语法的使用和简介
底层剖析 Window 、Activity、 View 三者关系
Unity音量可视化——粒子随声浪跳动
JavaScript中this指向问题
css talbe中td溢出隐藏 div溢出隐藏

最新

div css隐藏内容样式方法
OPPO Reno3 Pro远程守护怎么使用?
XP系统里让IE支持多线程下载怎么设置
qq电脑管家温度检测在哪设置的? QQ电脑管家怎么测电脑的温度
win7禁用ie浏览器方法 win7怎么把桌面的ie隐藏
WinXP笔记本声卡驱动无法成功安装的解决方法
XP⁄Win7共享⁄连接打印机设置详细 xp怎
华为笔记本蓝屏错误0xcoooo428怎么解决
企业如何部署微软Windows 8? Windows 8的开发历史
华为mate9怎样解指纹密码?

推荐阅读

c语言中break语句的作用
undefined是什么意思啊
vscode----vue中HTML代码tab键自动补全
前端该怎么学?推荐一个学习路线!
.Net轻松处理亿级数据--ClickHouse数据操作
js 中日期 转换成时间戳 例如2013-08-30 转换为时间戳
html5中datalist标签怎么用
DIV CSS字体(font-family)实现字体样式设置
Mysql里表示布尔型的类型是什么
Datalist options 集合,options语法,optio

Copyright © 2017 CSS5.NET教程.CSS5 内容仅用于学习和测试参考。 css5.net All Rights Reserved 蜀ICP备15003849号-16