摘要:使用框架進行開發時,我們在的和中定義屬性和方法,在調用時直接使用或的形式,而不是用或的方式。項目的地址,后續會持續更新,分析的源碼,爭取實現一個精簡版的。中的所有方法都直接在實例重新定義了引用。
使用Vue框架進行開發時,我們在option的data和methods中定義屬性和方法,在調用時直接使用 vm.attr 或 vm.func()的形式,而不是用vm.data.attr或vm.methods.func()的方式。
項目的git地址: https://github.com/xubaodian/... ,后續會持續更新,分析Vue的源碼,爭取實現一個精簡版的Vue。
Vue解析系列文章如下:
Vue源碼解析(二)Vue的雙向綁定講解及實現
我們傳入Vue的options對象一般為以下這種形式,
{ 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初始化時做了下面兩點操作:
1、給data中的屬性做了代理,所有訪問和設置vm[key]時,最終操作的是vm._data[key],而Vue在初始化時,會vm._data其實是options中data的引用。
2、methods中的所有方法都直接在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,可以把代碼直接復制出去試一下
上面代碼就完成了屬性的重新映射和方法的引用重新定義。
看下vue中源碼,,如下,我做了注釋,應該比較好懂:
簡單說明一下,源碼中使用了flow作為js代碼的靜態檢查工具,原理和typescript類似,所以代碼看起來會有些不同,不影響整體閱讀
//初始化,參數是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: " + "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,歡迎大家來討論
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100595.html
摘要:上篇文章,我們講解了的屬性映射和方法的重定義,鏈接地址如下源碼解析一屬性映射和函數引用的重定義這篇文章給大家帶來的是的雙向綁定講解。這就是的雙向綁定。使用定時器定時檢查的值,發生變化就通知訂閱者。這個方法不好,定時器不能實時反應變化。 文章中的代碼時階段,可以下載源碼測試一下。git項目地址:https://github.com/xubaodian/...項目使用webpack構建,下...
摘要:圖在中應用三數據渲染過程數據綁定實現邏輯本節正式分析從到數據渲染到頁面的過程,在中定義了一個的構造函數。一、概述 vue已是目前國內前端web端三分天下之一,也是工作中主要技術棧之一。在日常使用中知其然也好奇著所以然,因此嘗試閱讀vue源碼并進行總結。本文旨在梳理初始化頁面時data中的數據是如何渲染到頁面上的。本文將帶著這個疑問一點點追究vue的思路。總體來說vue模版渲染大致流程如圖1所...
摘要:無論是還是都提倡單向數據流管理狀態,那我們今天要談的雙向綁定是否和單向數據流理念有所違背我覺得不是,從上篇文章語法樹轉函數了解到,雙向綁定,實質是的單向綁定和事件偵聽的語法糖。源碼解析今天涉及到的代碼全在文件夾下。 通過對 Vue2.0 源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬2016.4.11第一次提交開始讀,準備陸續寫: 模版字符串轉AST語法樹 AST語法樹轉rend...
摘要:無論是還是都提倡單向數據流管理狀態,那我們今天要談的雙向綁定是否和單向數據流理念有所違背我覺得不是,從上篇文章語法樹轉函數了解到,雙向綁定,實質是的單向綁定和事件偵聽的語法糖。源碼解析今天涉及到的代碼全在文件夾下。 通過對 Vue2.0 源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬2016.4.11第一次提交開始讀,準備陸續寫: 模版字符串轉AST語法樹 AST語法樹轉rend...
閱讀 955·2021-11-17 09:33
閱讀 415·2019-08-30 11:16
閱讀 2468·2019-08-29 16:05
閱讀 3351·2019-08-29 15:28
閱讀 1393·2019-08-29 11:29
閱讀 1947·2019-08-26 13:51
閱讀 3385·2019-08-26 11:55
閱讀 1203·2019-08-26 11:31