Vue 源码解析(一):依赖收集(Observer,Dep与Watcher对象)

Vue 实例新建流程

Vue源码目录结构说明

  • src
    • compiler 解析模板生成AST和render函数
    • core
      • components 目前只有keep-alive组件
      • global-api 向Vue对象注入全局方法:Vue.use(),Vue.extend()等
      • instance 向Vue实例对象注入方法:this.$emit(),this.$forceUpdate()等
      • observer 实现data与Watch对象的依赖收集与更新
      • util 工具类
      • vdom Vdom有关方法
    • entries Vue 不同类型源码入口
    • platforms
    • server
    • sfc
    • shared

这次主要我们用到的目录只有:global-api,instance,observer三个。主要介绍Vue实例化时初始数据和计算属性时的源码具体内容。

initData初始化数据

Vue.prototype._init = function (options?: Object) {
   
    const vm: Component = this
    // a uid
    vm._uid = uid++
    ...
    // a flag to avoid this being observed
    vm._isVue = true
    ...
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(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)
    }
  }
}

当我们在 new Vue() 时首先调用得就是 _init() 方法。它主要初始化生命周期,事件,渲染相关数据,调用 beforeCreate 钩子,初始化provide/injections相关数据,初始化data相关数据,调用 created 钩子。最后调用 $mount() 方法挂载到对应的DOM元素上。

今天我们主要看一下初始化数据相关的部分。也就是 _init() 方法里面的 initState() 函数。

// 初始化数据
export function initState (vm: Component) {
   
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props) // 1. 初始化参数
  if (opts.methods) initMethods(vm, opts.methods) // 2、初始化方法
  if (opts.data) {
    // 3、初始化数据
    initData(vm)
  } else {
   
    observe(vm._data = {
   }, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed) // 4、初始化计算属性
  if (opts.watch) initWatch(vm, opts.watch) // 5、初始化监听属性
}

这个 initState() 代码很短,要做的事情也很清楚,就是初始化了 props 参数,methods 方法,data 数据,computed 计算属性,watch 监听属性。这次我们主要关注 initData() 方法和 initComputed() 方法。了解Vue依赖收集与双向绑定的完整流程。

所以,首先我们先看一下 initData() 方法。

function initData (vm: Component) {
   
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {
   }
  ...
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  let i = keys.length
  while (i--) {
   
    if (props && hasOwn(props, keys[i])) {
    // 判断props和data里的属性是否有重复
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${
     keys[i]}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(keys[i])) {
    // 判断data属性不以$或_开头
      proxy(vm, `_data`, keys[i]) // 将this.XXX代理到this._data.XXX
    }
  }
  // observe data
  observe(data, true /* asRootData */) // 
}

function getData (data: Function, vm: Component): any {
   
  try {
   
    return data.call(vm)
  } catch (e) {
   
    handleError(e, vm, `data()`)
    return {
   }
  }
}

export function proxy (target: Object, sourceKey: string, key: string) {
   
  sharedPropertyDefinition.get = function proxyGetter ()
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值