一:什么是双向数据绑定?
1.1 Vue中MVVM模型
- 模型(Model)表示应用程序的数据和业务逻辑。这可以是从后端API获取的数据,或者在前端应用程序内部定义的数据。
- 视图(View)是用户界面的可见部分,通常以HTML模板的形式存在。它负责将数据呈现给用户,并处理用户的输入事件。
- 视图模型(ViewModel)是模型和视图之间的中间层,它负责管理视图所需的数据,并处理视图中发生的事件。视图模型通过双向数据绑定将模型的状态与视图保持同步。
MVVM即是“Model-View-ViewModel”,它是一种设计模式,用于实现用户界面的分离和交互。
主要职责
View中视图变化,通过ViewModel中的
监听器
反馈给model进行数据的更新Model中数据的变化,通过ViewModel中的
解析器
反馈给View进行视图的更新
1.2 双向数据绑定原理
vue.js是采用
数据劫持
结合发布者-订阅者模式
的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
二:实现双向数据绑定
进行数据的准备,我们目的是为了实现双向数据绑定
- 模板解析,姓名年龄渲染出来的内容是将括号内容替换成我们的数据
- 数据绑定,文本框的内容和上方渲染的数据是一致的,通过修改文本框上方渲染的内容同步修改
1 |
|
数据初始化
1 | //定义vue类 |
我们创建的vm实例已经传给vue类,为了模拟vue中$data,也在构造函数利用this.$data来存储我们创建的vm实例中的data数据
只是此时的数据都还不是响应式的
2.1 数据劫持
1 | //数据劫持 - 监听实例中的数据 |
- Vue.js是通过Object.defineProperty来实现对数据的监视
- data_instance是一个对象,通过Object.keys来实现对对象以数组形式放回
- 将数组中的每一项通过Object.defineProperty进行数据监视,设置getter和setter,当访问数据和修改数据时调用
- 还要通过递归去将每一项的子属性的值都进行数据监听
2.2 模板解析
对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数
1 | //创建vue类 |
1 | //HTML模板解析 - {{}}替换dom |
- 开辟一个内存空间,创建fragment文档碎片,不属于dom,属于内存区域,当所有数据更新完成时再渲染页面,避免过多操作dom
- 将vm.$el中的dom元素 ,通过appendChild插入到fragment文档碎片中,原先dom中的元素会被移除,存放在fragment文档碎片之中
- 替换文档碎片中 括号中的内容,首先得遍历fragment中的node节点也要通过递归遍历, 通过正则表达式来匹配 内容
1 | //递归遍历 |
要将name 和 more.age 替换成数据,通过reduce方法获取数据
reduce迭代累加器 遍历arr数组 total[current] 不断地迭代的链式获取最终的值 ,reduce两个参数 , 第一个参数是个回调函数,第二参数vm.$data是初始值,total的初始值,无法通过vm.$data[more.age]来获取数据
1 | const value = arr.reduce( |
- vm.$data[more.age]
- 将括号内内容替换成value
1 | node.nodeValue = temp.replace(pattern, value); |
- 最终将文档碎片 fragment渲染到el中
1 | vm.$el.appendChild(fragment); |
2.3 订阅者-发布者模式
1 | //依赖 --收集和通知订阅者 |
- 数组用于收集订阅者
- 添加订阅者的方法
- 当数据修改时需要通知订阅者,触发自己的update更新函数来更新视图
1 | //订阅者 |
将watcher类实例添加到Dep数组中来实现数据视图的绑定
1 | //因为想要将watcher实例添加到依赖的数组中 |
创建Dependency.temp用于临时存储创建的watcher实例,触发getter
在observer类中触发getter时,将临时存储的watcher实例添加到Dependency的存储订阅者的数组之中
1 | get() { |
- 同时为了避免多次重复,添加watcher实例,在添加该实例过后,赋空值
2.4 v-model数据绑定
视图与数据的绑定
在fragment_compile()函数中
1 | //找v-model属性的元素 更改其nodeValue |
在文档碎片fragment中遍历node,通过node.attributes方法来找到属性值为v-model的node节点
遍历的节点 item.nodeValue,是name, more.age
- 通过reduce方法来获取到vm.$data上对应属性的属性值
- 将node.value 修改为属性值,此时将文本框中的内容和属性值相绑定
- 然后需要通过,文本框修改数据同时修改上方的视图,那就需要用到addEvetListener方法添加input事件
- 然后通过文本框视图来修改数据
1 | final[arr1[arr1.length - 1]] = e.target.value |
三:完整代码
html
1 |
|
vue.js
1 | //创建vue类 |