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

安裝完依賴后執(zhí)行命令:
pnpm run dev
編譯器會實時的將代碼打包到 dist 目錄下,如圖:

我們引入打包后的代碼,就可以實時的調試源碼了,在example文件夾下新建一個html文件,并放入如下內容:
<!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 會生成一個在線預覽的地址,所有的操作都是在瀏覽器中完成的,非常便捷。
使用瀏覽器的調試工具在 new Vue() 這行打上斷點,開始調試:

分析源碼
調試
我們在斷點調試的時候要帶著一下兩個問題,看看vue實例化的步驟是什么:
- data 中的數據為什么可以用 this 直接獲取到
- methods 中的方法為什么可以用 this 直接獲取到
也就是關注data 與 methods 兩個關鍵詞,果不其然,在 mergeOptions 方法中發(fā)現了我們想要尋找的關鍵字。

找到源碼中 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
這一步操作是將所有選項合并,為下一步 initState 做準備,在 initState 處打上斷點, F8 跳到這個斷點處,F10 進入到這個函數內部。
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)
}
}
從代碼上看這個函數的功能是進行一些初始化操作
- initMethods 初始化 methods
- initData 初始化 data
- initComputed 初始化 computed
在 initMethods 與 initData 處分別打斷點進入。
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)
}
}
這個函數看起來像是用來初始化組件實例的方法的。它接收兩個參數:vm 和 methods,其中 vm 是組件實例,methods 是包含組件方法的對象。
首先,這個函數檢查組件是否定義了 props 屬性。如果定義了,它會警告用戶,如果方法名和已有的 prop 名稱相同,給出警告。
然后檢查函數名是否包含 $ 與 _ ,如果方法名包含這兩個符號,給出警告。
最后使用bind函數將this指向為vm,因此我們才得以使用this訪問到vm實例中的所有選項。
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 函數初始化 Vue.js 組件的數據:
- 如果 data 屬性是一個函數,則使用 Vue 實例作為參數調用它以獲取數據。
- 檢查數據是否為普通對象。如果不是,則使用空對象作為數據,并給出警告。
- 循環(huán)訪問數據對象,使用 Object.defineProperty 設置對象的get與set函數,為下一步響應式做鋪墊。
- 使用觀察函數觀察數據。在數據發(fā)生改變時響應到頁面,或者在頁面發(fā)生變化時,響應到數據。
總結
通過本次課程的學習,加深了在瀏覽器中調試代碼的方法,并且通過閱讀源碼對vue2的響應式原理有了進一步的了解。
以上就是Vue2 this直接獲取data和methods原理解析的詳細內容,更多關于Vue2 this獲取data methods的資料請關注腳本之家其它相關文章!

