1.初始化响应式属性
初始化响应式的函数为initState(vm),我们进入函数中看一下
export function initState (vm: Component) {
vm._watchers = []
// 把vm实例上的属性赋值给一个变量,方便后续对属性进行操作
const opts = vm.$options
// 初始化props
// 数据代理到实例上,支持 this.propsKey 进行访问
if (opts.props) initProps(vm, opts.props)
// 初始化methods
// 1.对 propsKkey 和 methodsKey 进行判重处理, propsKey 的优先级大于 methodsKey
// 2.数据代理到实例上,支持 this.methodsKey 进行访问
if (opts.methods) initMethods(vm, opts.methods)
// 如果实例上有data的话,进行data属性的初始化
// 否则把data打入observe中,实例上的_data相对于data,因为_默认是私有属性
if (opts.data) {
// 初始化data
// 1.对 data 进行判重, props 和 methods 的属性不能和 data 重复
// 2.对 data 进行代理, 支持 this.data 的方式访问
// 3.对 data 进行响应式处理
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
// 初始化computed
if (opts.computed) initComputed(vm, opts.computed)
// 初始化watch
// nativeWatch为做火狐浏览器的兼容
// Firefox has a "watch" function on Object.prototype...
// export const nativeWatch = ({}).watch
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
initState(vm)中的initProps -----初始化props
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
if (!isRoot) {
toggleObserving(false)
}
// 对 props 进行循环遍历
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
/* istanbul ignore else */
// 在开发生产环境进行一些提示
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
// 响应式处理
defineReactive(props, key, value)
}
// vm._props = vm.options.props
// 代理,支持 this.propsKey 方式去访问 props 属性
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
让我们来看看数据代理做了什么
//数据代理
export function proxy (target: Object, sourceKey: string, key: string) {
// 添加get方法
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
// 添加set方法
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
// 通过 Object.defineProperty 进行代理
Object.defineProperty(target, key, sharedPropertyDefinition)
}
initState(vm)中的initMethods -----初始化mthods
function initMethods (vm: Component, methods: Object) {
// 取到propsKey
const props = vm.$options.props
// 遍历 methods 的 key
// 做了判重处理
// 如果 props 上的key和 methods 上的key一样
// propsKey 的优先级大于 methodsKey
for (const key in methods) {
if (process.env.NODE_ENV !== 'production') {
if (typeof methods[key] !== 'function') {
warn(
`Method "${key}" has type "${typeof methods[key]}" in the component definition. ` +
`Did you reference the function correctly?`,
vm
)
}
if (props && hasOwn(props, key)) {
warn(
`Method "${key}" has already been defined as a prop.`,
vm
)
}
if ((key in vm) && isReserved(key)) {
warn(
`Method "${key}" conflicts with an existing Vue instance method. ` +
`Avoid defining component methods that start with _ or $.`
)
}
}
// 把 methods 中的所有方法赋值到 vue 实例上,支持 this.methodsKey 的方法进行访问
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
}
}
initState(vm)中的initData -----初始化data
function initData (vm: Component) {
// 从配置项取 data 属性
let data = vm.$options.data
// 判断如果 data 是函数就表示是子组件的data
// 不是函数就是根上的 data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
// 判重处理
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
// 代理,支持 this.dataKey 进行访问
proxy(vm, `_data`, key)
}
}
// observe data
// 响应式处理
observe(data, true /* asRootData */)
}
initState(vm)中的initComputed -----初始化computed
function initComputed (vm: Component, computed: Object) {
// 创建 一个干净的 watchers 对象和一个 _computedWatchers 的属性
const watchers = vm._computedWatchers = Object.create(null)
// 判断是不是SSR(服务端渲染)
const isSSR = isServerRendering()
// 遍历 computed 对象
// computed = { key: computed[key]}
// key 为函数或者对象的名字
for (const key in computed) {
// 不管函数还是对象 都赋值给 userDef
const userDef = computed[key]
// 判断 getter 是否是一个函数
// 函数 执行 userDef 赋值给 getter
// 对象 执行 userDef.get 赋值给 getter
const getter = typeof userDef === 'function' ? userDef : userDef.get
// 环境检测
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
`Getter is missing for computed property "${key}".`,
vm
)
}
// 不是服务端渲染
// 构造函数生成实例对象
if (!isSSR) {
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
// 判断 key 是不是在 vm 或者 vm的原型上
// key 不在原型上, vm上没有相同的属性
if (!(key in vm)) {
// 定义computed, 参数 Vm 实例, 属性名字, 属性值
defineComputed(vm, key, userDef)
}
// key 在原型上证明data,props,methods提前定义了
// 不能与data,props,methods的属性相同
else if (process.env.NODE_ENV !== 'production') {
if (key in vm.$data) {
warn(`The computed property "${key}" is already defined in data.`, vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn(`The computed property "${key}" is already defined as a prop.`, vm)
} else if (vm.$options.methods && key in vm.$options.methods) {
warn(`The computed property "${key}" is already defined as a method.`, vm)
}
}
}
}
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
// 判断是不是SSR(服务端渲染)
// shouldCache --> true
// 不是SSR ---> true
const shouldCache = !isServerRendering()
// 如果是函数
// const sharedPropertyDefinition = {
// enumerable: true,
// configurable: true,
// get: noop,
// set: noop
// }
// export function noop (a?: any, b?: any, c?: any) {}
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
}
// 如果是对象
else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop
sharedPropertyDefinition.set = userDef.set || noop
}
// 环境处理
if (process.env.NODE_ENV !== 'production' &&
sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
`Computed property "${key}" was assigned to but it has no setter.`,
this
)
}
}
// 响应式处理,代理到 Vm 实例上,支持通过 this.ComputedKey 进行访问
Object.defineProperty(target, key, sharedPropertyDefinition)
}
// 因为 sharedPropertyDefinition.get 是要一个函数 得返回函数
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
// 如果在实例上存在 dirty 证明第一次进入或者update了
// 面试题:computed 和 methods 的区别
// computed 有缓存就是在这里 dirty 控制缓存
// update () {
// /* istanbul ignore else */
// if (this.lazy) {
// this.dirty = true
// } else if (this.sync) {
// this.run()
// } else {
// queueWatcher(this)
// }
// }
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
initState(vm)中的initWatch -----初始化watch
// 初始化watch
// watch:{ xxx(){} , xxxx:{ deep:true,handler(nv,ov){} } }
function initWatch (vm: Component, watch: Object) {
// 遍历 watch, key 名 watch[key] 值
for (const key in watch) {
// 保留值
const handler = watch[key]
// 判断是不是数组 没遇过 看下面官网
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
// 一般都是函数和对象
createWatcher(vm, key, handler)
}
}
}
function createWatcher (
vm: Component,
expOrFn: string | Function,
handler: any,
options?: Object
) {
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options)
}