学习目标
本篇文章将通过阅读 vue 的源码,来回答 [为什么 Vue2 this 能够直接获取到 data 和 methods?]
仓库地址:Github
- 如何学习调试 vue2 源码
- data 中的数据为什么可以用 this 直接获取到
- methods 中的方法为什么可以用 this 直接获取到
- 学习源码中优秀代码和思想,投入到自己的项目中
如何学习调试 vue2 源码
通过去改源码的方式来学习代码,就是看到一段代码,你可能不是太懂它具体的作用是什么,那就尝试去改其中几行代码,猜测他们可能会造成那些影响,然后执行代码去验证你的猜想。
使用 Github Workspace 克隆一份代码,定位到源码位置,如下图:
安装完依赖后执行命令:
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
方法中发现了我们想要寻找的关键字。
找到源码中 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 实例作为参数调用它以获取数据。
- 检查数据是否为普通对象。如果不是,则使用空对象作为数据,并给出警告。
- 循环访问数据对象,使用 Object.defineProperty 设置对象的get与set函数,为下一步响应式做铺垫。
- 使用观察函数观察数据。在数据发生改变时响应到页面,或者在页面发生变化时,响应到数据。
总结
通过本次课程的学习,加深了在浏览器中调试代码的方法,并且通过阅读源码对vue2的响应式原理有了进一步的了解。
以上就是Vue2 this直接获取data和methods原理解析的详细内容,更多关于Vue2 this获取data methods的资料请关注编程网其它相关文章!