vue深入解析之render function code详解


Posted in Javascript onJuly 18, 2017

前言

最近在深入的学习研究vue,其实vue在使用上入门并没有什么太高的门槛,但前端同学们也不该仅仅停留在使用上。以 vue 设计、编码之优秀,足当抽丝剥茧,扒开它的外壳,深入其原理。让我们一起来刺破 vue 的心脏,下面话不多说了,来一起看看详细的介绍吧。

vue核心执行过程图

vue深入解析之render function code详解

vue核心的执行过程主要分为这几个阶段:

      1) 编译模板,生成可复用的render function code(这是今天要重点解读的),这一步在vue实例的整个生命周期中只会执行一次甚至零次,因为我们可以在打包的时候可以预编译

      2) 生成watcher等核心渲染监听,在整个vue实例的生命过程中持续发生着作用,对view和modal进行双向绑定

      3) 虚拟dom的diff比较,当watcher监听到data的变更的时候,就会根据注入新的data执行render function code,生成新的虚拟dom,跟老的虚拟dom(第一次执行的时候可能为空)进行diff比对,不同的部分将写入真实的dom

这几个过程都会以源码解析的方式分篇解读,今天我们重点讲解的是第一部分模板编译中输出的render function code

render function code解析

一、模板编译过程分解

// 生成ast语法树
const ast = parse(template.trim(), options)
// 标记静态内容(以免diff的时候需要重复比较)
optimize(ast, options)
// 生成render function code
const code = generate(ast, options)

很遗憾,上诉三步的代码今天都只是点到为止,不需要太努力,就能在网上搜到相关解析的文章,如果实在找不到同时又感兴趣,可以找到vue源码中的下面文件去读:

// 编译入口
src/compiller/index.js
// html解析
src/compiller/parser/html-parser.js
// src/compiller下的其它文件

换一个姿势读源码,今天我们要读的是generate(ast, options)生成的render function code的具体代码

二、写一个 vue demo

之前有提到过,读源码需要先了解整体的设计思想、架构,上面那个执行过程图在此列;另外,搭建一个demo执行环境进行debug单步调试,也是一个重要手段(特别是你对源码的目录结构不是特别清晰的时候)。为了让生成的render function code更为完整,写一个覆盖面尽量广的 demo :

//template
 <div id="app">
  <p>普通属性:{{ message }}</p>
  <p>{{msg()}}</p>
  <p>{{ct}}</p>
  <input v-model="message">
  <div v-for="item in items">
   {{ item.text }}
  </div>
  <button v-on:click="bindClick">点我抓同伟</button>
 </div>

 // js
 new Vue({
  el: '#app',
  data: {
  message: '以vue的名义',
  items: [{
   text: '达康书记'
  }, {
   text: '育良书记'
  }]
  },
  methods: {
  bindClick: function() {
   this.message = '这就抓同伟去';
  },
  msg: function() {
   return this.message + "这个方法每次都会执行";
  }
  },
  computed: {
  ct: function() {
   return this.message + "计算属性并不会每次都执行";
  }
  }
 })

render function code 解析

debug 拿到生成的 render function code(当然也可以通过 webpack 的 vue-loader 编译之后的 dist 文件拿到,此处省略1000字)

with(this) {
 return _c('div', {
  attrs: {
   "id": "app"
  }
 },
 [_c('p', [_v("普通属性:" + _s(message))]), _v(" "), _c('p', [_v(_s(msg()))]), _v(" "), _c('p', [_v(_s(ct))]), _v(" "), _c('input', {
  directives: [{
   name: "model",
   rawName: "v-model",
   value: (message),
   expression: "message"
  }],
  domProps: {
   "value": (message)
  },
  on: {
   "input": function($event) {
    if ($event.target.composing) return;
    message = $event.target.value
   }
  }
 }), _v(" "), _l((items),
 function(item) {
  return _c('div', [_v("\n\t\t " + _s(item.text) + "\n\t ")])
 }), _v(" "), _c('button', {
  on: {
   "click": bindClick
  }
 },
 [_v("点我出奇迹抓同伟")])], 2)
}

甭看这段代码有点怪,但是如果告诉你,这段代码,注入 data 执行,生成的就是传得玄乎其玄的虚拟 dom 树,然后再来一本正经的解(cai)读(ce)一下,你又会觉得这段代码其实没有这么晦涩难懂。ok,结合 demo
来看看,模板相关指令都被解析成什么了:

      1) {{messge}} 解析成了 _s(message) ,果断认为这个 _s 就是 toString

      2) {{msg()}} method 解析成了 _s(msg()) ,可见每一次 render, msg 方法都会被执行一遍(即使最终没有被反应到真实 dom 上),这就是计算属性存在的意义

      3) {{ct}} 计算属性依然被解析成了 _s(ct) ,虽然计算属性可以称之为属性,但是形式上毕竟还是方法,是不是觉得比较奇怪?其实 watcher 除了监听组件,还会监听计算属性依赖的属性,一旦属性发生变更,就会执行计算属性方法,并将执行结果赋值给实例作用域下与计算属性方法名相同的属性,这就是直接使用 _s(ct) 而不是 _s(ct()) 就能正确引用计算属性值的原因,计算属性快就快在了这里

      4) v-for="item in items" 被解析成了

