Vue.js 源码分析(二十) 指令篇 v-once指令详解
数据绑定最常见的形式就是使用“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)
} else if (el.once && !el.onceProcessed) { //如果有设置了once属性,则调用genOnce()函数
return genOnce(el, state)
} else if (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)
} else if (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函数,如下:
writer by:大沙漠 QQ:22969969
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配合使用,则它会被当作一个静态节点来对待,经过了第一次渲染后就会把模板缓存起来,以后的更新渲染都只是从缓存中拿出结果而已。
Vue.js 源码分析(二十) 指令篇 v-once指令详解的更多相关文章
- Vue.js 源码分析(二十六) 高级应用 作用域插槽 详解
普通的插槽里面的数据是在父组件里定义的,而作用域插槽里的数据是在子组件定义的. 有时候作用域插槽很有用,比如使用Element-ui表格自定义模板时就用到了作用域插槽,Element-ui定义了每个单 ...
- Vue.js 源码分析(二十八) 高级应用 transition组件 详解
transition组件可以给任何元素和组件添加进入/离开过渡,但只能给单个组件实行过渡效果(多个元素可以用transition-group组件,下一节再讲),调用该内置组件时,可以传入如下特性: n ...
- Vue.js 源码分析(二十九) 高级应用 transition-group组件 详解
对于过度动画如果要同时渲染整个列表时,可以使用transition-group组件. transition-group组件的props和transition组件类似,不同点是transition-gr ...
- Vue.js 源码分析(二十四) 高级应用 自定义指令详解
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令. 官网介绍的比较抽象,显得很高大上,我个人对自定义指令的理解是:当自定义指令作用在一些DOM元素或组件上 ...
- Vue.js 源码分析(二十二) 指令篇 v-model指令详解
Vue.js提供了v-model指令用于双向数据绑定,比如在输入框上使用时,输入的内容会事实映射到绑定的数据上,绑定的数据又可以显示在页面里,数据显示的过程是自动完成的. v-model本质上不过是语 ...
- Vue.js 源码分析(二十五) 高级应用 插槽 详解
我们定义一个组件的时候,可以在组件的某个节点内预留一个位置,当父组件调用该组件的时候可以指定该位置具体的内容,这就是插槽的用法,子组件模板可以通过slot标签(插槽)规定对应的内容放置在哪里,比如: ...
- jQuery 源码分析(二十) DOM操作模块 插入元素 详解
jQuery的DOM操作模块封装了DOM模型的insertBefore().appendChild().removeChild().cloneNode().replaceChild()等原生方法.分为 ...
- Vue.js 源码分析(二十三) 指令篇 v-show指令详解
v-show的作用是将表达式值转换为布尔值,根据该布尔值的真假来显示/隐藏切换元素,它是通过切换元素的display这个css属性值来实现的,例如: <!DOCTYPE html> < ...
- Vue.js 源码分析(二十一) 指令篇 v-pre指令详解
该指令会跳过所在元素和它的子元素的编译过程,也就是把这个节点及其子节点当作一个静态节点来处理,例如: <!DOCTYPE html> <html lang="en" ...
随机推荐
- java循环定时器@Scheduled的使用
@Scheduled 注解 用于定时循环执行任务 例如: @Scheduled(cron="0 */10 * * * ?") 表示每隔十分钟执行一次 每隔5秒执行一次:" ...
- Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application-笔记
参考 Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application http://www.vnfan.com/helinbin/d/745b1e040c9b4f6 ...
- 总结了11条,我对Python 装饰器的理解
对于每一个学习 Python 的同学,想必对 @ 符号一定不陌生了,正如你所知, @ 符号是装饰器的语法糖,@符号后面的函数就是我们本文的主角:装饰器. 装饰器放在一个函数开始定义的地方,它就像一顶帽 ...
- Linux问题记录——主机名变成了bogon
Linux问题记录——主机名变成了bogon 摘要:本文主要记录了主机名变成bogon的原因以及解决办法. 问题重现 主机名在一次登录后,变成了bogon,此后每次登录Linux系统时都是bogon. ...
- C#NPOI对Excel的操作、导入导出时异常处理、最全的NPOI资料在这里~
一.Excel理论知识 最新版NPOI2.4.1链接:https://pan.baidu.com/s/1iTgJi2hGsRQHyw2S_4dIUw 提取码:adnq • 整个Excel表格叫做工作 ...
- 阿里P7工作总结:Spring MVC的工作原理,看完受益匪浅
这篇文章将深入探讨Spring框架的一部分——Spring Web MVC的强大功能及其内部工作原理. 项目安装 在本文中,我们将使用最新.最好的Spring Framework 5.我们将重点介绍S ...
- JavaScript HTML DOM Style flexWrap 属性
flexWrap 属性 flexWrap属性指定flex项是否应该换行. 注意:如果元素不是flex项,则flexWrap属性不起作用. 如果必要,使flex换行: document.getEleme ...
- 定制Dynamics CRM标准导出功能:不能导出指定列的值
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复239或者20161203可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- Dynamics CRM定制子网格添加按钮实例之一
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复221或者20160430可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- 区块链社交APP协议分析:Qbao
- Qbao是什么 - Qbao报文情况 本节我们开始使用Qbao软件,并抓取其报文进行分析. 对APP进行协议分析抓包的一般过程是: 1.打开抓包APP进行抓包: 2.打开APP开始使用: 3.对每 ...