Vue数据绑定简析小结

作为MVVM框架的一种,Vue最为人津津乐道的当是数据与视图的绑定,将直接操作DOM节点变为修改 data 数据,利用 Virtual DomDiff 对比新旧视图,从而实现更新。不仅如此,还可以通过 Vue.prototype.$watch 来监听 data 的变化并执行回调函数,实现自定义的逻辑。虽然日常的编码运用已经驾轻就熟,但未曾去深究技术背后的实现原理。作为一个好学的程序员,知其然更要知其所以然,本文将从源码的角度来对Vue响应式数据中的观察者模式进行简析。

初始化 Vue 实例

在阅读源码时,因为文件繁多,引用复杂往往使我们不容易抓住重点,这里我们需要找到一个入口文件,从 Vue 构造函数开始,抛开其他无关因素,一步步理解响应式数据的实现原理。首先我们找到 Vue 构造函数:

// src/core/instance/index.js
function Vue (options) {
 if (process.env.NODE_ENV !== 'production' &&
 !(this instanceof Vue)
 ) {
 warn('Vue is a constructor and should be called with the `new` keyword')
 }
 this._init(options)
}
// src/core/instance/init.js
Vue.prototype._init = function (options) {
 ...
 // a flag to avoid this being observed
 vm._isVue = true
 // merge options
 // 初始化vm实例的$options
 if (options && options._isComponent) {
  initInternalComponent(vm, options)
 } else {
  vm.$options = mergeOptions(
   resolveConstructorOptions(vm.constructor),
   options || {},
   vm
  )
 }
 ...
 initLifecycle(vm) // 梳理实例的parent、root、children和refs,并初始化一些与生命周期相关的实例属性
 initEvents(vm) // 初始化实例的listeners
 initRender(vm) // 初始化插槽,绑定createElement函数的vm实例
 callHook(vm, 'beforeCreate')
 initInjections(vm) // resolve injections before data/props
 initState(vm)
 initProvide(vm) // resolve provide after data/props
 callHook(vm, 'created')
 
 if (vm.$options.el) {
  vm.$mount(vm.$options.el) // 挂载组件到节点
 }
}

为了方便阅读,我们去除了 flow 类型检查和部分无关代码。可以看到,在实例化Vue组件时,会调用 Vue.prototype._init ,而在方法内部,数据的初始化操作主要在 initState (这里的 initInjectionsinitProvideinitProps 类似,在理解了 initState 原理后自然明白),因此我们重点来关注 initState

// src/core/instance/state.js
export function initState (vm) {
 vm._watchers = []
 const opts = vm.$options
 if (opts.props) initProps(vm, opts.props)
 if (opts.methods) initMethods(vm, opts.methods)
 if (opts.data) {
 initData(vm)
 } else {
 observe(vm._data = {}, true /* asRootData */)
 }
 if (opts.computed) initComputed(vm, opts.computed)
 if (opts.watch && opts.watch !== nativeWatch) {
 initWatch(vm, opts.watch)
 }
}
      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/385.html