- 前言
大家好,我是若川。为了能帮助到更多对源码感兴趣、想学会看源码、提升自己前端技术能力的同学。我倾力组织了每周大家一起学习200行左右的源码共读活动,感兴趣的可以点此扫码加我微信
ruochuan02参与。
之前写的《学习源码整体架构系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex420余篇源码文章。其中最新的三篇是:
50行代码串行Promise,koa洋葱模型原来是这么实现?
Vue 3.2 发布了,那尤雨溪是怎么发布 Vue.js 的?
写相对很难的源码,耗费了自己的时间和精力,也没收获多少阅读点赞,其实是一件挺受打击的事情。从阅读量和读者受益方面来看,不能促进作者持续输出文章。
所以转变思路,写一些相对通俗易懂的文章。其实源码也不是想象的那么难,至少有很多看得懂。歌德曾说:读一本好书,就是在和高尚的人谈话。
同理可得:读源码,也算是和作者的一种学习交流的方式。
本文源于一次源码共读群里群友的提问,请问@若川,“为什么 data 中的数据可以用 this 直接获取到啊”,当时我翻阅源码做出了解答。想着如果下次有人再次问到,我还需要回答一次。当时打算有空写篇文章告诉读者自己探究原理,于是就有了这篇文章。
阅读本文,你将学到:
1 | js复制代码1. 如何学习调试 vue2 源码 |
本文不难,用过 Vue 的都看得懂,希望大家动手调试和学会看源码。
看源码可以大胆猜测,最后小心求证。
- 示例:this 能够直接获取到 data 和 methods
众所周知,这样是可以输出我是若川的。好奇的人就会思考为啥 this 就能直接访问到呢。
1 | js复制代码const vm = new Vue({ |
那么为什么 this.xxx 能获取到data里的数据,能获取到 methods 方法。
我们自己构造写的函数,如何做到类似Vue的效果呢。
1 | js复制代码function Person(options){ |
如果是你,你会怎么去实现呢。带着问题,我们来调试 Vue2源码学习。
- 准备环境调试源码一探究竟
可以在本地新建一个文件夹examples,新建文件index.html文件。
在<body></body>中加上如下js。
1 | js复制代码<script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script> |
再全局安装npm i -g http-server启动服务。
1 | js复制代码npm i -g http-server |
这样就能在http://localhost:8080/打开刚写的index.html页面了。
对于调试还不是很熟悉的读者,可以看这篇文章《前端容易忽略的 debugger 调试技巧》,截图标注的很详细。
调试:在
F12打开调试,source面板,在例子中const vm = new Vue({打上断点。
刷新页面后按F11进入函数,这时断点就走进了 Vue 构造函数。
3.1 Vue 构造函数
1 | js复制代码function Vue (options) { |
值得一提的是:if (!(this instanceof Vue)){} 判断是不是用了 new 关键词调用构造函数。
一般而言,我们平时应该不会考虑写这个。
当然看源码库也可以自己函数内部调用 new 。但 vue 一般一个项目只需要 new Vue() 一次,所以没必要。
而 jQuery 源码的就是内部 new ,对于使用者来说就是无new构造。
1 | js复制代码jQuery = function( selector, context ) { |
因为使用 jQuery 经常要调用。
其实 jQuery 也是可以 new 的。和不用 new 是一个效果。
如果不明白 new 操作符的用处,可以看我之前的文章。面试官问:能否模拟实现JS的new操作符
调试:继续在
this._init(options);处打上断点,按F11进入函数。
3.2 _init 初始化函数
进入 _init 函数后,这个函数比较长,做了挺多事情,我们猜测跟data和methods相关的实现在initState(vm)函数里。
1 | js复制代码// 代码有删减 |
调试:接着我们在
initState(vm)函数这里打算断点,按F8可以直接跳转到这个断点,然后按F11接着进入initState函数。
3.3 initState 初始化状态
从函数名来看,这个函数主要实现功能是:
1 | bash复制代码初始化 props |
1 | js复制代码function initState (vm) { |
我们重点来看初始化
methods,之后再看初始化data。
调试:在
initMethods这句打上断点,同时在initData(vm)处打上断点,看完initMethods函数后,可以直接按F8回到initData(vm)函数。
继续按F11,先进入initMethods函数。
3.4 initMethods 初始化方法
1 | js复制代码function initMethods (vm, methods) { |
initMethods函数,主要有一些判断。
1 | js复制代码判断 methods 中的每一项是不是函数,如果不是警告。 |
除去这些判断,我们可以看出initMethods函数其实就是遍历传入的methods对象,并且使用bind绑定函数的this指向为vm,也就是new Vue的实例对象。
这就是为什么我们可以通过this直接访问到methods里面的函数的原因。
我们可以把鼠标移上 bind 变量,按alt键,可以看到函数定义的地方,这里是218行,点击跳转到这里看 bind 的实现。
3.4.1 bind 返回一个函数,修改 this 指向
1 | js复制代码function polyfillBind (fn, ctx) { |
简单来说就是兼容了老版本不支持 原生的bind函数。同时兼容写法,对参数多少做出了判断,使用call和apply实现,据说是因为性能问题。
如果对于call、apply、bind的用法和实现不熟悉,可以查看我在面试官问系列中写的面试官问:能否模拟实现JS的call和apply方法
面试官问:能否模拟实现JS的bind方法
调试:看完了
initMethods函数,按F8回到上文提到的initData(vm)函数断点处。
3.5 initData 初始化 data
initData 函数也是一些判断。主要做了如下事情:
1 | bash复制代码先给 _data 赋值,以备后用。 |
1 | js复制代码function initData (vm) { |
3.5.1 getData 获取数据
是函数时调用函数,执行获取到对象。
1 | js复制代码function getData (data, vm) { |
3.5.2 proxy 代理
其实就是用 Object.defineProperty 定义对象
这里用处是:this.xxx 则是访问的 this._data.xxx。
1 | js复制代码/** |
3.5.3 Object.defineProperty 定义对象属性
Object.defineProperty 算是一个非常重要的API。还有一个定义多个属性的API:Object.defineProperties(obj, props) (ES5)
Object.defineProperty 涉及到比较重要的知识点,面试也常考。
1 | bash复制代码value——当试图获取属性时所返回的值。 |
3.6 文中出现的一些函数,最后统一解释下
3.6.1 hasOwn 是否是对象本身拥有的属性
调试模式下,按alt键,把鼠标移到方法名上,可以看到函数定义的地方。点击可以跳转。
1 | js复制代码/** |
3.6.2 isReserved 是否是内部私有保留的字符串$ 和 _ 开头
1 | js复制代码/** |
- 最后用60余行代码实现简化版
1 | js复制代码function noop (a, b, c) {} |
- 总结
本文涉及到的基础知识主要有如下:
1 | js复制代码构造函数 |
本文源于解答源码共读群友的疑惑,通过详细的描述了如何调试 Vue 源码,来探寻答案。
解答文章开头提问:
通过this直接访问到methods里面的函数的原因是:因为methods里的方法通过 bind 指定了this为 new Vue的实例(vm)。
通过 this 直接访问到 data 里面的数据的原因是:data里的属性最终会存储到new Vue的实例(vm)上的 _data对象中,访问 this.xxx,是访问Object.defineProperty代理后的 this._data.xxx。
Vue的这种设计,好处在于便于获取。也有不方便的地方,就是props、methods 和 data三者容易产生冲突。
文章整体难度不大,但非常建议读者朋友们自己动手调试下。调试后,你可能会发现:原来 Vue 源码,也没有想象中的那么难,也能看懂一部分。
启发:我们工作使用常用的技术和框架或库时,保持好奇心,多思考内部原理。能够做到知其然,知其所以然。就能远超很多人。
你可能会思考,为什么模板语法中,可以省略this关键词写法呢,内部模板编译时其实是用了with。有余力的读者可以探究这一原理。
最后欢迎加我微信 ruochuan12 交流,参与 源码共读 活动,大家一起学习源码,共同进步。
关于 && 交流群
最近组织了源码共读活动,感兴趣的可以加我微信 ruochuan12 参与,长期交流学习。
作者:常以若川为名混迹于江湖。欢迎加我微信ruochuan12。前端路上 | 所知甚少,唯善学。
关注公众号若川视野,每周一起学源码,学会看源码,进阶高级前端。
segmentfault若川视野专栏,开通了若川视野专栏,欢迎关注~
掘金专栏,欢迎关注~
知乎若川视野专栏,开通了若川视野专栏,欢迎关注~
github blog,求个star^_^~
本文转载自: 掘金