看一下源码里的render.js

206 阅读1分钟

initRender

init初始化做了两件事:createElement 和 实现$listeners的响应化

其中创建元素是由vue自带的complier实现的,其实我对这玩意没啥理解,只是顺藤摸瓜找到了一个叫_c的渲染函数,创建初始化元素就是由它帮我们干的;在代码中可以看到_c是由vue自带的complier编译解析的,而$createElement是我们自己写的render中的h函数,而响应化则通过defineProperty的set去notify()通知subscribers有值被修改,并执行watchers的update函数

// 初始化渲染 _c $createElement 和$attrs $listeners的响应化
export function initRender (vm: Component) {
  vm._vnode = null 
  vm._staticTrees = null 
  const options = vm.$options
  const parentVnode = vm.$vnode = options._parentVnode 
  const renderContext = parentVnode && parentVnode.context
  vm.$slots = resolveSlots(options._renderChildren, renderContext)
  vm.$scopedSlots = emptyObject
  // 看这里————------------------------
  // 默认编译器 内部由模板编译的函数 _c
  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
  // $createElement 自定义h函数 也就是在initRender中声明
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
  // $attrs和$listeners需要被公开,以便更容易地进行临时创建
  // 获取父节点 
  const parentData = parentVnode && parentVnode.data

  /* 然后就是attrs和listeners的响应化,通过defineProperty的set去notify()通知**subscribers**有值被修改,并执行**watchers**的update函数 */
  // 忽略环境部分
  if (process.env.NODE_ENV !== 'production') {
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
      !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
    }, true)
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
      !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
    }, true)
  } else {
    // 看这里————------------------------
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
  }
}

renderMixin

renderMixin主要也做了两件事:nextTick和render,nextTick在下篇日记里专门记录

// $nextTick和render
export function renderMixin (Vue: Class<Component>) {
  installRenderHelpers(Vue.prototype)
  // $nextTick :下次一定
  Vue.prototype.$nextTick = function (fn: Function) {
    return nextTick(fn, this)
  }
  //自己写的render 
  Vue.prototype._render = function (): VNode {
    const vm: Component = this
    // 拿到render和父节点
    const { render, _parentVnode } = vm.$options
    // 设置父节点的vnode,允许渲染函数载入,并在渲染完成后把父vnode赋给$vnode
    if (_parentVnode) {
      vm.$scopedSlots = normalizeScopedSlots(
        _parentVnode.data.scopedSlots,      
        vm.$slots,
        vm.$scopedSlots
      )
    }
    vm.$vnode = _parentVnode     // 渲染自己,把自己赋给$vnode
    let vnode
    try {
      currentRenderingInstance = vm
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
        try {
          vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
        } catch (e) {
          handleError(e, vm, `renderError`)
          vnode = vm._vnode
        }
      } else {
        vnode = vm._vnode
      }
    } finally {
      currentRenderingInstance = null
    }
 
    //以下为将渲染完的node挂载到dom的场景
    if (Array.isArray(vnode) && vnode.length === 1) {
      vnode = vnode[0]
    }
    if (!(vnode instanceof VNode)) {
      if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
        warn(
          'Multiple root nodes returned from render function. Render function ' +
          'should return a single root node.',
          vm
        )
      }
      vnode = createEmptyVNode()
    }
    vnode.parent = _parentVnode
    return vnode
  }
}