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 相关文章推荐
JS判定是否原生方法
Jul 22 Javascript
用javascript判断IE版本号简单实用且向后兼容
Sep 11 Javascript
JS截取字符串常用方法整理及使用示例
Oct 18 Javascript
通过JQuery将DIV的滚动条滚动到指定的位置方便自动定位
May 05 Javascript
动态设置form表单的action属性的值的简单方法
May 25 Javascript
详解微信小程序 通过控制CSS实现view隐藏与显示
May 24 Javascript
Node.js之readline模块的使用详解
Mar 25 Javascript
JavaScript函数式编程(Functional Programming)箭头函数(Arrow functions)用法分析
May 22 Javascript
使用js实现一个简单的滚动条过程解析
Sep 10 Javascript
jquery将json转为数据字典的实例代码
Oct 11 jQuery
JS Generator 函数的含义与用法实例总结
Apr 08 Javascript
JS实现选项卡插件的两种写法(jQuery和class)
Dec 30 jQuery
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(7) php 字符串相关应用
2010/03/05 PHP
PHP用户验证和标签推荐的简单使用
2016/10/31 PHP
数组任意位置插入元素,删除特定元素的实例
2017/03/02 PHP
Laravel 中使用 Vue.js 实现基于 Ajax 的表单提交错误验证操作
2017/06/30 PHP
PHP常用的类封装小结【4个工具类】
2019/06/28 PHP
php设计模式之正面模式实例分析【星际争霸游戏案例】
2020/03/24 PHP
脚本吧 - 幻宇工作室用到js,超强推荐base.js
2006/12/23 Javascript
js实现连续英文字符自动换行兼容ie6 ie7和firefox
2013/09/06 Javascript
9个让JavaScript调试更简单的Console命令
2016/11/14 Javascript
AngularJS自定义服务与fliter的混合使用
2016/11/24 Javascript
Angular使用ng-messages与PHP进行表单数据验证
2016/12/28 Javascript
解决Vue页面固定滚动位置的处理办法
2017/07/13 Javascript
JS计算距当前时间的时间差实例
2017/12/29 Javascript
JavaScript中Object基础内部方法图
2018/02/05 Javascript
详解easyui基于 layui.laydate日期扩展组件
2018/07/18 Javascript
javascript实现小型区块链功能
2019/04/03 Javascript
前端js中的事件循环eventloop机制详解
2019/05/15 Javascript
解决vue项目中遇到 Cannot find module ‘chalk‘ 报错的问题
2020/11/05 Javascript
详解阿里Node.js技术文档之process模块学习指南
2021/01/04 Javascript
python中引用与复制用法实例分析
2015/06/04 Python
Python callable()函数用法实例分析
2018/03/17 Python
tensorflow1.0学习之模型的保存与恢复(Saver)
2018/04/23 Python
python连接mongodb密码认证实例
2018/10/16 Python
Python实现的逻辑回归算法示例【附测试csv文件下载】
2018/12/28 Python
利用Python进行图像的加法,图像混合(附代码)
2019/07/14 Python
CK加拿大官网:Calvin Klein加拿大
2020/03/14 全球购物
计算机相关的自我评价
2014/01/15 职场文书
学生党支部先进事迹
2014/02/04 职场文书
经济担保书范文
2014/04/02 职场文书
《夕阳真美》教学反思
2014/04/27 职场文书
机关领导干部作风整顿整改措施
2014/09/19 职场文书
2014最新党员批评与自我批评材料
2014/09/24 职场文书
2015年世界水日活动总结
2015/02/09 职场文书
结婚通知短信怎么写
2015/04/17 职场文书
敬老院义诊活动总结
2015/05/07 职场文书
微信小程序实现轮播图指示器
2022/06/25 Javascript