使用Vue框架進行開發時,咱們在option的data和methods中定義屬性和方法,在調用時直接使用 vm.attr 或 vm.func()的形式,而不是用vm.data.attr或vm.methods.func()的方式。
項目的git地址: https://github.com/xubaodian/... ,後續會持續更新,分析Vue的源碼,爭取實現一個精簡版的Vue。
Vue解析系列文章以下:javascript
Vue源碼解析(二)Vue的雙向綁定講解及實現
咱們傳入Vue的options對象通常爲如下這種形式,
html
{ data: { name: 'xxx' }, mounted() { //調用方法,沒有使用this.methods.getInfo(); this.getInfo(); }, methods: { getInfo() { //獲取屬性,沒有使用this.data.name this.name = 'xxxx2314'; //操做等等.... } }, computed: { getName() { return this.name; } }, watch: { 'name'(val, oldVal) { //這是操做 } } }
在vue實例中,咱們不管data仍是method,都直接調用,這是由於一下vue初始化時作了下面兩點操做:
一、給data中的屬性作了代理,全部訪問和設置vm[key]時,最終操做的是vm._data[key],而Vue在初始化時,會vm._data實際上是options中data的引用。
二、methods中的全部方法都直接在vue實例從新定義了引用。
看下個人實現代碼,是對Vue源碼的精簡,以下:vue
//vue構造函數 class Vue { constructor(options) { //$options存儲構造選項 this.$options = options || {}; //data保存構造設置中的data,暫時忽略data爲函數的狀況 let data = options.data; this._data = data; //初始化 this._init(); } _init() { //映射key mapKeys(this); //在vue實例上從新定義方法的引用 initMethods(this, this.$options.methods) } } //從新定義方法的引用,注意修改調用函數時的上下文環境,這裏用bind,固然也能夠用apply和call function initMethods (vm, methods) { for (const key in methods) { vm[key] = typeof methods[key] !== 'function' ? noop : methods[key].bind(vm); } } //從新定義data的get和set function mapKeys(vm) { let data = vm._data; if (null !== data && typeof data === 'object') { const keys = Object.keys(data); let i = keys.length; while (i-- >= 0) { //全部屬性的操做就從新定向到了_data上 proxy(vm, `_data`, keys[i]); } } } //使用defineProperty從新定義get和set function proxy (target, sourceKey, key) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) } const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } //空函數,佔位用 function noop () {} //使用 let options = { data: { name: 'xxx', age: 18 }, methods: { sayName() { console.log(this.name); } } } let vm = new Vue(options); vm.sayName();//控制檯打印了xxx,能夠把代碼直接複製出去試一下
上面代碼就完成了屬性的從新映射和方法的引用從新定義。
java
看下vue中源碼,,以下,我作了註釋,應該比較好懂:
簡單說明一下,源碼中使用了flow做爲js代碼的靜態檢查工具,原理和typescript相似,因此代碼看起來會有些不一樣,不影響總體閱讀git
//初始化,參數是vue實例 function initData (vm: Component) { //獲取options中的 let data = vm.$options.data //設置vm._data,判斷data是obj仍是函數 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)) { //代理訪問,這就是爲什麼操做vm[key]被定位到vm._data[key]的緣由 proxy(vm, `_data`, key) } } const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } //代理函數 export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } //利用defineProperty設置對象的get和set,操做屬性時,target[key]會映射到target[sourceKey][key] Object.defineProperty(target, key, sharedPropertyDefinition) } //方法映射 function initMethods (vm: Component, methods: Object) { const props = vm.$options.props 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 $.` ) } } //關鍵在這,從新定義了引用 vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm) } }
有疑問能夠給我留言,或發郵件至472784995@qq.com,歡迎你們來討論github