Vue双向绑定:原理篇(详细)
文章目录
- 前言
- 什么是响应式
- 数据劫持
- 发布者-订阅者模式
- 模式简介
- 发布者 Observer
- 订阅器 dep
- 订阅者 Watcher
- 整体流程
- 初始化data
- data变为响应式数据
- 解析模板
- 收集依赖
- 数据变化—视图更新
- 视图更新—数据变化
- 更新的粒度
- 更细的粒度更新
- 中等粒度更新
- Vue3的Proxy数据劫持
- 理解Proxy
- 特点
- 兼容性问题
- 总结
前言
提起Vue的双向绑定或数据响应式,很多人都知道是数据劫持和发布者-订阅者模式,这里具体分析一下这两部分具体是怎么实现。
(最近看了相关资料,对原来不足的地方进行修改完善,还增加了更新的粒度和Vue3的Proxy内容)
什么是响应式
如果经常使用Vue
或React
等框架开发,对响应式更新这个词并不陌生,简单来说就是视图会自动更新。
- 原生JS实现就需要先找到DOM,再修改DOM
比如
const clockDom = document.getElementById
clockDom.innerText = '修改文本内容'
- 现在使用响应式框架,修改数据的时候,我们不需要关注DOM。在Vue中,this.xxx就可以实现页面数据更新。我们从数据劫持开始了解。
数据劫持
- 数据劫持其实就是数据响应式基础,当获取数据或者修改数据的时候,能够被我们知道,然后触发响应操作,在Vue2中是通过Object.defineProperty()实现的。
比如,下面这个对象
let person = {name:'tom',age:15
}
- 我们可以
person.name
获取到tom
,但是我想在获取到tom
的时候,还要进行其他操作,就要使用Object.defineProperty()
Object.defineProperty(person,'name',{get(){console.log('name属性被读取了...');},set(newVal){console.log('name属性被修改了...');}
})
当
访问name属性
的时候,会调用get方法
,而修改name属性
的时候,会调用set方法
,可以去执行相应的操作。但是,这个时候访问被拦截了,我们获取不到name的属性值,所以需要在get方法里面return一个值,上面代码修改如下:
let person = {}
let val = 'tom'
Object.defineProperty(person,'name',{get(){console.log('name属性被读取了...');return val;},set(newVal){console.log('name属性被修改了...');val = newVal;}
})
- 因为属性值可以由对象直接提供,不会单独声明,所以传入对象的时候,可以传入键和值。所以将val变量和
defineProperty
方法提取到一个函数中,就形成defineReactive
函数
function defineReactive(obj, key, val) { // 这里相当于let val= val(传入的参数)Object.defineProperty(obj, key, {get() {console.log(`${key}属性被读取了...`);return val;},set(newVal) {console.log(`${key}属性被修改了...`);val = newVal;}})
}
至此,就完成了简单的数据劫持
发布者-订阅者模式
模式简介
发布者和订阅者是互相不知道对方的存在的,发布者只需要把消息发送到订阅器里面,订阅者只管接受自己需要订阅的内容。
主要有三个概念:发布者、订阅器、订阅者
发布者 Observer
Observe
r就是进行数据劫持,内部包含了defineReactive()
函数。每次数据读或写时,我们能感知到数据被读取了或数据被改写了。要使数据变得“可观测”。
订阅器 dep
- 收集依赖,内部维护了一个数组,用来记录该数据的所有Watcher,一旦数据发生变化就会发布通知所有Watcher
订阅者 Watcher
- 作为依赖,会被dep收集。其实是个中介角色,数据发生变化时通知它,然后它去通知其他地方。
了解到这里,可能还会有些疑问:
依赖是什么?怎么产生?这就需要知道解析器 Compile,它会对模板进行解产生Watcher。
下面介绍一下双向绑定的整体流程,有一个更直观的了解。
整体流程
初始化data
- 首先要知道每个组件都是一个Vue实例,也就是new Vue(),然后将data等数据传入进去。
new Vue({el: '#app', // 挂载点data: { // 状态},methods: { // 方法},
});
- 在Cass Vue中,可以在constructor中获取到data
constructor(options) {this.$el = options.el //获取挂载点this.$data = options.data}
然后对
data.xx
的一级属性进行劫持,方法是直接遍历data的key,使用Object.defineProperty方法
对每个属性都进行劫持,返回对应的值data[key]
。这也是为什么可以在Vue实例中直接通过
this.xxx
访问到data
中的数据的原因。使用过React就知道,React使用
setdata()
方法才能修改数据。
data变为响应式数据
data对象实例化一个Observer实例,绑定在data的
ob
属性上面,防止重复绑定Observer实例中创建一个dep实例,用于收集依赖
Observer内部有
Object.definedpropty
,对属性进行劫持,修改成getter
、setter
方法,用于依赖收集和派发更新如果data中包含数组,Vue重写了数组的7种原生方法,实现响应式
如果data为多级对象,需要深度监听,递归data对象进行监听,data值更新的时候也需要进行判断深度监听
解析模板
对节点和Vue指令进行编译
编译过程中如果遇到
{{}}
或v-bind
、v-model
等指令使用到的时候,实例化Wacther(模板解析也另一大块内容,后面有机会再详细分析)
收集依赖
- 编译过程中当data中的某个属性被读时(模板中使用了data数据),get 方法会被调用, 该属性的dep实例会收集该属性的Watcher,放置到dep维护的数组中。
至此,修改data中的数据,就能够影响模板中的数据
数据变化—视图更新
- 修改data数据,Observer实例就会触发set方法,然后调用Dep 的
notify
方法,notify
方法中又去调用所有依赖该属性的 Watcher 的updater
方法,进行视图更新
视图更新—数据变化
上面的流程主要是数据变化更新视图,要实现双向绑定,还需要进行事件监听,也就是注册监听用户对视图的修改事件,触发修改data数据的方法
这也是
v-model
的实现双向绑定的原理
更新的粒度
更细的粒度更新
假如有一个状态绑定着好多依赖,每个依赖表示一个具体的DOM节点,那么当这个状态发生变化时,向这个状态的所有依赖发送通知,让它们进行DOM更新操作。
但是这样的代价是:粒度越细,每个状态所绑定的依赖就越多,依赖追踪的开销就越大。
中等粒度更新
- 从
Vue2.0
开始,它引入了虚拟DOM
,将粒度调整为中等粒度
,即一个状态所绑定的依赖不再是具体的DOM节点,而是一个组件。 - 这样状态变化后,会通知到组件,组件内部再使用虚拟DOM进行对比后进行重新渲染。
- 这可以大大降低依赖数量,从而降低依赖追踪所消耗的内存。
Vue3的Proxy数据劫持
- 从前面的内容可以看出
Vue2
的defineProperty
方法和重写数组方法的形式存在很多不足,针对这些问题,Vue3使用Proxy
来代替defineProperty
进行数据劫持。
理解Proxy
Proxy
是ES6新增的类,这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。Proxy
可以理解为:在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
特点
能监听对象和数组
新增的属性也能够被拦截,多层对象需要递归处理,对每一层对象进行代理
除了能拦截
访问
和修改
操作之外,还能拦截in操作符
和delete
操作符
// 不需要关心是哪个属性
new Proxy(data, {get(key) { },set(key, value) { },
})// Vue2
Object.defineProperty(data, 'count', {get() {},set() {},
})
与defineProperty
的相同点就是对操作进行拦截,不同点是需要关心是哪个属性。
兼容性问题
- 但是
Proxy
是不能通过babel
转译的,因为在ES5中完全没有一种语法可以模拟出Proxy
的特性。因此Vue3.x
版本没有办法兼任一些低版本浏览器。
总结
双向绑定:就是数据变化更新视图,视图变化更新数据
数据响应式:通过对数据的访问和修改进行劫持,然后进行相应的操作,是实现双向绑定的基础。
Vue2数据劫持的缺点
监听对象:
Object.defineProperty
- 只能监听对象,这个对象不是指引用类型,所以不包括数组
- 不能够对新增属性进行监听
- 不能监听数组内部的元素
监听数组:Vue2重写了部分数组方法去实现,这部分和
Object.defineProperty
就没有关系了。直接通过索引修改数组无法触发更新
最好用**
splice
**方法对数组进行增删操作,因为splice在vue中重写了
新增属性:
$set
方法为对象的新增属性并进行拦截,如果是数组,$set
内部调用splice方法删除属性:
$delete
删除数据中的某个属性,并且能够侦测到数据的变化。Vue3数据劫持使用Proxy
对比Vue2的拦截方式更加全面,避免了需要考虑使用
$set
之类等情况发布者-订阅者模式
Observer:发布者,内部对数据访问和修改进行拦截,发送通知给Dep
Dep:订阅器,收集Watcher和通知Watcher
Watcher:订阅者,通知视图更新
这一篇文章主要从原理方面进行说明,在这个基础上可以看我下一篇实现双向绑定的文章。
Vue双向绑定:实现篇
参考
Proxy - ECMAScript 6入门 (ruanyifeng.com)。
Vue双向绑定:原理篇(详细)相关推荐
- vue双向绑定原理及实现
vue双向绑定原理及实现 一.MVC模式 二.MVVM模式 三.双向绑定原理 1.实现一个Observer 2.实现一个Watcher 3.实现一个Compile 4.实现一个MVVM 四.最后写一个 ...
- 【vue双向绑定原理浅析】
vue双向绑定原理浅析 1.什么是双向绑定? 所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据.(数据变化更新视图,视图变 ...
- 前端技巧|vue双向绑定原理,助你面试成功
在面试一些大厂的时候,面试官可能会问到你vue双向数据绑定的原理是什么?有些小伙伴不知道是什么东西,这样你在面试官的眼里就大打折扣了.今天小千就来给大家介绍一下vue的双向绑定原理,千万不要错过啦. ...
- 浅谈vue双向绑定原理
简析mvvm框架 目前angular,reat和vue都是mvvm类型的框架 以vue为例 这里的vm 就是vue框架,它相当于中间枢纽的作用,连接着model 和view. 当前台显示的view发生 ...
- Vue双向绑定原理代码实现
1.代码实现Vue双向绑定与事件绑定,v-bind v-model v-on DOM结构准备 <body><div id="app"><form> ...
- 浅聊vue双向绑定原理Object.defineProperty-/-Proxy
什么是双向绑定呢?vue又是怎么做的我们接下来就聊一聊 什么是双向绑定? 当数据模型data变化时,页面视图会得到响应更新 vue又是怎么做的? vue其实现原理是对data的getter/sette ...
- 通俗易懂了解Vue双向绑定原理及实现
https://www.cnblogs.com/wangjiachen666/p/9883916.html 亲测可用
- Vue基础知识总结 6:vue双向绑定原理
- Vue数据双向绑定原理(vue2向vue3的过渡)
众所周知,Vue的两大重要概念: 数据驱动 组件系统 接下来我们浅析数据双向绑定的原理 一.vue2 1.认识defineProperty vue2中的双向绑定是基于defineProperty的ge ...
- vue的数据双向绑定原理
前言: 什么是数据双向绑定? vue是一个mvvm框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化,数据也会跟着同步变化.这也算是vue的精髓之处了.单项数据绑定是使用状 ...
最新文章
- linux发送email错误 501 Syntax: HELO hostname
- python操作excel-Python对Excel的读写等操作(转)
- 文件系统与数据库的优缺点
- 方法重载(overload)和方法重写(override)的比较
- 使用Servlet上传多张图片——实体层(ProductInfo.java)
- TCP流中各种队列:
- 【渝粤教育】电大中专Office办公软件 (2)作业 题库
- element table批量删除_element 表格批量删除
- VMware虚拟机文件
- python中datetime默认的1990年改为_如何更改numpy datetime64中的年份值?
- centos6.5 下搭建lamp环境
- 线性代数 : 方程组的几何解释
- 【Python秒杀脚本】淘宝或京东等秒杀抢购
- Qt实现提示音以及QSound的使用说明
- R语言 判别分析:线性判别、K最邻近、有权重的K最邻近、朴素贝叶斯
- 梦三国解析服务器spl文件头失败解决,梦三国手游|深度解析“伪法师”张角:“不容忽视的战场杀器”...
- 高级软件工程第七次作业:LLS战队Alpha敏捷冲刺5
- css设置十六进制背景色和透明度
- 中国移动 云MAS平台HTTP2.1(HTTP版)发送普通短信
- 《JOEL说软件》中文版翻译质量令人失望