写文章不容易,点个赞呗兄弟
专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧
研究基于 Vue版本 【2.5.17】
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧html
【Vue原理】Render - 源码版 之 主要 Render node
compile 咱们已经讲了九篇的内容了,终于走到了 render,今天就来给本身记录下渲染三部曲的第二部,render,咦,render 内容很少的,就两篇文章哈哈哈数组
噔噔噔噔函数
render 的做用你们应该清楚学习
就是 执行 compile 生成的 render函数,而后获得返回的 vnode 节点this
好比如今存在这个简单的模板spa
通过 compile 以后,解析成了对应的 render 函数,以下prototype
function render() { with(this) { return _c('div', { attrs: { "data": 111 } }, [_v(111)]) } }
看着这个莫名其妙的 render 函数,里面都是些什么东西?3d
不怕,主要是出现了两个函数,咱们要探索的就是这两个东西code
_c , _v
这个两个函数的做用,都是建立 Vnode,可是建立的过程不同
而且 render 函数执行的时候,会绑定上 模板对应的实例 为上下文对象
模板是属于哪一个实例的,就绑定哪一个实例
render.call(实例)
再经过 with 的做用
调用 _c 和 _v 就至关于 vm._c 和 vm._v
如今就来看看 vm._v 是哪里来的
function installRenderHelpers(target) { target._v = createTextVNode; } installRenderHelpers(Vue.prototype);
由上面可知,每一个Vue 实例都会继承有 _v 这个方法,因此能够经过 vm._v 直接调用
再来看看 _v 对应的 createTextVNode 的做用是什么
建立文本节点!!
看下源码
function createTextVNode(val) { return new VNode( undefined, undefined, undefined, String(val) ) }
好比这个模板
{{data}} 虽然是字符串,可是也要做为一个子节点存在,因此就当作是 文本节点
而 data 的值是 111
而后 上面的模板就会获得这样的 Vnode 结构以下
_c 是一个大头,render 的重中之重,先来看看他是怎么来的
function initRender(vm) { vm._c = function(a, b, c, d) { return createElement(vm, a, b, c, d); }; } Vue.prototype._init = function(options) { initRender(this) }
在实例初始化的时候,就会给实例绑定上 _c 方法
因此,vm 能够直接调用到 _c
看了上面的源码,看到 _c 内部调用了 createElement
那就来看看createElement 的源码吧
我的已经简化得很是简单,以为不偏离咱们的主题就能够
function createElement( context, tag, data, children ) { return _createElement( context, tag, data, children ) } function _createElement( context, tag, data, children ) { var vnode; if (若是tag是正常html标签) { vnode = new VNode( tag, data, children, undefined, undefined, context ); } .....若是tag是组件名,就特殊处理 ,处理流程已经省略 if (Array.isArray(vnode)) return vnode else { // ...动态绑定 style ,class,代码已经省略 return vnode } }
你一看就能够看到,createElement 主要就是调用了 new VNode,固然了,render 就是为了建立 vnode 的嘛
你在前面也看到了 render 函数,有传了不少参数给 _c,以下,_c 再把这些参数传给构造函数 VNode
_c('div', { attrs: {"data": 111} }, [_v(111)] )
上面这些参数都会传给 Vnode,并保存在建立的 Vnode 中
function VNode( tag, data, children, text ) { this.tag = tag; this.data = data; this.children = children; this.text = text; }
而后获得这么一个 Vnode
{ tag:"div", data:{ attrs: {"data": 111} }, children:[{ tag:undefined, data:undefined, text:111 }] }
说到这里,已经能很清楚 render 内部是如何建立Vnode 了
可是这里只是其中一种小小的简单 render
要是项目中的render,数据是不少,很复杂的
而咱们主要要把握的是主要流程就能够了
不过,还有必要记录其余 render,那就是遍历
看下面这个 template
解析成下面的render
function render() { with(this) { return _c('div', _l(2,function(item, index) { return _c('span') }) ) } }
看到一个 _l, 他一定就是遍历生成 Vnode 的幕后黑手了
一样的,_l 和 _v 在同一个地方 installRenderHelpers 注册的
function installRenderHelpers(target) { target._l = renderList; }
不客气地搜出 renderList 源码出来
先跳到后面的分析啊,源码有点长了,虽然很简单
function renderList(val, _render) { var ret, i, l, keys, key; // 遍历数组 if ( Array.isArray(val) ) { ret = new Array(val.length); // 调用传入的函数,把值传入,数组保存结果 for (i = 0, l = val.length; i < l; i++) { ret[i] = _render(val[i], i); } } // 遍历数字 else if (typeof val === 'number') { ret = new Array(val); // 调用传入的函数,把值传入,数组保存结果 for (i = 0; i < val; i++) { ret[i] = _render(i + 1, i); } } // 遍历对象 else if (typeof val =="object") { keys = Object.keys(val); ret = new Array(keys.length); // 调用传入的函数,把值传入,数组保存结果 for (i = 0, l = keys.length; i < l; i++) { key = keys[i]; ret[i] = _render(val[key], key, i); } } // 返回 vnode 数组 return ret }
看到 renderList 接收两个参数,val 和 render,而 _l 调用的时候,也就是传入的这两个参数,好比下面
_l(2,function(item, index) { return _c('span') })
val 就是 2,_render 就是上面的函数
遍历的数据分为三种类型,一种是对象,一种是数字,一种是数组
重要是这个回调
一、renderList 每次遍历都会执行回调,并把的每一项 item 和 index 都传入 回调中
二、回调执行完毕,会返回 vnode
三、使用数组保存 vnode,而后 遍历完毕就返回 数组
因而能够看上面的 render 函数 ,传入了 数字2,和 建立 span 的回调
_l(2,function(item, index) { return _c('span') })
_l 执行完毕,内部遍历两次,最后返回 两个 span vnode 的数组,而后传给外层的 _c ,做为 vnode.children 保存
render 执行完毕,获得这样的 vnode
{ tag:"div", data:undefined, children:[{ tag:"span", data:undefined },{ tag:"span", data:undefined }] }
都灰常简单啊,没写以前,我还以为内容应该挺多的,写完发现还能够
固然还有其余的 render ,可是我都已经在其余文章中有详细的记录了,能够直接点链接观看
好比要模板含有 filter
好比要模板含有 普通 slot
Slot - 源码版之普通插槽
好比要模板含有 做用域 slot
如今咱们来解决一个问题
render 何时开始执行?
能够参考另外一篇文章:
从模板到DOM的简要流程
每一个模板通过 compile 都会生成一个 render 函数
render 做为 渲染三部曲的第二部,主要做用就是 执行 render,生成 Vnode
把 template 上绑定的数据,都保存到 vnode 中
而后,生成 Vnode,就是为了给 渲染三部曲的 第三部 Diff 提供源动力
从而完成 DOM 挂载