-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vue早期源码学习系列之四:如何实现动态数据绑定 #87
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: