上一章,我们分析了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])
}
}
}