vue2源码通读计划
2022-11-25
- 我们使用vue是通过new vue()来初始化一个vue实例,那么它到底做了什么呢
new Vue({
el: '#app',
data() {
return {
message: 'hello'
}
}
})
- 首先检测是不是通过new Vue()来初始化根实例,如果直接在index.html中把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)
}
- 然后进入init函数、初始化生命周期、初始化事件、初始化 render、调用 beforeCreate 钩子函数并且触发 beforeCreate 钩子事件、初始化 props、methods、data、computed 与 watch、调用 created 钩子函数并且触发 created 钩子事件
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
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')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
-
在initState(vm)中,有内置的组件实例挂载顺序,props>>>methods>>>data>>>computed>>>watch,如果再加上inject和provide,那就是inject>>>props>>>methods>>>data>>>computed>>>watch>>>provide。
-
vm.options.el),挂载节点
2023-02-17
-
上面的mergeOptions是合并传入的options选项的,对options的props,inject,directives进行了格式化处理,规范化为对象格式。
-
initLifecycle(vm) 挂载了一些属性在vue上
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
-
initEvents(vm),这里面有事件分发,once,emit,全部挂在vue原型对象(vue.prototype)上
-
initRender(vm) 这里有两个重要的函数,初始化_vnode
vm._vnode = null // the root of the child tree
vm._staticTrees = null // v-once cached trees
//将createElement fn绑定到此实例
//以便在其中获得正确的渲染上下文。
//参数顺序:tag, data, children,normalizationType,false
//内部版本由从模板编译的呈现函数使用
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
-
callHook(vm, 'beforeCreate'),这里就是执行beforeCreate()里面的代码,this指向vm
-
callHook(vm, 'created'),这里就是执行created()里面的代码,this指向vm
-
initInjections(vm),初始化挂载inject
-
initProvide(vm),初始化挂载provide
-
还有一个initState(vm)是重点
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
2023-02-28
-
这里有内置的挂载先后顺序,props-methods-data-computed-watch
-
数据响应式原理(这一块太绕了,先放一下,空了再来看看)
-
接下来就是vm.options.el)
-
$mount里面先获取模板template,然后是模板编译compileToFunctions
2023-03-01
- 从$mount调试完了整个代码,记录了一下大概的渲染流程,算是勉强完成了一次简略的源码调试