用了多年vue 今天对自己了解的render 做一个梳理

一、使用template模板

先从vue 初始化开始:

众所周知项目的main.js中定义了

var app = new Vue({})这vue初始化操作

其实他会执行到

这个方法中的_init函数,在这个方法执行一些列的初始化后,判断$options是否定义el,如果定义调用

vm.$mount(vm.$options.el)函数,这个函数其实是在entry-runtime-with-compiler.js中定义的,if (!options.render) {}判断是否定义了render函数,如果未定义再判断是否定义template,如果定义直接调用‘compileToFunctions’函数将template编译成为一个匿名函数,这里先借一个简单案例,打个断点测试下执行流程

import Vue from 'vue'

/**
* 使用 template 方式
*/
var app = new Vue({
el:'#admin',
template:'<ul :class="bindCls" class="list" v-if="isShow">'+
'<li v-for="(item,index) in data" @click="clickItem(index)">{{item}}:{{index}}</li></ul>',
data(){
return {
bindCls:'a',
isShow:true,
data:['A','B','C','D']
}
},
methods:{
clickItem(index){
console.log(index);
}
}
})

最后render匿名函数内容是这样的

(function anonymous(
) {
with(this){return (isShow)?_c('ul',{staticClass:"list",class:bindCls},_l((data),function(item,index){return _c('li',{on:{"click":function($event){return clickItem(index)}}},[_v(_s(item)+":"+_s(index))])}),0):_e()}
})

看似一段简短的render,其实生成这个render是一个相当复杂的过程

1、将template 转换为render 字符串

首先在compiler 文件中的index.js中

定义了createCompiler常量该值为create-compiler.js里的createCompilerCreator函数,同时将baseCompile函数传入

而createCompilerCreator返回的是一个createCompiler函数而该createCompiler函数最后的返回值是一个

{ compile, compileToFunctions: createCompileToFunctionFn(compile) } 对象,


该对像中createCompileToFunctionFn其实是to-functiuon.js中定义的,目的就是把compile作为参数传入,进行一些列校验合并等

再回过头来调用compile实现编译操作,取得最后compile编译的结果,其实准确说是baseCompile编译后的结果,因compile中其实是调用baseCompile进行模板编译的(其中的通过parse 生成ast和generate生成code就不细说了,其实这个code中就包含了render),

createCompileToFunctionFn取得了编译后的render并使用createFunction将其转为一个匿名函数,就是上面看见的样子了


createCompilerCreator值返回到entry-runtime-with-compiler.js中的Vue.prototype.$mount函数中,同时将render挂载到了vu.$options上,再次调用runtime下index.js中的Vue.prototype.$mount函数

此时函数中又调用到了core/instance/lifecycle中的mountComponent函数;mountComponent函数在new Watcher中执行get(), 调用了updateComponent函数; 开始了执行vm._render()的操作,这个函数其实就定义在instance/render.js中;


这一连串的调用最终将template编译为render,又将其使用createElement编译为vdom,再使用__patch__初始化事件将其生成真实dom插入到页面的body中

看文字很不直观,这里截几个源码图来看看

2、render执行调用

好了现在就来看看render究竟是怎么执行的,其实他的执行也挺简单直接使用call进行调用,如

这个里面的_renderProxy其实是在init.js中绑定的





其实就是在开发阶段做一些校验,再将vm 使用proxy代理一个handlers将其返回,而生产环境就是vm实例

vm.$createElement是上面一个initRender中初始化了,其实这个主要是用于我们手写render用的

render.call(vm._renderProxy, vm.$createElement),中render就是上面说过的匿名函数

(function anonymous(
) {
with(this){return (isShow)?_c('ul',{staticClass:"list",class:bindCls},_l((data),function(item,index){return _c('li',{on:{"click":function($event){return clickItem(index)}}},[_v(_s(item)+":"+_s(index))])}),0):_e()}
})

他里面的with作用更改作用域链,执行时会首先从局部去查找里面的函数,如_c(),如果局部没有就会到其绑定的作用域this中去找,在render.call时我们知道其实是将vm传入了,而这个_c()就是上面initRender中定义的

,

而其他的如_e(),_l()这些就是在render-helper中了

而_c()又对应一个createElement函数这个就是真正的创建vdom的地方了,到此render 编译template就完成了

二、手写render

import Vue from 'vue'

var app = new Vue({
el:'#admin',
render(createElement){
return createElement('div',{
attrs:{
id:'admin'
}
},this.message)
},
data(){
return {
message:'Hello vue'
}
}
})

而手写render 其实是一样的,只是在entry-runtime-with-compiler.js不会进行compileToFunctions编译,而直接调用mount,而上面代码中render里的createElement是在render.call(vm._renderProxy, vm.$createElement)直接将createElement函数作为参数传入的,最后将该函数返回值返回给 Vue.prototype._update,然后将其使用vm.__patch__转为真实dom数插入到页面中

三、自定义函数进行模拟

手动写render


var vm = function(){} function createEl(a,b){
console.log(a,b);
} vm.$createEl = (a,b) => createEl(a,b); render.call(vm,vm.$createEl); // div1 div2 function render(createEl){
return createEl('div1','div2');
}

2、编译 template为render

   // vm构造函数
var vm= function(){};
// 创建vdom函数
function createEl(a,b){
console.log(a,b);
};
// 模拟获得编译后的render 字符串
var compiledRender = "with(this){return _c('div1','div2')}";
// 将其转换为函数
var render= new Function(compiledRender); // 为vm定义_c函数
vm._c = (a,b) => createEl(a,b);
// 为vm定义$createEl 函数
vm.$createEl = (a,b) => createEl(a,b); // 执行调用
render.call(vm, vm.$createEl ); // div1 div2

到此把整个render 流程理了一遍,感谢阅读有不正确处欢迎指正和探讨,学习永不止步,探索从未停止。

vue 中render执行流程梳理的更多相关文章

  1. tf中计算图 执行流程学习【转载】

    转自:https://blog.csdn.net/dcrmg/article/details/79028003 https://blog.csdn.net/qian99/article/details ...

  2. Spring启动执行流程梳理

    注:本文梳理启动流程使用的Spring版本:4.0.2.RELEASE 使用spring配置,都需要在web.xml中配置一个spring的监听器和启动参数(context-param),如下: &l ...

  3. [转]两表join的multi update语句在MySQL中的执行流程分析

    出自:http://hedengcheng.com/?p=209 两表join的multi update语句,执行结果与预计不一致的分析过程 — multi update结论在实际应用中,不要轻易使用 ...

  4. Vue中render: h => h(App)的含义

    // ES5 (function (h) { return h(App); }); // ES6 h => h(App); 官方文档 render: function (createElemen ...

  5. vue中render: h => h(App)的详细解释

    2018年06月20日 10:54:32 H-L 阅读数 5369   render: h => h(App) 是下面内容的缩写:   render: function (createEleme ...

  6. Java 中控制执行流程

    if-else 非常常用的流程控制非 if-else 莫属了,其中 else 是可选的,if 有两种使用方式 其一: if (Boolean-expression) { statement; } 其二 ...

  7. web项目中的执行流程参数传递详解

    还是从这个图开始讲解: struts2中有一个存放数据的中心:值栈.(值栈里面有map和对象栈) 首先:值栈的作用范围是一个请求:request作用域(一个请求是代表的一个过程,即页面点击到数据返回到 ...

  8. vue中beforeRouteEnter 执行的时机及运用的误区?

    beforeRouteEnter钩子 beforeRouteEnter (to, from, next) { console.log(this); //undefined,不能用this来获取vue实 ...

  9. MySQL之执行流程

    最近开始在学习mysql相关知识,自己根据学到的知识点,根据自己的理解整理分享出来,本篇文章会分析下一个sql语句在mysql中的执行流程,包括sql的查询在mysql内部会怎么流转,sql语句的更新 ...

  10. 一条SQL语句在MySQL中如何执行的

    本篇文章会分析一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的. 在分析之前我会先带着你看看 MySQL 的基础架构, ...

随机推荐

  1. nginx配置反向代理服务器,实现在https网站中请求http资源

    网站使用nginx作为服务器,协议从http升级为https的注意事项. 具体升级步骤请点击搜索 1.首先,修改宝塔面板配置 选择配置文件,http请求重定向为https.所有80端口请求都重定向为h ...

  2. Echarts中slider滑块调整样式

    今天遇到了一个问题,记录一下. 效果图. 原型图 一个页面中,引入了echarts的柱状图来动态显示数据,由于柱状图太高,echarts没有自动生成的滚动条,所以就用slider滑块手写了一个,但是效 ...

  3. App 用户新体验——Agora Native SDK 3.4.0

    声网Agora Native SDK 3.4.0 本月已正式上线.新版本不仅增加了更丰富的实时美声音效.屏幕共享.虚拟节拍器等功能,同时在 SDK 的稳定性.兼容性及安全合规上做了大幅度升级,希望为 ...

  4. 基于DPDK抓包的Suricata安装部署

    一.背景 Suricata支持网卡在线抓包和离线读取PCAP包两种形式的抓包: 离线抓包天然具有速度慢.非实时的特点 在线捕获数据包又包括常规网卡抓包.PF_RING和DPDK的方式 由于项目分光的流 ...

  5. 本地搭建Stable-Diffusion 教程

    前置条件 有一些python的基础 会使用git 有梯子(最好可以全局代理) Windows系统 + 英伟达1060以上显卡 (mac或者linux 以及电脑是AMD显卡,安装的话,可以自行搜索相关教 ...

  6. SpringBoot——配置及原理

    更多内容,前往IT-BLOG 一.Spring Boot全局配置文件 application.properties 与 application.yml 配置文件的作用:可以覆盖 SpringBoot ...

  7. VUE3.x之Proxy 我们为什么要使用Proxy

    Object.defineProperty 劫持数据 只是对对象的属性进行劫持 无法监听新增属性和删除属性 需要使用 vue.set, vue.delete 深层对象的劫持需要一次性递归 劫持数组时需 ...

  8. react抽离配置文件、配置@符号、调整src文件夹---配置scss、编写项目的页面结构、创建各个页面 src/views、开始路由、入口文件处修改代码、修改App.js布局文件、添加底部的导航布局、构建个人中心。。。声明式跳转路由、使用React UI库请求渲染首页数据、

    1.回顾 2.react项目的配置 react默认创建的项目配置文件在 node_modules/react-scripts 文件夹内部 2.1 抽离配置文件 cnpm run eject cnpm ...

  9. pandas之使用自定义函数

    如果想要应用自定义的函数,或者把其他库中的函数应用到 Pandas 对象中,有以下三种方法: 1) 操作整个 DataFrame 的函数:pipe() 2) 操作行或者列的函数:apply() 3)  ...

  10. .NET中使用RabbitMQ总结

    目前业界使用较多的消息队列组件有RabbitMQ.ActiveMQ.MSMQ.kafka.zeroMQ等 之间的对比可以看这里 之前搭过ActiveMQ环境带源码 点击这里 后来发现RabbitMQ性 ...