【闲谈Vue1.0】- 3.instance - Vue实例(2)

上一章,我们分析了api文件夹中的模块。这章我们将分析internal文件下都是何方神圣。


内部方法初始化模块 - init.js

init( ) - 内部方法初始化

Vue.prototype._init = function (options) {
  options = options || {}
  this.$el = null
  ...
  // 更新 v-ref
  // @internal/events
  this._updateRef()
  ...
  // 触发初始化hook
  // @internal/events
  this._callHook('init')
  // 初始化数据监控和作用域继承
  // @internal/state
  this._initState()
  // 安装内部事件和用户声明的事件
  // @internal/events
  this._initEvents()
  // 触发已创建hook
  // @internal/events
  this._callHook('created')
  // 渲染元素
  // @api/lifecycle
  if (options.el) {
    this.$mount(options.el)
  }
}

事件内部方法模块 - events.js

initEvents( ) - 注册组件事件

Vue.prototype._initEvents = function () {
  var options = this.$options
  if (options._asComponent) {
    // 将'v-on:click="click1()"'此类事件属性,解析成eventName和fn
    // 然后通过$on(eventName, fn)的形式注册到vm上
    // 这个里面值得好好看看
    registerComponentEvents(this, options.el)
  }
  // 用$on,$watch分别初始化options中的events,watch
  // {
  //  events: {
  //    sayhi: function() { ... }
  //  },·
  //  watch: {
  //    'a': function() { ... }
  //  }
  // }
  registerCallbacks(this, '$on', options.events)
  registerCallbacks(this, '$watch', options.watch)
}

initDOMHooks( ) - 注册组件生命周期的hook

Vue.prototype._initDOMHooks = function () {
  // 分别注册attached,detached两个hook
  // onAttached,onDetached,两个方法不仅仅给当前组件注册
  // 并且进行递归,给所有的子孙节点都进行了注册
  this.$on('hook:attached', onAttached)
  this.$on('hook:detached', onDetached)
}

callHook( ) - 触发hook

Vue.prototype._callHook = function (hook) {
  this.$emit('pre-hook:' + hook)
  var handlers = this.$options[hook]
  if (handlers) {
    // 遍历注册这个hook的所有回调
    for (var i = 0, j = handlers.length; i < j; i++) {
      handlers[i].call(this)
    }
  }
  this.$emit('hook:' + hook)
}

生命周期内部方法模块 - lifecycle.js

updateRef( ) - 更新 v-ref 引用

Vue.prototype._updateRef = function (remove) {
  // ref是通过v-ref设置的属性
  // v-ref="some-ref"
  var ref = this.$options._ref
  if (ref) {
    var refs = (this._scope || this._context).$refs
    // 是否删除引用
    if (remove) {
      if (refs[ref] === this) {
        refs[ref] = null
      }
    } else {
      // 将自身设置到refs数组中,
      refs[ref] = this
    }
  }
}

compile( ) - 编译

Vue.prototype._compile = function (el) {
  var options = this.$options

  var original = el
  // 转换节点
  // @compiler/transclude
  el = transclude(el, options)
  // 初始化$el节点
  // case:fragment时,$el为第一个子节点
  // case:否则,$el为el
  this._initElement(el)

  ...

  // 编译根节点
  var rootLinker = compileRoot(...)

  // slot的嵌入
  resolveSlots(...)

  // compile and link the rest
  var contentLinkFn
  var ctor = this.constructor
  // 缓存组件是否被链接过
  if (options._linkerCachable) {
    contentLinkFn = ctor.linker
    // 如果没有,先编译,获取链接器
    if (!contentLinkFn) {
      contentLinkFn = ctor.linker = compile(el, options)
    }
  }

  // 链接阶段
  var rootUnlinkFn = rootLinker(this, el, this._scope)
  var contentUnlinkFn = contentLinkFn
    ? contentLinkFn(this, el)
    : compile(el, options)(this, el)

  // 解链方法
  this._unlinkFn = function () {
    rootUnlinkFn()
    contentUnlinkFn(true)
  }

  // 替换
  if (options.replace) {
    replace(original, el)
  }
  ...
}

initElement( ) - 初始化$el对象

Vue.prototype._initElement = function (el) {
  // fragment取第一个节点
  if (isFragment(el)) {
    this._isFragment = true
    this.$el = this._fragmentStart = el.firstChild
    ...
  } else {
    // 否则直接取
    this.$el = el
  }
  ...
}

bindDir( ) - 创建,绑定一个指令到元素上

Vue.prototype._bindDir = function (descriptor, node, host, scope, frag) {
  this._directives.push(
    new Directive(...)
  )
}

destroy( ) - 删除一个实例

