Vue2?this直接獲取data和methods原理解析
學(xué)習(xí)目標(biāo)
本篇文章將通過閱讀 vue 的源碼,來回答 [為什么 Vue2 this 能夠直接獲取到 data 和 methods?]
倉庫地址:Github
- 如何學(xué)習(xí)調(diào)試 vue2 源碼
- data 中的數(shù)據(jù)為什么可以用 this 直接獲取到
- methods 中的方法為什么可以用 this 直接獲取到
- 學(xué)習(xí)源碼中優(yōu)秀代碼和思想,投入到自己的項(xiàng)目中
如何學(xué)習(xí)調(diào)試 vue2 源碼
通過去改源碼的方式來學(xué)習(xí)代碼,就是看到一段代碼,你可能不是太懂它具體的作用是什么,那就嘗試去改其中幾行代碼,猜測(cè)他們可能會(huì)造成那些影響,然后執(zhí)行代碼去驗(yàn)證你的猜想。
使用 Github Workspace 克隆一份代碼,定位到源碼位置,如下圖:

安裝完依賴后執(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,打開文件:

Github Workspace 會(huì)生成一個(gè)在線預(yù)覽的地址,所有的操作都是在瀏覽器中完成的,非常便捷。
使用瀏覽器的調(diào)試工具在 new Vue() 這行打上斷點(diǎn),開始調(diào)試:

分析源碼
調(diào)試
我們?cè)跀帱c(diǎn)調(diào)試的時(shí)候要帶著一下兩個(gè)問題,看看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ù)看起來像是用來初始化組件實(shí)例的方法的。它接收兩個(gè)參數(shù):vm 和 methods,其中 vm 是組件實(shí)例,methods 是包含組件方法的對(duì)象。
首先,這個(gè)函數(shù)檢查組件是否定義了 props 屬性。如果定義了,它會(huì)警告用戶,如果方法名和已有的 prop 名稱相同,給出警告。
然后檢查函數(shù)名是否包含 $ 與 _ ,如果方法名包含這兩個(gè)符號(hào),給出警告。
最后使用bind函數(shù)將this指向?yàn)関m,因此我們才得以使用this訪問到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)訪問數(shù)據(jù)對(duì)象,使用 Object.defineProperty 設(shè)置對(duì)象的get與set函數(shù),為下一步響應(yīng)式做鋪墊。
- 使用觀察函數(shù)觀察數(shù)據(jù)。在數(shù)據(jù)發(fā)生改變時(shí)響應(yīng)到頁面,或者在頁面發(fā)生變化時(shí),響應(yīng)到數(shù)據(jù)。
總結(jié)
通過本次課程的學(xué)習(xí),加深了在瀏覽器中調(diào)試代碼的方法,并且通過閱讀源碼對(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數(shù)據(jù)雙向綁定原理解析(get & set)
這篇文章主要為大家詳細(xì)解析了vue.js數(shù)據(jù)雙向綁定原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
VueJS實(shí)現(xiàn)用戶管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了VueJS實(shí)現(xiàn)用戶管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Vue渲染器如何對(duì)節(jié)點(diǎn)進(jìn)行掛載和更新
這篇文章主要介紹了Vue 的渲染器是如何對(duì)節(jié)點(diǎn)進(jìn)行掛載和更新的,文中通過代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-05-05

