FED

©FrontEndDev.org
2015 - 2024
web@2.22.0 api@2.20.0

Vuejs核心代码解析之初始化(一)

首先看最简单的一个two way :

//初始化过程
var vm = new Vue({
	data:{
		'show':'init'
	}
});

//编译过程
vm.$mount('#node');

上面是一个最简单的双向绑定,今天要说的就是“初始化的过程”,这个过程相对比较简单, 主要有以下:

  • 初始化
  • 为vm添加data访问器
  • 创建Observer, 并未data定义__ob__属性
  • 创建__ob__访问器,与编译触发相关联

以下代码解释:

exports._init = function (options) {

  options = options || {}

  this.$el           = null
  this.$root         = options._root || this

  this._watcherList  = [] // all watchers as an array
  this._watchers     = {} // internal watchers as a hash
  this._directives   = [] // all directives

  // 标记,避免被观察
  this._isRebirth = true

  // events bookkeeping
  this._events         = {}    // registered callbacks
  this._eventsCount    = {}    // for $broadcast optimization
  this._eventCancelled = false // for event cancellation

  // block instance properties
  this._isBlock     = false
  this._blockStart  =          // @type {CommentNode}
  this._blockEnd    = null     // @type {CommentNode}

  // lifecycle state
  this._isCompiled  =
  this._isDestroyed =
  this._isReady     =
  this._isAttached  =
  this._isBeingDestroyed = false
  this._unlinkFn    = null

  // children
  this._children = []
  this._childCtors = {}

  // transcluded components that belong to the parent.
  // need to keep track of them so that we can call
  // attached/detached hooks on them.
  this._transCpnts = []


  // props used in v-repeat diffing
  this._new = true
  this._reused = false

  // merge options.
  options = this.$options = mergeOptions(
    this.constructor.options,
    options,
    this
  )

  // set data after merge.
  this._data = options.data || {}

  // initialize data observation and scope inheritance.
  this._initScope()

}

与双向绑定关键为最后2个:

  // set data after merge.
  this._data = options.data || {}

  // initialize data observation and scope inheritance.
  this._initScope()

执行_initScope 初始化访问器

//initScope
exports._initScope = function () {
  //add accessor
  this._initData()
}

/**
 * Initialize the data.
 */

exports._initData = function () {
  // proxy data on instance
  var data = this._data
  var i, key
  var keys = Object.keys(data)
  i = keys.length
  while (i--) {
    key = keys[i]
    if (!_.isReserved(key)) {
      //vm.prop 与 vm._data.prop 代理
      this._proxy(key)
    }
  }
  // observe data
  Observer.create(data).addVm(this)
}

initData 主要分为两部分:

  • vm.prop 与 vm._data.prop 之间创建访问器
  • 创建Observer, 以及推入vms
//vm.prop 与 vm._data.prop  之间创建访问器

exports._proxy = function (key) {
  // need to store ref to self here
  // because these getter/setters might
  // be called by child instances!
  var self = this
  Object.defineProperty(self, key, {
    configurable: true,
    enumerable: true,
    get: function proxyGetter () {
      return self._data[key]
    },
    set: function proxySetter (val) {
      self._data[key] = val
    }
  })
}

Observer

function Observer (value, type) {
  this.id = ++uid
  this.value = value
  this.active = true
  this.deps = []
  _.define(value, '__ob__', this)
  if (type === OBJECT) {
    this.walk(value)
  }
}

//跳过$和_
p.walk = function (obj) {
  var keys = Object.keys(obj)
  var i = keys.length
  var key, prefix
  while (i--) {
    key = keys[i]
    prefix = key.charCodeAt(0)
    if (prefix !== 0x24 && prefix !== 0x5F) { // skip $ or _
      this.convert(key, obj[key])
    }
  }
}

//最重要的一步,当然这部要配合 vm.$mount来看
p.convert = function (key, val) {
  var ob = this
  var childOb = ob.observe(val)
  var dep = new Dep()
  if (childOb) {
    childOb.deps.push(dep)
  }
  Object.defineProperty(ob.value, key, {
    enumerable: true,
    configurable: true,
    get: function () {
      // Observer.target is a watcher whose getter is
      // currently being evaluated.
      if (ob.active && Observer.target) {
        Observer.target.addDep(dep)
      }
      return val
    },
    set: function (newVal) {
      if (newVal === val) return
      // remove dep from old value
      var oldChildOb = val && val.__ob__
      if (oldChildOb) {
        oldChildOb.deps.$remove(dep)
      }
      val = newVal
      // add dep to new value
      var newChildOb = ob.observe(newVal)
      if (newChildOb) {
        newChildOb.deps.push(dep)
      }
      dep.notify()
    }
  })
}

vue的初始化还算简单,compile阶段还正在学习中。。!!!