Vue2?this直接獲取data和methods原理解析
學(xué)習(xí)目標(biāo)
本篇文章將通過(guò)閱讀 vue 的源碼,來(lái)回答 [為什么 Vue2 this 能夠直接獲取到 data 和 methods?]
倉(cāng)庫(kù)地址:Github
- 如何學(xué)習(xí)調(diào)試 vue2 源碼
- data 中的數(shù)據(jù)為什么可以用 this 直接獲取到
- methods 中的方法為什么可以用 this 直接獲取到
- 學(xué)習(xí)源碼中優(yōu)秀代碼和思想,投入到自己的項(xiàng)目中
如何學(xué)習(xí)調(diào)試 vue2 源碼
通過(guò)去改源碼的方式來(lái)學(xué)習(xí)代碼,就是看到一段代碼,你可能不是太懂它具體的作用是什么,那就嘗試去改其中幾行代碼,猜測(cè)他們可能會(huì)造成那些影響,然后執(zhí)行代碼去驗(yàn)證你的猜想。
使用 Github Workspace 克隆一份代碼,定位到源碼位置,如下圖:
安裝完依賴(lài)后執(zhí)行命令:
pnpm run dev
編譯器會(huì)實(shí)時(shí)的將代碼打包到 dist
目錄下,如圖:
我們引入打包后的代碼,就可以實(shí)時(shí)的調(diào)試源碼了,在example文件夾下新建一個(gè)html文件,并放入如下內(nèi)容:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="app"> <h2 @click="changeMsg">hello {{msg}}</h2> </div> <script src="../dist/vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { msg: 'world' }, methods: { changeMsg() { this.msg = 'codeniu' } } }) </script> </body> </html>
使用 vscode 拓展 Live Server,打開(kāi)文件:
Github Workspace 會(huì)生成一個(gè)在線預(yù)覽的地址,所有的操作都是在瀏覽器中完成的,非常便捷。
使用瀏覽器的調(diào)試工具在 new Vue()
這行打上斷點(diǎn),開(kāi)始調(diào)試:
分析源碼
調(diào)試
我們?cè)跀帱c(diǎn)調(diào)試的時(shí)候要帶著一下兩個(gè)問(wèn)題,看看vue實(shí)例化的步驟是什么:
- data 中的數(shù)據(jù)為什么可以用 this 直接獲取到
- methods 中的方法為什么可以用 this 直接獲取到
也就是關(guān)注data 與 methods 兩個(gè)關(guān)鍵詞,果不其然,在 mergeOptions
方法中發(fā)現(xiàn)了我們想要尋找的關(guān)鍵字。
找到源碼中 mergeOptions
的位置:
export function initMixin(Vue: typeof Component) { Vue.prototype._init = function (options?: Record<string, any>) { ... // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options as any) } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor as any), options || {}, vm ) } ... } }
initState
這一步操作是將所有選項(xiàng)合并,為下一步 initState
做準(zhǔn)備,在 initState
處打上斷點(diǎn), F8 跳到這個(gè)斷點(diǎn)處,F(xiàn)10 進(jìn)入到這個(gè)函數(shù)內(nèi)部。
export function initState(vm: Component) { const opts = vm.$options if (opts.props) initProps(vm, opts.props) // Composition API initSetup(vm) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { const ob = observe((vm._data = {})) ob && ob.vmCount++ } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
從代碼上看這個(gè)函數(shù)的功能是進(jìn)行一些初始化操作
- initMethods 初始化 methods
- initData 初始化 data
- initComputed 初始化 computed
在 initMethods 與 initData 處分別打斷點(diǎn)進(jìn)入。
initMethods
function initMethods(vm: Component, methods: Object) { const props = vm.$options.props for (const key in methods) { if (__DEV__) { 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) } }
這個(gè)函數(shù)看起來(lái)像是用來(lái)初始化組件實(shí)例的方法的。它接收兩個(gè)參數(shù):vm 和 methods,其中 vm 是組件實(shí)例,methods 是包含組件方法的對(duì)象。
首先,這個(gè)函數(shù)檢查組件是否定義了 props 屬性。如果定義了,它會(huì)警告用戶(hù),如果方法名和已有的 prop 名稱(chēng)相同,給出警告。
然后檢查函數(shù)名是否包含 $ 與 _ ,如果方法名包含這兩個(gè)符號(hào),給出警告。
最后使用bind函數(shù)將this指向?yàn)関m,因此我們才得以使用this訪問(wèn)到vm實(shí)例中的所有選項(xiàng)。
initData
function initData(vm: Component) { let data: any = vm.$options.data data = vm._data = isFunction(data) ? getData(data, vm) : data || {} if (!isPlainObject(data)) { data = {} __DEV__ && warn( 'data functions should return an object:\n' + 'https://v2.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 (__DEV__) { if (methods && hasOwn(methods, key)) { warn(`Method "${key}" has already been defined as a data property.`, vm) } } if (props && hasOwn(props, key)) { __DEV__ && 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 const ob = observe(data) ob && ob.vmCount++ }
InitData 函數(shù)初始化 Vue.js 組件的數(shù)據(jù):
- 如果 data 屬性是一個(gè)函數(shù),則使用 Vue 實(shí)例作為參數(shù)調(diào)用它以獲取數(shù)據(jù)。
- 檢查數(shù)據(jù)是否為普通對(duì)象。如果不是,則使用空對(duì)象作為數(shù)據(jù),并給出警告。
- 循環(huán)訪問(wèn)數(shù)據(jù)對(duì)象,使用 Object.defineProperty 設(shè)置對(duì)象的get與set函數(shù),為下一步響應(yīng)式做鋪墊。
- 使用觀察函數(shù)觀察數(shù)據(jù)。在數(shù)據(jù)發(fā)生改變時(shí)響應(yīng)到頁(yè)面,或者在頁(yè)面發(fā)生變化時(shí),響應(yīng)到數(shù)據(jù)。
總結(jié)
通過(guò)本次課程的學(xué)習(xí),加深了在瀏覽器中調(diào)試代碼的方法,并且通過(guò)閱讀源碼對(duì)vue2的響應(yīng)式原理有了進(jìn)一步的了解。
以上就是Vue2 this直接獲取data和methods原理解析的詳細(xì)內(nèi)容,更多關(guān)于Vue2 this獲取data methods的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue cli構(gòu)建的項(xiàng)目中請(qǐng)求代理與項(xiàng)目打包問(wèn)題
這篇文章主要介紹了vue cli構(gòu)建的項(xiàng)目中請(qǐng)求代理與項(xiàng)目打包問(wèn)題,需要的朋友可以參考下2018-02-02vue項(xiàng)目總結(jié)之文件夾結(jié)構(gòu)配置詳解
這篇文章主要給大家總結(jié)介紹了關(guān)于vue項(xiàng)目之文件夾結(jié)構(gòu)配置的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12Vue實(shí)現(xiàn)內(nèi)部組件輪播切換效果的示例代碼
這篇文章主要介紹了Vue實(shí)現(xiàn)內(nèi)部組件輪播切換效果的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04vue實(shí)現(xiàn)word,pdf文件的導(dǎo)出功能
這篇文章給大家介紹了vue實(shí)現(xiàn)word或pdf文檔導(dǎo)出的功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-07-07Vue3警告:Failed to resolve component:XXX的詳細(xì)解決辦法
最近在一個(gè)vue3項(xiàng)目中遇到了報(bào)錯(cuò),整理了些解決辦法,這篇文章主要給大家介紹了關(guān)于Vue3警告:Failed to resolve component:XXX的詳細(xì)解決辦法,文中介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05vue自定義組件如何通過(guò)v-model指令控制組件的隱藏、顯示
這篇文章主要介紹了vue自定義組件如何通過(guò)v-model指令控制組件的隱藏、顯示,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05vue中electron框架自定義外部配置文件的配置與讀取辦法
使用Electron開(kāi)發(fā)本地跨平臺(tái)的本地程序時(shí),有時(shí)需要添加一些程序的配置文件,下面這篇文章主要給大家介紹了關(guān)于vue中electron框架自定義外部配置文件的配置與讀取的相關(guān)資料,需要的朋友可以參考下2023-12-12Vue?3?中使用?vue-router?進(jìn)行導(dǎo)航與監(jiān)聽(tīng)路由變化的操作
在Vue3中,通過(guò)useRouter和useRoute可以方便地實(shí)現(xiàn)頁(yè)面導(dǎo)航和路由變化監(jiān)聽(tīng),useRouter允許進(jìn)行頁(yè)面跳轉(zhuǎn),而useRoute結(jié)合watch可以根據(jù)路由變化更新組件狀態(tài),這些功能為Vue3應(yīng)用增加了靈活性和響應(yīng)性,使得路由管理更加高效2024-09-09