文章目录
Vue 实例新建流程
Vue源码目录结构说明
- src
- compiler
解析模板生成AST和render函数
- core
- components
目前只有keep-alive组件
- global-api
向Vue对象注入全局方法:Vue.use(),Vue.extend()等
- instance
向Vue实例对象注入方法:this.$emit(),this.$forceUpdate()等
- observer
实现data与Watch对象的依赖收集与更新
- util
工具类
- vdom
Vdom有关方法
- components
- entries
Vue 不同类型源码入口
- platforms
- server
- sfc
- shared
- compiler
这次主要我们用到的目录只有:global-api,instance,observer三个。主要介绍Vue实例化时初始数据和计算属性时的源码具体内容。
initData初始化数据
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
...
// a flag to avoid this being observed
vm._isVue = true
...
// 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')
...
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
当我们在 new Vue()
时首先调用得就是 _init()
方法。它主要初始化生命周期,事件,渲染相关数据,调用 beforeCreate
钩子,初始化provide/injections相关数据,初始化data相关数据,调用 created
钩子。最后调用 $mount()
方法挂载到对应的DOM元素上。
今天我们主要看一下初始化数据相关的部分。也就是 _init()
方法里面的 initState()
函数。
// 初始化数据
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // 1. 初始化参数
if (opts.methods) initMethods(vm, opts.methods) // 2、初始化方法
if (opts.data) {
// 3、初始化数据
initData(vm)
} else {
observe(vm._data = {
}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed) // 4、初始化计算属性
if (opts.watch) initWatch(vm, opts.watch) // 5、初始化监听属性
}
这个 initState()
代码很短,要做的事情也很清楚,就是初始化了 props
参数,methods
方法,data
数据,computed
计算属性,watch
监听属性。这次我们主要关注 initData()
方法和 initComputed()
方法。了解Vue依赖收集与双向绑定的完整流程。
所以,首先我们先看一下 initData()
方法。
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {
}
...
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
let i = keys.length
while (i--) {
if (props && hasOwn(props, keys[i])) {
// 判断props和data里的属性是否有重复
process.env.NODE_ENV !== 'production' && warn(
`The data property "${
keys[i]}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(keys[i])) {
// 判断data属性不以$或_开头
proxy(vm, `_data`, keys[i]) // 将this.XXX代理到this._data.XXX
}
}
// observe data
observe(data, true /* asRootData */) //
}
function getData (data: Function, vm: Component): any {
try {
return data.call(vm)
} catch (e) {
handleError(e, vm, `data()`)
return {
}
}
}
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter ()