网站首页 > 技术文章 正文
我们都知道,Vue 是一套用于构建用户界面的渐进式框架,是目前前端领域主流框架之一,作为前端框架,它有两大核心:
1.数据双向绑定:当数据发生改变,视图可以自动更新,可以不用关心dom操作,而专心数据操作;
2.可组合的视图组件:把视图按照功能切分成若干基本单元,组件可以一级一级组合整个应用形成倒置组件树,可维护,可重用,可测试。
Vue从之前的1版本到现在基本所有公司都用到的2版本经历了一次重大变革,到2020年10月5日,Vue3的源码正式发布。3版本的发布其中比较变化大的一个点就是Vue的数据双向绑定的原理发生了变化,那么我们知道,数据双向绑定的原理在前端的面试中基本上是必问的,可见其重要性。那么接下来我们着重讨论一下Vue2和Vue3版本的数据双向绑定原理的区别及其各自的优缺点。
由于本篇篇幅较长,咱们本次先讨论Vue2的数据双向绑定原理,Vue3的数据双向绑定原理咱们在下篇再做介绍。
一、什么是MVVM模式
在讨论Vue的数据双向绑定原理之前,我们还得知道咱们平时工作中用Vue开发的开发模式是什么,那就是MVVM模式。
大多数的开发模式,可能通过最多的是MVC开发模式,当然还有更多的MVP模式,发布订阅者模式等等,那么Vue为什么要采用MVVM的开发模式,其实主要还是因为MVVM模式可以实现数据的双向绑定,它是如何实现的?
MVVM可以分为,模型层,视图层,视图模型层,模型层主要干的事情就是负责与业务数据相关的操作,就像我们data函数中返回的数据一样,主要负责数据方面的工作,view层顾名思义就是主要负责视图相对应的操作,神秘的vm层也就是视图模型层主要是干什么的?视图模型层就类似一座桥梁,链接模型(数据层)和视图层,它的工作是双向的一边监听数据一边观测试图,无论是数据层发生变化还是视图层发生变化,他都会同步给对方,让视图层和数据层保持一致。实现数据的双向绑定。
通过下面这张图可以看到他们三者之间的一个工作流程:
二、Vue2数据双向绑定原理的实现
Vue2采用数据劫持并结合发布者-订阅者模式的方式,通过ES6的object.defineProperty()方法去劫持各个属性的setter/getter方法,在数据发生变化的时候,发布消息给订阅者,触发相应的监听回调。
具体步骤如下:
1、需要observe(观察者)的数据对象进行遍历,包括子属性对象的属性,都加上setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到数据的变化。
2、compile(解析)解析模版指令,将模版中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。
3、watcher(订阅者)是observer和compile之间通信的桥梁,主要做的事情是:
Ⅰ、在实例化时往属性订阅器(dep)里添加自己;
Ⅱ、自身必须有一个update()方法;
Ⅲ、待属性变动dep.notice()通知时,能够调用自身的update()方法,并触发compile中绑定的回调。
4、MVVM作为数据绑定入口,整合observer,compile和watcher来监听自己的model数据变化,通过compile来解析编译模版,最终利用watcher搭起observer和compile之间的通信桥梁,达到数据变化->更新视图:视图交互变化->数据model变更的双向绑定效果。
结合上面所说可以看下面这张图,能有个直观的感受:
三、源码剖析
那么Vue2的源码是如何具体实现它的数据双向绑定原理的呢?我们对源码进行剖析,发现它的代码实现步骤如下:
1. observer实现对vue各个属性进行监听
function defineReactive( obj, key, val ) {
// 每个属性建立个依赖收集对象,get中收集依赖,set中触发依赖,调用更新函数
var dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
// 收集依赖 Dep.target标志
Dep.target && dep.addSub(Dep.target)
return val
},
set: function(newVal){
if(newVal === val) return
// 触发依赖
dep.notify()
val = newVal
}
})
}
2. dep实现
function Dep(){
this.subs = []
}
Dep.prototype = {
constructor: Dep,
addSub: function(sub){
this.subs.push(sub)
},
notify: function(){
this.subs.forEach(function(sub){
sub.update() // 调用的Watcher的update方法
})
}
}
3. compiler实现对各个指令模板的解析器
通过compiler实现对vue各个指令模板的解析器,生成抽象语法树,编译成Virtual Dom,渲染视图。
// 编译器
function compiler(node, vm){
var reg = /\{\{(.*)\}\}/;
// 节点类型为元素
if(node.nodeType ===1){
var attr = node.attributes;
// 解析属性
for(var i=0; i< attr.length;i++){
if(attr[i].nodeName == 'v-model'){
var _value = attr[i].nodeValue
node.addEventListener('input', function(e){
//给相应的data属性赋值,触发修改属性的setter
vm[_value] = e.target.value
})
node.value = vm[_value] // 将data的值赋值给node
node.removeAttribute('v-model')
}
}
new Watcher(vm,node,_value,'input')
}
// 节点类型为text
if(node.nodeType ===3){
if(reg.test(node.nodeValue)){
var name = RegExp.$1;
name = name.trim()
new Watcher(vm,node,name,'input')
}
}
}
4.Watcher连接observer和compiler
通过他们之间的连接,接受每个属性变动的通知,绑定更新函数,更新视图。
function Watcher(vm,node,name, nodeType){
Dep.target = this; // this为watcher实例
this.name = name
this.node = node
this.vm = vm
this.nodeType = nodeType
this.update() // 绑定更新函数
Dep.target = null //绑定完后注销 标志
}
Watcher.prototype = {
get: function(){
this.value = this.vm[this.name] //触发observer中的getter监听
},
update: function(){
this.get()
if(this.nodeType == 'text'){
this.node.nodeValue = this.value
}
if(this.nodeType == 'input') {
this.node.value = this.value
}
}
}
总结
以上就是关于Vue2的数据双向绑定原理的刨析,如果你仔细阅读了,相信一定会让你有所收获,接下来咱们会在下篇中讨论Vue3的数据双向绑定原理,这里咱们先做个思考:是不是新版的数据双向绑定原理就优于旧版的?可以给大家提前透露一下,并不是这样的,两者各有优缺点。
猜你喜欢
- 2024-10-12 学习Vue的数据绑定语法,实现动态的用户界面(四)
- 2024-10-12 Vue前端框架:绑定单个复选框。 vue复选框默认选中
- 2024-10-12 vue2响应式原理 vue2.0响应式原理
- 2024-10-12 vue数据绑定语法 vue样式绑定语法
- 2024-10-12 【Vue】第5章 Vue数据绑定和计算属性详解
- 2024-10-12 Vue数据及事件绑定、filter过滤器
- 2024-10-12 Vue开发:组件绑定自定义事件及其应用
- 2024-10-12 vue.js实战之表单绑定-单选按钮 vue表格单选
- 2024-10-12 跟神一起学前端-初识vue、数据绑定
- 2024-10-12 vue.js 动态绑定class的几种方式 vue动态绑定类
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)