本篇文章自定义一个vue,逐步实现数据的双向绑定,给大家通过实例来一步步搞懂vue双向绑定原理,希望对大家有所帮助!
vue最少需要两个参数:模板和data。【相关推荐:vue.js视频教程】
创建Compiler对象,将数据渲染到模板后,挂载到指定跟节点中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Compiler
1,node2fragment函数将模板元素提取到内存中,方便将数据渲染到模板后,再一次性挂载到页面中
2,模板提取到内存后,使用buildTemplate函数遍历该模板元素
元素节点
文本节点
3,创建CompilerUtil类,用于处理vue指令和{{}},完成数据的渲染
4,到此就完成了首次数据渲染,接下来需要实现数据改变时,自动更新视图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
Observer
1,使用defineRecative函数对data做Object.defineProperty处理,使得data中的每个数据都可以进行get/set监听
2,接下来将考虑如何在监听到data值改变后,更新视图内容呢?使用观察者设计模式,创建Dep和Wather类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
使用观察者设计模式,创建Dep和Wather类
1,使用观察者设计模式的目的是:
解析模板,收集data中某个数据在模板中被使用的dom节点集合,当该数据改变时,更新该dom节点集合就实现了数据更新。
Dep:用于收集某个data属性依赖的dom节点集合,并提供更新方法
Watcher:每个dom节点的包裹对象
2,到这里感觉思路是没问题了,已经是胜券在握了。那Dep和Watcher该怎么使用呢?
为每个属性添加一个dep,用来收集依赖的dom
因为页面首次渲染的时候会读取data数据,这时候会触发该data的getter,所以在此收集dom
具体如何收集呢,在CompilerUtil类解析v-model,{{}}等命令时,会触发getter,我们在触发之前创建Wather,为Watcher添加一个静态属性,指向该dom,然后在getter函数里面获取该静态变量,并添加到依赖中,就完成了一次收集。因为每次触发getter之前都对该静态变量赋值,所以不存在收集错依赖的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
3,到这里就实现了数据绑定时,视图自动更新,本来想代码一步步实现的,但是发现不好处理,就把完整的class贴出来了。
其实就是监听输入框的input、change事件。修改CompilerUtil的model方法。具体代码如下
1 2 3 4 5 6 7 8 9 10 11 12 |
|
vue双向绑定原理
vue接收一个模板和data参数。1,首先将data中的数据进行递归遍历,对每个属性执行Object.defineProperty,定义get和set函数。并为每个属性添加一个dep数组。当get执行时,会为调用的dom节点创建一个watcher存放在该数组中。当set执行时,重新赋值,并调用dep数组的notify方法,通知所有使用了该属性watcher,并更新对应dom的内容。2,将模板加载到内存中,递归模板中的元素,检测到元素有v-开头的命令或者双大括号的指令,就会从data中取对应的值去修改模板内容,这个时候就将该dom元素添加到了该属性的dep数组中。这就实现了数据驱动视图。在处理v-model指令的时候,为该dom添加input事件(或change),输入时就去修改对应的属性的值,实现了页面驱动数据。3,将模板与数据进行绑定后,将模板添加到真实dom树中。
如何将watcher放在dep数组中?
在解析模板的时候,会根据v-指令获取对应data属性值,这个时候就会调用属性的get方法,我们先创建Watcher实例,并在其内部获取该属性值,作为旧值存放在watcher内部,我们在获取该值之前,在Watcher原型对象上添加属性Watcher.target = this;然后取值,将讲Watcher.target = null;这样get在被调用的时候就可以根据Watcher.target获取到watcher实例对象。
methods的原理
创建vue实例的时候,接收methods参数
在解析模板的时候遇到v-on的指令。会对该dom元素添加对应事件的监听,并使用call方法将vue绑定为该方法的this:vm.$methods[value].call(vm, e);
computed的原理
创建vue实例的时候,接收computed参数
初始化vue实例的时候,为computed的key进行Object.defineProperty处理,并添加get属性。
(学习视频分享:web前端)
以上是一文带你深入解析vue双向绑定原理(彻底搞懂它)的详细内容。更多信息请关注PHP中文网其他相关文章!