title | date | tags |
---|---|---|
vue源码解析 |
2019-06-22 09:56:20 -0700 |
做了哪些事情呢?
- 从
src/platforms/web/runtime/index.js
里导入Vue
- 获取Vue原型上的
$mount
方法,先存下来const mount = Vue.prototype.$mount
- 重写
Vue.prototype.$mount
- 获取
el
- 判断
this.$options
里是否有render
函数,如果有直接进入第5步;没有则进入第三步 - 获取
template
(template
可能来自this.$options
,或者getOuterHTML(el)
) - 将
template
通过compileToFunctions
方法编译为渲染函数,得到render
,staticRenderFns
,并将他们挂在this.$options
上 - 返回
mount.call(this, el, hydrating)
,这里mount
就是事先存下的
- 获取
- 在
Vue
上挂载compile
方法Vue.compile = compileToFunctions
- 导出
Vue
export default Vue
通常我们写代码时,会在main.js
里,这样写:
new Vue({
// 这里是我们写的配置,对应的是this.$options
}).$mount('#app')
这里的$mount
就是上面重写后的Vue.prototype.$mount
,所以执行$mount
时实际执行的是mount.call(this, el, hydrating)
,也就是重写前的Vue.prototype.$mount
。重写前的Vue.prototype.$mount
来自src/platforms/web/runtime/index.js
。我们去看一下这个文件里做了哪些事情。
做了哪些事情呢?
- 从
src/core/index
里导入Vue
- 安装平台指定的
utils
到Vue.config
上Vue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement
- 安装平台指定的
directives
、components
extend(Vue.options.directives, platformDirectives) extend(Vue.options.components, platformComponents)
platformDirectives
包括v-model
,v-show
platformComponents
包括transition
、transition-group
- 安装平台指定的
patch
函数Vue.prototype.__patch__ = inBrowser ? patch : noop
- 安装$mount方法到原型
Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && inBrowser ? query(el) : undefined return mountComponent(this, el, hydrating) }
- 导出
Vue
export default Vue
可以看到这个文件里定义了我们执行的$mount
,而返回的是mountComponent(this, el, hydrating)
,这个mountComponent
方法来自src/core/instance/lifecycle
。
mountComponent
函数做了哪些事情呢?
- 设置
vm.$el
:vm.$el = el
- 执行
beforeMount
钩子:callHook(vm, 'beforeMount')
- 实例化一个
watcher
new Watcher(vm, updateComponent, noop, { before () { if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate') } } }, true
- 执行
mounted
钩子if (vm.$vnode == null) { vm._isMounted = true callHook(vm, 'mounted') }
- 返回
return vm
可以看到这里就完成了整个组件的挂载,并且在这里调用了new Watcher
,如果有更新,就执行beforeUpdate
钩子。
这个new Watcher
我们稍后再说。
总结:上面这些是顺着.$mount('#app')
讲的,说明了执行.$mount('#app')
过程中做了什么事情。那么new Vue({// 这里是我们写的配置,对应的是this.$options})
这个实例化Vue的过程做了什么呢?请接着阅读下文
从第一部分入口文件和第二部分分析可知,最终我们在main.js
里使用的Vue
来自src/core/index.js
,继续追踪src/core/index.js
,发现Vue
来自src/core/instance/index.js
。
这个文件,定义了Vue
函数,然后下面执行的一系列函数,就是在Vue的原型上添加属性。
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
这一系列函数就是关键了。
来自src/core/instance/init.js
,它在Vue
原型上设置了_init
方法(只是定义,没有执行),来看一下这个_init
方法:
- 设置
vm
等于this
- 设置
vm.$options
- 设置
vm._renderProxy
- 设置
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')
6. 如果有el,则执行$mount。
### `initLifecycle`
```javascript
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
vm._events = Object.create(null)
vm._hasHookEvent = false
// init parent attached events
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
vm._vnode = null // the root of the child tree
vm._staticTrees = null
vm.$slots
vm.$scopedSlots
vm._c
vm.$createElement
defineReactive(vm, '$attrs',...
defineReactive(vm, '$listeners',...
解析inject,关闭响应式
设置vm._watchers = []
initProps
定义vm._props
校验props
关闭响应式,然后defineReactive(props, key, value,...
proxy(vm,_props
, key)initMethods
:vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
initData
定义vm._data
proxy(vm, '_data', key)
observe(data, true)
initComputed
定义vm._computedWatchers
对每个key new一个watcherinitWatch
createWatcher
设置vm._provided
为vm.$options.provide
来自src/core/instance/state.js
:
- 定义
Vue.prototype.$data
,其get方法是返回this._data
- 定义
Vue.prototype.$props
,其get方法是返回this._props
- 定义
Vue.prototype.$set = set
, 这个set
来自src/core/observer/index
- 定义
Vue.prototype.$delete = del
, 这个del
来自src/core/observer/index
- 定义
Vue.prototype.$watch
来自src/core/instance/event.js
:
- 定义
Vue.prototype.$on
、Vue.prototype.$once
、Vue.prototype.$off
、Vue.prototype.$emit
来自src/core/instance/lifecycle.js
:
- 定义
Vue.prototype._update
、Vue.prototype.$forceUpdate
、Vue.prototype.$destroy
来自src/core/instance/render.js
:
- 执行
installRenderHelpers
,略 - 定义
Vue.prototype.$nextTick
- 定义
Vue.prototype._render
还记得我们上面分析.$mount
执行过程时,留着一个new Watcher
没说呢。Watcher
是在src/core/observer/watcher.js
里定义的。下面就开始分析: