-
Notifications
You must be signed in to change notification settings - Fork 384
vue早期源码学习系列之四:如何实现动态数据绑定 #87
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
cool ! |
@yutingzhao1991 只是一些粗浅的理解,还希望跟大家一起多交流交流。 |
这里Binding class是为了实现Vue代码里的linker function的功能吗? |
@F-12 不是。
|
看了下代码,求科普 ...require('') 是什么用法,那三个点? |
@freeozyl80 。。。es6的语法糖,先去看一下ES6的相关知识吧 |
我正纳闷怎么更新指定数据了 看了这个文章 帮助很多 |
牛逼 |
老铁 这一系列的博客很赞啊!👍 |
你好,有些困惑。
|
你好,按照你的思路我自己实现了一下。有一个不明白的地方:
如果按照上面的代码,更新文本节点的nodeValue,你的列子没有问题。如果模板是这样的如下: |
@cllee1214 是用 正则表达式解析出来的 |
@ZYSzys 那是不是指令里面存的表达式是一个整个字符串?能不能说详细一点。望指教。 |
前言
在上一篇中, #86 ,我们已经掌握了如何监听数据的变化,以及使用观察者模式和事件传递响应变化事件。那么,今天我们来看看,基于watch库,如何实现动态数据绑定?
问题具象
我们可以把问题具象化为下面的例子
问题是:如何做到当user.name或者user.age发生改变的时候,html上的DOM元素也发生相应的改变呢?
笨拙的做法
先来看看我一开始采取的办法。

思路:因为数据所有属性的变化都会冒泡到顶层,所以我只需要在数据顶层注册一个事件。当任意一个属性发生改变的时候,我都重新遍历DOM模板,把{{user.name}}这些转换成实际值,在内存中拼接成fragment,最后把生成的新fragment整块地替换掉原先的DOM结构。
这里比较简单,我就不多说了,可以直接去看这个版本的源码。
实现效果如下图所示。
但是这样的做法非常粗暴,存在不少问题。
基于上述两个缺陷,这种做法肯定不能忍。那么,我们来看看怎么解决它们。
指令Directive
想要做到:只更新数据变动相关的DOM,必须有个这样的对象,将DOM节点和对应的数据一一映射起来,这里引入Directive(指令)的概念,它的构造函数和原型方法如下所示。
关键实现思路:

在遍历DOM模板的过程中,当遍历到文本节点:"{{name}}"的时候,会先将其中的表达式"name"匹配出来,然后新建一个空的textNode,也就是上面的this.el,插入到这个文本节点的前面,最后remove掉这个文本节点。这样,就实现了用一个程序生成的textNode代替原来的textNode,从而实现每个textNode都跟它的表达式一一对应起来。
可以直接去看这个版本的源码
具体实现的效果如下图所示。
从图中我们可以看到,bue的实例app中多出了_directive属性,它是一个数组,存放的是在遍历DOM模板的时候解析出来的若干条指令。当数据发生变化的时候,会找到对应的指令,然后执行对应指令上面的update方法。
这样我们就实现了只更新数据变动对应的那一个部分DOM。
然而,这样子还是存在其他问题。
Binding、Watcher
为了解决上述的两个问题,我们引入Binding和Watcher这两个“类”(Binding是为了解决键值索引,Watcher是为了解决$watch)。那么,Binding、Watcher、Directive这三者之间是什么关系呢?我们来看看下面这张图。(这是本文最重要的图)

从图中我们可以看到。有一个_rootBind对象,它的属性就是按照DOM模板中用到的数据层层深入排列下去的。(因为我们在模板中只用到了user.name和user.age,所以这里只有这两个属性)。而且,在每个属性上有一个_subs数组。这个数组其实就是subscibe订阅的意思,里面存放的是一系列Watcher。这些Watcher代表着当此属性数据发生改变的时候,就会循环遍历_subs里面的Watcher,执行里面的update方法。

那么,Watcher又跟我们上面实现的Directive什么关系呢?是包含的关系。也就是说,Watcher是一个观察容器,它既可以装载Directive,这时候cb是更新DOM的函数,从而实现数据变动的时候更新DOM;也可以装载$watch,这时候cb是自定义的回调函数,从而实现数据变动的时候执行自定义回调函数。
这就是vue实现动态数据绑定的三大核心概念。
实现效果如下:
不多说了,直接上代码。
参考资料
后话
此次学习vue,我checkout到的是vue的这个版本。然而,这个版本相比于我在学习写watch库的时候,代码量剧增,从原先的七八百行增加到差不多五千行,看起来非常地费劲。但是没有办法,因为这个commit是实现动态数据绑定功能最早的commit了,我只能从这儿开始看起。特别是Binding、Watcher和Directive这几个核心概念,一开始简直要把人绕晕了,压根不知道为什么要有这些东西。经过多日的思考和不断的debug,我才慢慢地想通。
经过这一次,我发现了学习别人的源码,一个很大的困难不在于明白作者写的是什么(因为往往会有注释),而在于明白作者为什么非这样写不可,在于明白作者设计的思路,然而注释通常不会提到这些。这就只能靠大胆的想象和不断的尝试了。
另外,上面通过Binding、Watcher、Directive构建起来的动态数据绑定体系还有一个重大的缺陷,我们把它留到下一篇来专门阐述。
The text was updated successfully, but these errors were encountered: