一步一步实现Vue数据绑定

这阶段代码git地址如下:github.com/Ace7523/vue… 因为我学习的习惯就是一步一步的从简到繁增加功能,大家看时候对应的看提交版本的,这部分代码对应的是第一次commit。

webpack新建一个vue项目,相信这个大家都知道了,我就无须赘述。我会把每个阶段的代码git地址提供出来。需要强调一点就是改了这里:

作用是 import 引用依赖时候优先在指定目录下寻找,找不到的话再去node_modules中寻找。

es6的export 和 export default 的区别:

答: export default 一个文件只能有一个, 引用时候 import XXX from …
export 一个文件中可以有多个,引用时候用 import { XXX } from …
2 vue 对象数据劫持

这阶段代码git地址如下:

先来提两个问题,1,vue是数据驱动的这句话怎么理解? 2,如何实现数据驱动?

答: 数据驱动我个人理解是,数据优先,只要数据变了,页面就跟着变化,而不用再去想着这个数据应该和页面中哪个dom位置相关,再手动去改。
答:对数据进行劫持,比如一个var a = 10 ,当读取a的值时候,知道在读取,当给a赋值操作时候,又知道在赋值。这就是对数据的劫持。

因为完整代码地址我都有提供,所以就截图来看关键的点

1 创建自己的vue实例

2 利用 Object.defineProperty 对上图中data方法返回的对象进行数据劫持,先来看一下劫持之前的数据表征:

写数据劫持的方法

这段代码也很容易理解,就是利用了一下Object.defineProperty,把对象的每个属性的get和set重新写了一遍。这么做之后,我们再打印data数据看一下:

可以看出,name age testObj,分别都拥有了get和set属性,那么这个是干嘛用的呢?再来修改一点代码,如下

运行一下下面的语句就知道了(解释一下_data, data的值挂在的vm实例的_data上)
console.log( vm._data.name )
复制代码
打印结果如下:

这就表明了,在想要读取name属性的值,之前,先打印的语句,也就是,实现数据劫持。同理,设置也是一样。

3 完善上述的劫持,因为对象内部还包含对象的这种情况代码中没有实现

这次打印结果如下,testObj对象内部的对象,也拥有的set 和 get

4 把vm._data 代理到 vm 上,一边以后取值直接 vm.name 拿到的就是vm._data.name

就是通过这样一个方法实现的,大家自己看下就好

5 小结

这阶段代码不难,其实分开一步步来看的话,每一阶段都不难,难得是,后面说到依赖收集的时候,不记得这段的逻辑了,所以一定要好好消化。
3 vue 数组数据劫持

这阶段代码git地址如下:github.com/Ace7523/vue… , 具体对应的是第二次commit。

为什么要对数组做一次特殊的劫持呢?因为就目前的对象劫持成程度,无法监听到数组push了一个元素,举个例子:如下图测试一下

看看打印结果:

从这个结果中可以看出,只触发了一次get,没有set。 get是在vm.arr , 这个过程触发的,因为读取arr的值就会触发get。 打印出的4是数组的push方法返回的值。(ps:数组push返回变化后的数组长度)
那这种情况肯定不是我们想要的,比如页面根据数组中元素个数来渲染按钮,但是数组中元素是接口动态返回的,虽然数组已经是劫持过的,但是新增元素时候,我们不知道这个数组新增了,那就不行了。

修改如下 在数据劫持的Observer 构造函数中 针对数组重新封装劫持方法

arrayMethods observerArray这两个方法写在了另外一个array.js文件中。稍微解释一下,就是说在进行数据劫持的时候,发现是数组的话,就先改变数组的原型指向,然后再对这个数组进行劫持。下面用代码来解释
其实更通俗一点的说,就是要重写数组的push等方法,要求既要保留原来的push方法全部,又要有所增加。(限定只是被劫持的数组元素的原型方法重新写,而不是直接改变Array.prototype) 那要怎么实现?就像下面那样实现

配合上边图的修改,首先,在劫持对象为数组的时候,数组的原型指向已经修改了
data.proto= arrayMethods
复制代码
而这个arrayMethods是继承了原生数组的原型,所以,该有的方法都会有,此时我们只需把需要改变的方法修改即可。图中的7个方法会改变原数组,所以,要对这7个方法重新加工一下。就上图来解释,其实上图对于那7个方法,可以还一点都没有修改,但是留了位置,可以在那图中17行位置添加一些操作。 其实这也就是所谓的切片编程,即保留原有功能的基础之上,添加新的功能。
再啰嗦几句,因为怕这里会有朋友还不理解。 从劫持那里说起,当判断到要劫持的数据类型是数组的时候,就先改变了这个被劫持的数组的原型,arr为例,当劫持后,再次执行arr.push的时候,这个push方法不是在原生数组的原型链中获取的,而是在我们写的这个array.js中获取的,因为此时是这样执行的
arr.push() 就是 arrayMethods.push()
arrayMethods.push = function(…args){
let r = oldArrayProtoMethods[push].apply(this,args);
//
// 这里就是要增加的功能部分
//
return r
}
复制代码
估计这么写一下就明朗多了吧,apply不明白的话,建议补一补js基础。 然后我们接着完善这个方法,注意一下apply的第二个参数是数组。看代码

也许一些js基础不牢固的小伙伴又要问了,…args是什么,apply中的args又是什么。
答:… 用在函数参数中,就是剩余运算符,用在不知道参数具体有几个的时候,如
fun(…args){
console.log(args)
}
fun(1,2,3,4)

// 打印结果是 [1,2,3,4]
复制代码
所以还是以push为例子,push(4),那么此时 inserted = [4], 就表示对原来的那个[1,2,3]的数组arr,改变arr,让arr变成了[1,2,3,4] , 那么我们肯定要对这个新增加的元素 进行 劫持,这才能够是数组一直都是响应式的。也就是增加如下代码

因为inserted是数组,所以对他进行遍历,要对新增加的每一项都要重新进行劫持。
好了,来增加一些代码来测试一下本部分增加的功能点吧。

运行结果如下

小结 这一部分是增加了对于初始化vue实例中data参数中的数组进行劫持。 其实也不难,但是却很难用文章的形式把这部分讲出来,不过我的这部分代码地址也贴出来了,大家down下来自己看看就会清楚其中的原理。

这里提一下 vue 的数据劫持是有缺点的 1 不能对数组的索引进行监控 2 arr.length = 0 这种情况也没有监控到。 反正有个印象就行。

发布了60 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/a59612/article/details/104399351