Vue.prototype._destroy = function (remove, deferCleanup) {

  ...

  var self = this
  // 删除dom元素
  if (remove && this.$el) {
    pendingRemoval = true
    this.$remove(...)
  }

  ...

  // 如果父节点被销毁了,则将自己的引用也删除
  var parent = this.$parent
  if (parent && !parent._isBeingDestroyed) {
    parent.$children.$remove(this)
    // 删除引用
    this._updateRef(true)
  }
  // 销毁所有子元素
  i = this.$children.length
  while (i--) {
    this.$children[i].$destroy()
  }
  // 卸载所有属性
  if (this._propsUnlinkFn) {
    this._propsUnlinkFn()
  }
  // 卸载所有指令
  if (this._unlinkFn) {
    this._unlinkFn()
  }
  // 卸载所有观察者
  i = this._watchers.length
  while (i--) {
    this._watchers[i].teardown()
  }
  ...
}

cleanup( ) - 清空所有内部变量,确保垃圾回收

Vue.prototype._cleanup = function () {
  ...
  this.$el =
  ...

生命周期内部方法模块 - misc.js

applyFilters( ) - 应用所有过滤器

Vue.prototype._applyFilters = function (value, oldValue, filters, write) {
  ...
  for (i = 0, l = filters.length; i < l; i++) {
    ...
    value = fn.apply(this, args)
  }
  return value
}

resolveComponent( ) - 加载component

Vue.prototype._resolveComponent = function (value, cb) {
  var factory

  ...
  // 从asset中加载component
  factory = resolveAsset(this.$options, 'components', value, true)

  // 异步组件
  // 三种状态策略
  if (!factory.options) {
    if (factory.resolved) {
      // 完成态,直接执行
      cb(factory.resolved)
    } else if (factory.requested) {
      // 正在请求,插入请求队列
      factory.pendingCallbacks.push(cb)
    } else {
      // 标记状态
      factory.requested = true
      var cbs = factory.pendingCallbacks = [cb]
      factory.call(this, function resolve (res) {
        ...
        // 缓存resolve
        factory.resolved = res
        // 执行回调
        for (var i = 0, l = cbs.length; i < l; i++) {
          cbs[i](res)
        }
      }, function reject (reason) {
        ...
      })
    }
  } else {
    // 普通组件
    cb(factory)
  }
}

状态相关内部方法 - state.js

initState( ) - 初始化状态入口方法

Vue.prototype._initState = function () {
  // 初始化各种数据入口
  ...
}

initProps( ) - 初始化props方法

Vue.prototype._initProps = function () {
  ...
  el = options.el = query(el)
  ...
  // 链接属性和元素
  this._propsUnlinkFn = compileAndLinkProps(this, el, props, this._scope)
}

setData( ) - 设置data方法

Vue.prototype._setData = function (newData) {
  newData = newData || {}
  var oldData = this._data

  ...

  // 双向比较,删除不存在的值,同时也删除了它的代理
  keys = Object.keys(oldData)
  i = keys.length
  while (i--) {
    ...
    this._unproxy(key)
  }

  // 双向比较,给新增的值加代理
  keys = Object.keys(newData)
  i = keys.length
  while (i--) {
    ...
    this._proxy(key)
  }

  // 删除旧值的观察者
  oldData.__ob__.removeVm(this)

  // 添加新值的观察者
  observe(newData, this)

  // 立即开始下一次循环
  this._digest()
}

proxy( ),unproxy( ) - 设置与取消代理

Vue.prototype._proxy = function (key) {
  ...
  var self = this
  // 设置getter/setter代理
  Object.defineProperty(self, key, {
    configurable: true,
    enumerable: true,
    get: function proxyGetter () {
      return self._data[key]
    },
    set: function proxySetter (val) {
      self._data[key] = val
    }
  })
}

Vue.prototype._unproxy = function (key) {
  ...
  delete this[key]
}

initComputed( ) - 初始化Computed

Vue.prototype._initComputed = function () {
  var computed = this.$options.computed
  if (computed) {
    for (var key in computed) {
      var userDef = computed[key]
      var def = {
        enumerable: true,
        configurable: true
      }
      // like.
      // {
      //  computed: {
      //    a: function() {...}
      //  }
      // }
      if (typeof userDef === 'function') {
        def.get = makeComputedGetter(userDef, this)
        def.set = noop
      } else {
        // like.
        // {
        //  computed: {
        //    a: {
        //      get: function() { ... },
        //      set: function() { ... }
        //    }
        //  }
        // }
        def.get = userDef.get
          ? userDef.cache !== false
            ? makeComputedGetter(userDef.get, this)
            : bind(userDef.get, this)
          : noop
        def.set = userDef.set
          ? bind(userDef.set, this)
          : noop
      }
      Object.defineProperty(this, key, def)
    }
  }
}

initMethods( ) - 初始化method

Vue.prototype._initMethods = function () {
  var methods = this.$options.methods
  if (methods) {
    for (var key in methods) {
      // 将vm.$options.methods下面的方法
      // 遍历绑定到vm下面
      // @util/lang
      this[key] = bind(methods[key], this)
    }
  }
}

initMeta( ) - 初始化元信息,比如$index, $key & $value.

Vue.prototype._initMeta = function () {
  var metas = this.$options._meta
  if (metas) {
    for (var key in metas) {
      // @observer/index
      // 定义拥有getter/setter的metas
      defineReactive(this, key, metas[key])
    }
  }
}
*****
Written by Zheng Yu on 30 November 2016