本篇文章介绍的是
Vue.js
如何取到data
和methods
里的属性?
准备工作
- 克隆源码到本地
git clone https://github.com/vuejs/vue.git
下载完毕后,用vscode
打开,目光移动到package.json
的scripts
属性,我们看到有dev
和build
,dev
会启动一个开发环境的服务,也就是说,我们在源码里做的改动,都会及时生效。build
就是打包。和我们平时开发Vue.js
项目是一个道理。
我们首先安装一下Vue.js
项目的依赖(使用pnpm
),然后运行npm run dev
。这样的好处就是我们能随时看到代码改动后的效果。
- 接下来我们在
examples
目录下创建一个html
文件,引入打包后的vue.js
<!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"> <h1 @click="changeMsg">hello {{msg}}</h1> </div> <script src="../dist/vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { msg: 'world' }, methods: { changeMsg() { this.msg = 'me' } } }) </script>
</body>
</html>
- 安装一个
serve
全局包启动, 在根目录运行一下serve
,就能看到页面展示出来了。
调试源码
我们这里使用谷歌浏览器调试,F12
找到sources
面板如下图所示的位置打上断点,接着刷新页面,就进入了调试模式。
然后,我们就通过step into
按钮进入new Vue
的函数内部。 接着进入_init
的内部,找到initState(vm)
,也就是当前文件代码的4714行,这个函数的内部就是我们要研究的部分。
进入initState
内部,我们看到
if (opts.methods)initMethods(vm, opts.methods);if (opts.data) {initData(vm);}
initMethods
function initMethods(vm, methods) {var props = vm.$options.props;for (var key in methods) {{if (typeof methods[key] !== 'function') {warn$2("Method \"".concat(key, "\" has type \"").concat(typeof methods[key], "\" in the component definition. ") +"Did you reference the function correctly?", vm);}if (props && hasOwn(props, key)) {warn$2("Method \"".concat(key, "\" has already been defined as a prop."), vm);}if (key in vm && isReserved(key)) {warn$2("Method \"".concat(key, "\" conflicts with an existing Vue instance method. ") +"Avoid defining component methods that start with _ or $.");}}vm[key] = typeof methods[key] !== 'function' ? noop : bind$1(methods[key], vm);}}
- 首先判断组件内部是否声明了函数
- 其次判断是否和
props
、保留键名的名字冲突了 - 最后是处理逻辑,如果对应值的类型是函数将传入的
vm
对应的属性赋值,否则为noop
,赋值的函数这里做了一个强绑(使用的bind
,this
指向vm
)。这个bind$
来自原生的bind
方法
var bind$1 = Function.prototype.bind ? nativeBind : polyfillBind;
initData
调试完了initMehtods
后,就开始initData
,我们使用step out
按钮就跳出了当前函数,接着进入initData
内部。
function initData(vm) {var data = vm.$options.data;data = vm._data = isFunction(data) ? getData(data, vm) : data || {};if (<img src="https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm);}// proxy data on instancevar keys = Object.keys(data);var props = vm.$options.props;var methods = vm.$options.methods;var i = keys.length;while (i--) {var key = keys[i];{if (methods && hasOwn(methods, key)) {warn$2("Method \"".concat(key, "\" has already been defined as a data property."), vm);}}if (props && hasOwn(props, key)) {warn$2("The data property \"".concat(key, "\" is already declared as a prop. ") +"Use prop default value instead.", vm);}else if (!isReserved(key)) {proxy(vm, "_data", key);}}// observe datavar ob = observe(data);ob && ob.vmCount++;}" style="margin: auto" />
逻辑和initMethods
类似,和props
、methods
做了比对,最后通过proxy
将data
,绑定到vm
上
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);}
最终我们知道data
的值是通过Object.defineProperty
,实现绑定的。
结束语
我们要研究一个源码,首先要准备源码、serve、和调试工具(谷歌浏览器),然后进入代码的内部,才能看的清楚明白,我们就此知道了Vue.js
的this
如何取到data
和methods
的属性。
最后
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取