关于前端:vue源码选读

44次阅读

共计 3185 个字符,预计需要花费 8 分钟才能阅读完成。

以下是 Vue 结构申明外围代码,是跨平台共用的外围代码,为什么说跨平台共用呢,这里次要指的是不同平台(web 和 weex)在 vue.prototype 上还会丰盛本人的个性(属性和办法),如比拟重要的__patch__办法、以及平台特定的组件和指令。

import {initMixin} from './init'
import {stateMixin} from './state'
import {renderMixin} from './render'
import {eventsMixin} from './events'
import {lifecycleMixin} from './lifecycle'
import {warn} from '../util/index'

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)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

其中各种 Mixin 次要是丰盛原型链上的属性或办法:

initMixin: 申明 vue.prototype.init,前面会细看,见原型 init
stateMixin: 申明 vue.prototype.[$data | $props]属性,以及 $set、$delete、$watch 办法
eventsMixin: 申明 vue.prototype.[$on | $off | $once | $emit] 办法,让 vm 本身取得了事件的注册触发能力
lifecycleMixin: 申明 vue.prototype.[_update | $forceUpdate | $destroy] 办法
renderMixin: 申明 vue.prototype.[_render | $nextTick] 办法,以及一系列外部 helpers 工具函数,以(_字母)别名办法

切换到 Web 平台视角

先暂且只看 web 平台目录给 vue.prototype 上削减 [_patch_ | $mount] 办法,并且将平台特定指令和组件装置到结构 Vue.options 上。

原型 init

其中次要局部,一部分是:

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)
}

其它蕴含 vue 的 options 的解析、合并及合并策略。

第一局部:
initLifecycle: 申明 vm 实例属性[$parent | $root | $children | $refs | _watcher | _inactive | _directInactive | _isMounted | _isDestroyed | _isBeingDestroyed],其中 $parent 会波及 parent 的解析和关系绑定,即在 parent 的 $children 中注册子 vm

initEvents: 申明 vm 实例属性[_events | _hasHookEvent],以及更新组件的事件 listeners 或者登记事件

initRender: 申明 vm 实例属性[_vnode | _staticTrees | $options | $vnode | $slots | $scopedSlots | _c | $createElement | $attrs | $listeners],其中 $attrs 和 $listeners 是响应式的属性,然而浅响应的。即在
defineReactive 申明时指定 shallow 为 true

看一看 defineReactive

  1. 肯定状况下放弃申明 key 原来的 getter 和 setter 的切面影响
  2. 通过 shallow 传入来管制对象该 key 的值是否须要持续纳入响应式治理
  3. 具体通过闭包 Dep 实例将该 key 纳入响应式零碎

通过 dep.depend 将 target:[Watcher]放入本人的 subs
看上去是调用 watcher 的 addDep 办法,并且该办法会判断是否曾经依赖该 dep

depend () {if (Dep.target) {Dep.target.addDep(this)
    }
  }

本质上最初还是调用了 dep 的 addSub 办法

addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {dep.addSub(this)
      }
    }
  }

这样一来,dep 中有 watcher(通过 subs),watcher 中也有 dep(通过 newDepIds)

Dep 类

  1. Dep 类有一个动态属性 target,代表以后 watcher,全局惟一,每次只会有一个正在进行的 watcher,然而指标 watcher 是一个栈并且有配套的入栈和出栈操作,别离对应 pushTarget 和 popTarget,这种设计能够反对嵌套的调用和 watcher 解决。
  2. Dep 类通过 depend 办法建设和 watcher 的双向关联,并且 watcher 关联 dep 是通过 dep 的 addSub 办法,反之勾销关联是通过 dep 的 removeSub 办法
  3. Dep 类通过 notify 办法来告诉关联 wathcer[]来进行更新及动作
  4. Dep 代表响应式零碎中的被观察者
  5. 所有 Dep 实例对立进行 Uid 调配,即 Uid++

Watcher 类

  1. watcher 分为 renderWatcher 和 userWatcher,二者都会关联在 vm 的_watchers[]列表中,而且 renderWatcher 还会关联在 vm 的_watcher
  2. lazy-watcher 的 value 会通过 Watcher 的 evaluate 设置
  3. Dep 的 notify 触发 watcher 的 update 会辨别几种状况,一种是 lazy 的话会设置 dirty 的 flag,如果是 sync 的话会间接同步执行 getter 并且执行 cb 回调,其它状况会 queueWatcher
  4. Watcher 的 get 执行在执行初始化解析的 getter 之前会 pushTarget,这样在 getter 执行中若有相干的 dep.depend,则会关联以后 watcher 和 dep
  5. Watcher 监听 path 的解析算法是简略的 a.b.c 属性点宰割,但解析后的后果是一个闭包
  6. watcher 的回调执行只有在其 value 发生变化,或者 value 是对象,或者是 deep 时才会执行,具备肯定的惰性
  7. queueWatcher 的算法是若 watcher 队列还没有被 flush 则增加即可,若曾经被 flush 了然而还没有到它,则对刚 flush 的待执行队列进行查找替换,否则安顿至 nextTick 下一波队列
  8. Watcher 代表响应式零碎中的订阅者
  9. 所有 Watcher 实例对立进行 Uid 调配,即 Uid++

callHook: 执行相应的钩子函数及 vm 本身的钩子事件监听(如 vm.$on(‘hook:created’))
initInjections: 略
initState:
initProvide: 略
callHook: 同上

正文完
 0