Vue源码解析(3)-AST到VNode过程

模版如下

<div>
      <h1 style="color:red">我是选项模板3</h1>
      <p>{{number}}</p>
      <p>{{message}}</p>
      <div>
        <div>
          1
          <div>11</div>  
          <div>12</div>  
        </div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
      </div>
    </div>

options.render为

function anonymous( ) { 
    with(this){
      return _c(
        'div',
        [_c('h1',{staticStyle:{"color":"red"}},[_v("我是选项模板3")])
        ,_v(" "),_c('p',[_v(_s(number))]),
        _v(" "),
        _c('p',[_v(_s(message))]),
        _v(" "),
        _m(0)]
        )
    } 
  }

对应

 <h1 style="color:red">我是选项模板3</h1>
  <p>{{number}}</p>
  <p>{{message}}</p>
  

options.staticRenderFns为 [0]

 function anonymous() {
   with(this){
     return _c('div',[
              _c('div',[_v("\n          1\n          "),
                _c('div',[_v("11")]),_v(" "),
                _c('div',[_v("12")])
              ]),_v(" "),
              _c('div',[_v("2")]),_v(" "),
              _c('div',[_v("3")]),_v(" "),
              _c('div',[_v("4")]),_v(" "),
              _c('div',[_v("5")])
            ])
    }
  }

对应的template为

  <div>
    <div>
      1
      <div>11</div>  
      <div>12</div>  
    </div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
  </div>

render-helpers 下 index.js

export function installRenderHelpers (target) {
  target._o = markOnce
  target._n = toNumber
  target._s = toString
  target._l = renderList
  target._t = renderSlot
  target._q = looseEqual
  target._i = looseIndexOf
  target._m = renderStatic
  target._f = resolveFilter
  target._k = checkKeyCodes
  target._b = bindObjectProps
  target._v = createTextVNode
  target._e = createEmptyVNode
  target._u = resolveScopedSlots
  target._g = bindObjectListeners
}

render.js

function renderMixin (Vue) {
   //把_v,_m等方法挂载到vue原型上
 installRenderHelpers(Vue.prototype)
}
function initRender (vm) {
 vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
}

这样this._c就是执行createElement
this._m就是执行renderStatic
_v就是执行createTextVNode

在vdom下create-element.js

tag  // 标签
data    // 关于这个节点的data值,包括attrs,style,hook等
children  // 子vdom节点
context  // vue实例对象
function _createElement(context,tag,data,children,normalizationType) {
  vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
  )
 return vnode
}

创建vdom对象

vnode

class VNode {
    constructor (
    tag,
    data,     // 关于这个节点的data值,包括attrs,style,hook等
    children, // 子vdom节点
    text,     // 文本内容
    elm,      // 真实的dom节点
    context,  // 创建这个vdom的上下文
    componentOptions,
    asyncFactory
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.fnContext = undefined
    this.fnOptions = undefined
    this.fnScopeId = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }
}

下面dom分别依次执行,先渲染里面然后再渲染外层

1.   [_c('h1',{staticStyle:{"color":"red"}},[_v("我是选项模板3")])
  <h1 style="color:red">我是选项模板3</h1>
2._c('p',[_v(_s(number))]
 <p>{{number}}</p>
3._c('p',[_v(_s(message))])
<p>{{message}}</p>
4._m(0)是缓存渲染数
function anonymous() {
   with(this){
     return _c('div',[
              _c('div',[_v("\n          1\n          "),
                _c('div',[_v("11")]),_v(" "),
                _c('div',[_v("12")])
              ]),_v(" "),
              _c('div',[_v("2")]),_v(" "),
              _c('div',[_v("3")]),_v(" "),
              _c('div',[_v("4")]),_v(" "),
              _c('div',[_v("5")])
            ])
    }
  }

vdom顺序依次为
(1)<div>11</div>  
(2)<div>12</div> 
(3)<div> 
     1
     <div>11</div>  
     <div>12</div>  
   </div>
(4)<div>2</div>
(5)<div>3</div>
(6)<div>4</div>
(7)<div>5</div>
(8)<div>
     <div>
      1
      <div>11</div>  
      <div>12</div>  
    </div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
  </div>
(9)<div>
  <h1 style="color:red">我是选项模板3</h1>
  <p>{{number}}</p>
  <p>{{message}}</p>
  <div>
     <div>
      1
      <div>11</div>  
      <div>12</div>  
    </div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
  </div>
</div>
    

《Vue源码解析(3)-AST到VNode过程》

《Vue源码解析(3)-AST到VNode过程》

最后一次执行最外面的
《Vue源码解析(3)-AST到VNode过程》

render-static.js给m[0]静态树做了缓存处理

renderStatic (
  index,
  isInFor
) {
//缓存处理
 const cached = this._staticTrees || (this._staticTrees = [])
// staticRenderFns被执行
tree = cached[index] = this.$options.staticRenderFns[index].call(
    this._renderProxy, // 代理可以理解为vue实例对象,多了一些提示处理
    null,
    this // for render fns generated for functional component templates
)
markStatic(tree, `__static__${index}`, false)
return tree
}

function markStatic (
  tree,
  key,
  isOnce
) {
function markStaticNode (node, key, isOnce) {
  node.isStatic = true  //静态树为true
  node.key = key  // `__static__${index}` 标志
  node.isOnce = isOnce // 是否是v-once
}
markStaticNode(node, key, isOnce)
}

然后又重新执行

function anonymous(
) {
with(this){return _c('div',[_c('div',[_v("\n          1\n          "),_c('div',[_v("11")]),_v(" "),_c('div',[_v("12")])]),_v(" "),_c('div',[_v("2")]),_v(" "),_c('div',[_v("3")]),_v(" "),_c('div',[_v("4")]),_v(" "),_c('div',[_v("5")])])}
}

打印render()结果
静态树有了isStatic和key值
《Vue源码解析(3)-AST到VNode过程》

补充个小问题 render函数里number 还是变量是什么时候变成数字的
因为function有个with(this){}改变了作用域,当this.render()执行时候,那么this就是指得vue, $options.data 已经通过defineProperty代理到了vue下,访问this.data就是访问$options.data 所以number就是数字啦

    原文作者:织雪纱奈
    原文地址: https://segmentfault.com/a/1190000016277098
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