Vue源码系列 -- 数据驱动流程分析(3)

158 阅读2分钟

render 函数

什么是 Vritual DOM?

Virtual DOM 是vue2.0以后才引入的概念,一个真实的DOM他的操作性能是比较高的,一个div元素的第一层key,就有将近300个属性,Virtual DOM 是用 JavaScript 对象来表示 DOM 信息和结构,也就是在js和DOM之间做了一层缓存,去操作一个VDOM的代价自然是要小于操作一个真实DOM的,而对于真实DOM的任何细微操作都有可能引起页面的 重绘回流 ,从性能的角度出发,这是我们不愿意看到的情况,所以通过对象模拟出来的DOM结构,我们可以对他先进行操作,最后统一把修改映射到真实DOM上。Vue2.0的Virtual DOM参考自 snabbdom

virtual DOM相比真是DOM的优势

  • 虚拟 DOM 不会立刻进行 回流与重绘 操作
  • 虚拟 DOM 进行频繁修改,然后一次性比较并修改真实 DOM 中需要改的部分,最后在真实 DOM 中进行排版与重绘,减少过多DOM节点排版与重绘损耗
  • 虚拟 DOM 有效降低大面积真实 DOM 的重绘与排版,因为最终与真实 DOM 比较差异,可以只渲染局部

vm._render()

承接 数据驱动流程分析(2)
挂载方法vm._update(vm._render(), hydrating)中的vm._render()函数
vm._render()最终会返回一个 VNode,并传入vm._update()函数

// src/croe/instance/render.js
Vue.prototype._render = function (): VNode {
  const vm: Component = this
  const { render, _parentVnode } = vm.$options
	// ...
  try {
    // ...
    vnode = render.call(vm._renderProxy, vm.$createElement)
  } catch (e) {
      // ...
  }
	// ...
  return vnode
}

_render() 方法的核心部分是 vnode =(vm._renderProxy, vm.$createElement) ,即vm._renderProxy来调用render方法,并传入vm.$createElement作为参数
看一下vm._renderProxy 是如何定义的

// src/core/instance/init.js
if (process.env.NODE_ENV !== 'production') {
	// 开发环境下会实例化一个 proxy
  initProxy(vm)
} else {
	// 生产环境下 vm._renderProxy 实际上就是 vm本身
  vm._renderProxy = vm
}

hasProxy是用于判断当前浏览器环境是否支持Proxy这个方法

// src/core/instance/proxy.js
initProxy = function initProxy (vm) {
  if (hasProxy) {
    // determine which proxy handler to use
    const options = vm.$options
    const handlers = options.render && options.render._withStripped
      ? getHandler
		// 执行了hasHandler
      : hasHandler
    vm._renderProxy = new Proxy(vm, handlers)
  } else {
    vm._renderProxy = vm
  }
}
// ....
const hasHandler = {
  has (target, key) {
    const has = key in target
    const isAllowed = allowedGlobals(key) ||
      (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data))
    if (!has && !isAllowed) {
      if (key in target.$data) warnReservedPrefix(target, key)
		// 如果访问了不存在于vm上的属性,则触发warnNonPresent
      else warnNonPresent(target, key)
    }
    return has || !isAllowed
  }
}

// ...
// 这个报错是常见的错误,一般我们访问data中未定义的数据时便会报这个错
const warnNonPresent = (target, key) => {
  warn(
    `Property or method "${key}" is not defined on the instance but ` +
    'referenced during render. Make sure that this property is reactive, ' +
    'either in the data option, or for class-based components, by ' +
    'initializing the property. ' +
    'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
    target
  )
}