_l((items),
 function(item) {
  return _c('div', [_v("\n\t\t " + _s(item.text) + "\n\t ")])
 })

可见 _l 跟 for-each 很类似

      5) 至于 v-on:click 则解析成了

_c('button', {
  on: {
   "click": bindClick
  }
 }

      6) 再来看看 _c, c->create->createNode, 假设这个方法就是创建虚拟节点,回头看上述代码,是不是挺有道理,_c 创建根节点,传入的子节点同样需要这个方法创建。找找源码验证下 src/core/vdom/vnode.js,以下中文注释是我加的

constructor (
 tag?: string, //标签名
 data?: VNodeData, //属性数据,事件监听等
 children?: ?Array<VNode>, //子节点
 text?: string,
 elm?: Node,
 context?: Component,
 componentOptions?: VNodeComponentOptions
 ) {
 ...
 }

至此,也算是自圆其说了

总结

以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript小数计算出现近似值的解决办法
Feb 06 Javascript
js 通用javascript函数库整理
Aug 14 Javascript
jQuery不间断滚动效果(模拟百度新闻支持文字/图片/垂直滚动)
Feb 05 Javascript
给ListBox添加双击事件示例代码
Dec 02 Javascript
springMVC + easyui + $.ajaxFileUpload实现文件上传注意事项
Apr 23 Javascript
基于substring()和substr()的使用以及区别(实例讲解)
Dec 28 Javascript
AngularJS监听ng-repeat渲染完成的两种方法
Jan 16 Javascript
如何利用@angular/cli V6.0直接开发PWA应用详解
May 06 Javascript
JS实现点击li标签弹出对应的索引功能【案例】
Feb 18 Javascript
小程序scroll-view安卓机隐藏横向滚动条的实现详解
May 16 Javascript
vue下使用nginx刷新页面404的问题解决
Aug 02 Javascript
vue使用vant中的checkbox实现全选功能
Nov 17 Vue.js
jquery插件canvaspercent.js实现百分比圆饼效果
Jul 18 #jQuery
浅谈JS封闭函数、闭包、内置对象
Jul 18 #Javascript
iscroll实现下拉刷新功能
Jul 18 #Javascript
vue.js实现单选框、复选框和下拉框示例
Jul 18 #Javascript
vue2.0中vue-cli实现全选、单选计算总价格的实例代码
Jul 18 #Javascript
iscroll.js滚动加载实例详解
Jul 18 #Javascript
基于iScroll实现下拉刷新和上滑加载效果
Jul 18 #Javascript
You might like
PHP项目多语言配置平台实现过程解析
2020/05/18 PHP
关于JavaScript对象的动态选择及遍历对象
2014/03/10 Javascript
javascript随机之洗牌算法深入分析
2014/06/07 Javascript
用box固定长宽实现图片自动轮播js代码
2014/06/09 Javascript
JavaScript中window.open用法实例详解
2015/04/15 Javascript
JavaScript实现将xml转换成html table表格的方法
2015/04/17 Javascript
javascript随机抽取0-100之间不重复的10个数
2016/02/25 Javascript
浅析jquery与checkbox的checked属性的问题
2016/04/27 Javascript
Active控件问题小结(附解决办法)
2016/06/09 Javascript
浅谈Vue.nextTick 的实现方法
2017/10/25 Javascript
关于vue单文件中引用路径的处理方法
2018/01/08 Javascript
原生js实现无缝轮播图
2020/01/11 Javascript
修改vue源码实现动态路由缓存的方法
2020/01/21 Javascript
浅谈vue-props的default写不写有什么区别
2020/08/09 Javascript
Javascript类型判断相关例题及解析
2020/08/26 Javascript
基于Vue3.0开发轻量级手机端弹框组件V3Popup的场景分析
2020/12/30 Vue.js
[50:58]2018DOTA2亚洲邀请赛 4.1 小组赛 B组 Mineski vs EG
2018/04/03 DOTA
Python使用multiprocessing创建进程的方法
2015/06/04 Python
Python验证码识别处理实例
2015/12/28 Python
利用python获取Ping结果示例代码
2017/07/06 Python
python 删除非空文件夹的实例
2018/04/26 Python
Python3.5内置模块之shelve模块、xml模块、configparser模块、hashlib、hmac模块用法分析
2019/04/27 Python
python经典趣味24点游戏程序设计
2019/07/26 Python
解析PyCharm Python运行权限问题
2020/01/08 Python
python自定义函数def的应用详解
2020/06/03 Python
美国新蛋IT数码商城:Newegg.com
2016/07/21 全球购物
德国50岁以上交友网站:Lebensfreunde
2020/03/18 全球购物
生物化工专业个人自荐信
2013/09/26 职场文书
财务管理专业自荐信范文
2013/12/24 职场文书
蜜蜂引路教学反思
2014/02/04 职场文书
公司口号大全
2014/06/11 职场文书
个人党性分析材料
2014/12/19 职场文书
二审答辩状范文
2015/05/22 职场文书
2016猴年春节问候语
2015/11/11 职场文书
2016幼儿园教师节新闻稿
2015/11/25 职场文书
TV动画《史上最强大魔王转生为村民A》番宣CM公布
2022/04/01 日漫