Vue源码解析:AST语法树转render函数
开始
今天要说的代码全在codegen文件夹中,在说实现原理前,还是先看个简单的例子!
<div class="container">
<span>{{msg}}</span>
<button :class="{active: isActive}" @click="handle">change msg</button>
</div>
上述类名为container
的元素节点包含5个子节点(其中3个是换行文本节点),转化成的AST语法树:
AST语法树转的render函数长这样:
function _render() {
with (this) {
return __h__(
'div',
{staticClass: "container"},
[
" ",
__h__('span', {}, [String((msg))]),
" ",
__h__('button', {class: {active: isActive},on:{"click":handle}}, ["change msg"]),
" "
]
)
};
}
可以的看出,render函数做的事情很简单,就是把语法树每个节点的指令进行解析。
看下render函数,它是由with函数包裹(为了改变作用域),要用的时候直接_render.call(vm)
;另外就是__h__
函数,这个后面会说到,这个函数用于元素节点的解析,接收3个参数:元素节点标签名,节点数据,子节点数据。这个函数最后返回的就是虚拟dom了,不过今天先不深究,先说如何生成这样的render函数,主要是v-if
、v-for
、v-bind
、v-on
等指令的解析。
源码解析
这边解析的是从AST树转换成render函数部分的源码,由于vue2.0第一次提交的源码这部分不全,故做了部分更新,代码全在codegen文件夹中。
入口
整个AST语法树转render函数的起点是index.js
文件中的generate()
函数:
export function generate (ast) {
const code = genElement(ast);
return new Function (`with (this) { return ${code}}`);
}
明显看到,generate()
函数传入参数为AST语法树,内部调用genElement()
函数开始解析根节点(容器节点)。genElement()
函数用于解析元素节点,它接收两个参数:AST对象
和节点标识
(v-for的key),最后返回形如__h__('div', {}, [])
的字符串,看一下内部逻辑:
function genElement (el, key) {
let exp;
if (exp = getAndRemoveAttr(el, 'v-for')) { // 解析v-for指令
return genFor(el, exp);
} else if (exp = getAndRemoveAttr(el, 'v-if')) { // 解析v-if指令
return genIf(el, exp, key);
} else if (el.tag === 'template') { // 解析子组件
return genChildren(el);
} else {
return `__h__('${el.tag}', ${genData(el, key) }, ${genChildren(el)})`;
}
}
genElement()
函数内部依次调用getAndRemoveAttr()
函数判断了v-for
、v-if
标签是否存在,若存在则删除并返回表达式;随后判断节点名为template
就直接进入子节点解析;以上条件都不符合就返回__h__
函数字符串,该字符串将使用到属性解析和子节点解析。
function getAndRemoveAttr (el, attr) {
let val;
// 如果属性存在,则从AST对象的attrs和attrsMap移除
if (val = el.attrsMap[attr]) {
el.attrsMap[attr] = null;
for (let i = 0, l = el.attrs.length; i < l; i++) {
if (el.attrs[i].name === attr) {
el.attrs.splice(i, 1);
break;
}
}
}
return val;
}
v-for 和 v-if 指令解析
让我们先看看v-for
的编译:
function genFor (el, exp) {
const inMatch = exp.match(/([a-zA-Z_][\w]*)\s+(?:in|of)\s+(.*)/);
if (!inMatch) {
throw new Error('Invalid v-for expression: '+ exp);
}
const alias = inMatch[1].trim();
exp = inMatch[2].trim();
let key = getAndRemoveAttr(el, 'track-by'); // 后面用 :key 代替了 track-by
if (!key) {
key ='undefined';
} else if (key !== '$index') {
key = alias + '["' + key + '"]';
}
return `(${exp}) && (${exp}).map(function (${alias}, $index) {return ${genElement(el, key)}})`;
}
该函数先进行正则匹配,如"item in items"
,将解析出别名(item
)和表达式(items
),再去看看当前节点是否含:key
,如果有那就作为genElement()
函数的参数解析子节点。举个
Vue源码解析:AST语法树转render函数的更多相关文章
- 【VUE】Vue 源码解析
Vue 源码解析 Vue 的工作机制 在 new vue() 之后,Vue 会调用进行初始化,会初始化生命周期.事件.props.methods.data.computed和watch等.其中最重要的 ...
- 【vuejs深入二】vue源码解析之一,基础源码结构和htmlParse解析器
写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. vuejs是一个优秀的前端mvvm框架,它的易用性和渐进式的理念可以使每一个前端开发人员感到舒服,感到easy.它内 ...
- 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现
写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...
- Vue源码解析---数据的双向绑定
本文主要抽离Vue源码中数据双向绑定的核心代码,解析Vue是如何实现数据的双向绑定 核心思想是ES5的Object.defineProperty()和发布-订阅模式 整体结构 改造Vue实例中的dat ...
- Vue源码解析之nextTick
Vue源码解析之nextTick 前言 nextTick是Vue的一个核心功能,在Vue内部实现中也经常用到nextTick.但是,很多新手不理解nextTick的原理,甚至不清楚nextTick的作 ...
- Vue源码解析(一):入口文件
在学习Vue源码之前,首先要做的一件事情,就是去GitHub上将Vue源码clone下来,目前我这里分析的Vue版本是V2.5.21,下面开始分析: 一.源码的目录结构: Vue的源码都在src目录下 ...
- Vue源码解析之数组变异
力有不逮的对象 众所周知,在 Vue 中,直接修改对象属性的值无法触发响应式.当你直接修改了对象属性的值,你会发现,只有数据改了,但是页面内容并没有改变. 这是什么原因? 原因在于: Vue 的响应式 ...
- VUE源码解析心得
解读vue源码比较好奇的几个点: VUE MVVM 原理 http://www.cnblogs.com/guwei4037/p/5591183.html https://cn.vuejs.org/v2 ...
- vue源码解析之observe
一. vue文档中有"由于 JavaScript 的限制,Vue 不能检测以下数组的变动",是否真是由于JavaScript的限制,还是出于其他原因考虑 当你利用索引直接设置一个数 ...
随机推荐
- hash索引
hash算法 哈希表(hash table ,也叫散列表),是根据关键码值(key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个 ...
- wkhtmltopdfhtml php生成pdf快照,网页截图,网页快照完整版 (原)
首先,安装(linux安装为例) 1.下载wkhtmltopdf wget http://download.gna.org/wkhtmltopdf/obsolete/linux/wkhtmltopdf ...
- 原生Js_实现简单的下拉折叠菜单(添加弹出动画效果)
用javascript实现简单的下拉折叠菜单效果 实现步骤 (a)获得各操作的dom对象: (b)在所有菜单按钮对象上添加单击事件: (c)设置所有菜单按钮样式为空,并将当前按钮的样式设置为“acti ...
- 微信小程序需求IIS服务器配置https关于SSL,TLS的综合解决方案
SpringBoot配置SSL同时支持http和https访问 传输层安全性协议(英语:Transport Layer Security,缩写作 TLS),及其前身安全套接层(Secure Socke ...
- TCP定时器 之 坚持定时器
坚持定时器在接收方通告接收窗口为0,阻止发送端继续发送数据时设定. 由于连接接收端的发送窗口通告不可靠(只有数据才会确认,ACK不会确认),如果一个确认丢失了,双方就有可能因为等待对方而使连接终止:接 ...
- ffmpeg修复时间戳
ffmpeg -re -i e:/media/baifa.mp4 -filter_complex -hls_wrap -hls_time d:/demo/hls/cctv13/playlist.m3u ...
- [论文理解] Spatial Transformer Networks
Spatial Transformer Networks 简介 本文提出了能够学习feature仿射变换的一种结构,并且该结构不需要给其他额外的监督信息,网络自己就能学习到对预测结果有用的仿射变换.因 ...
- Jquery 选择器的用法
用js 动态生成<ul>标签的<li>条目,因为在动态的创建过程中li的点击事件是不太方便的,因此采用JQuery 的类选择器来实现. html代码: <div id=& ...
- Ubuntu16.04小白入门分享之 玩转Ruby你需要安装什么软件(持续更新)
Ubuntu提示功能很强大,一般如果你想安装什么软件,可以直接输入名字,然后会有提示,安装格式一般为: sudo apt install 名字 Ubuntu14.04/16.04命令行快速安装Ruby ...
- 如何使用Loadrunner Controller 监控服务器的系统资源
(1)保证装有loadrunner Controller的控制机和被监控的目标机(服务器)之间能够ping通,在同一个网段内,保证两台机器用administrator登陆. (2)Win + R, s ...