Vue源码学习2-props&methods&data
前言
---props是父子组件之间通信的方式之一
--data会在Vue中被处理成响应式数据
--以下vm都代指vue的实例
--以下所有代码来自Vue 2.6.11 版本
initState
在new Vue 的整个流程中,props和data数据的处理都发生在 _init 的 initState 方法中(newVue整体流程可以看这里)
进入这个方法 (src/core/instance/state.js)可以清楚的看到处理流程
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) }
根Vue实例中的data可以直接返回一个对象 ,组件就必须返回一个匿名函数(原因就不说了,熟练Vue的应该都知道)
//根Vue实例 new Vue({ data:{} }); //组件实例 component = { data(){ return {} } }
initProps
props的初始化赋值发送在 组件化的流程中,这里就不说了,后面梳理组件化流程的时候再说
平时开发使用 props 的方式有两种:
1:数组形式 props:['name','person']
2:对象形式
props:{ type:{ type:String, default(){}, ......更多选项看vue的文档就行了 } }
下面这段代码是 initProps 方法的核心处理流程,忽略掉环境判断以及一些warn,总的来说就是遍历一个对象,然后 validateProp(校验/默认值/取值 等操作) ,最后使用 defineReactive 把数据处理成响应式数据(具体的响应式处理流程后面再说)
Vue通过proxy方法把props中的所有数据都挂载到 vm 的 _props 这个属性上,然后,将vm.xxx 的引用代理到 vm._props.xxx上,当我们访问 this.name 的时候,就相当于访问 this._props.name
proxy在 data methods computed 初始化的过程中都会见到
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( ....,vm ) } defineReactive(props, key, value, () => { if (!isRoot && !isUpdatingChildComponent) { warn( ......,vm ) } }) } else { defineReactive(props, key, value) } if (!(key in vm)) { proxy(vm, `_props`, key) } }
proxy
const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop }
proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) }
initData
initData的流程和props略有不同,先是检查一下key是否和props和methods有重复,然后就是和initProps一样的proxy操作,只不过这次是 vm._data
最后,observe就是处理响应式数据的流程,这里还是先不说,后面再梳理
let data = vm.$options.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)) { proxy(vm, `_data`, key) } } // observe data observe(data, true /* asRootData */)
initMethods
initMethods方法做的就相当简单了,简单的遍历操作,先是校验,最后就是bind(这里的bind方法来自于 share/util.js),就等价于Function.prototype.bind方法,将 method 的作用域绑定为 vm,并且给 vm[key]赋值 vm[key] = method。然后我们就能愉快的在代码中调用this.xxxx方法了(这里的bind、作用域两个知识点不懂就先去自行了解)
function initMethods (vm: Component, methods: Object) { const props = vm.$options.props for (const key in methods) { ......忽略一段代码 1:检查是否为 function 2:检查和props是否 key 冲突 ....... vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm) } }
总结
以上就简单的梳理了一下代码,细节方面就放在后面的响应式原理来讲。下篇文章不打算讲 initComputed 和 initWatch,必须要先讲清楚Vue的响应式原理才能更容易学习和理解这两个东西。
发表回复