vue双向绑定原理浅析

1、什么是双向绑定?

​ 所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据。(数据变化更新视图,视图变化更新数据)

2、如何实现双向绑定?

在vue中可以通过v-model实现双向绑定

<template><div id="app">{{username}} <br/><input type="text" v-model="username"></div>
</template>
<script>
export default {name: 'App',data(){return {username:''}}
}
</script>

但其实v-model只是一个语法糖,他实际做了两步动作:1、绑定数据元素;2、触发输入事件

ps: v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
text 和 textarea 元素使用 value 属性和 input 事件;
checkbox 和 radio 使用 checked 属性和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件;

也就是说其实v-model等同于如下代码:

<template><div id="app">{{username}} <br/><input type="text" :value="username" @input="username=$event.target.value"></div>
</template>
<script>
export default {name: 'App',data(){return {username:''}}
}
</script>

但为什么 <input type=“text” :value=“username” @input=“username=$event.target.value”>这样写就会实现双向绑定?他的核心是什么?

3、vue实现双向数据绑定的核心是Object.defineProperty()方法

Object.defineProperty(obj,prop,descriptor)使用:

obj:要在其上定义属性的对象。

prop:要定义或修改的属性的名称。

descriptor:将被定义或修改的属性描述符。

descriptor的基本结构
{value: 属性对应的值,默认为 undefined。configurable: true | false,   //属性是否可以被delete,或者再次修改descriptorenumerable: true | false,     //属性是否可以被for...in,Object.keys()枚举writable: true | false,       //对象是否可被赋值get:function(){} | undefined, set:function(){} | undefined
}

4、简单双向绑定代码

<body><div id="demo"></div><input type="text" id="inp">
</body>
<script type="text/javascript">var obj = {};var demo = document.querySelector('#demo')var inp = document.querySelector('#inp')Object.defineProperty(obj, 'name', {get: function() {return val;},set: function(newVal) { //当该属性被赋值的时候触发inp.value = newVal;demo.innerHTML = newVal;}})inp.addEventListener('input', function(e) {// 给obj的name属性赋值,进而触发该属性的set方法obj.name = e.target.value;});obj.name = '测试'; //在给obj设置name属性的时候,触发了set这个方法
</script>

由上得出Object.defineProperty可以先实现简单的双向绑定,但是如果有100个、1000个dom,我们不可能一个一个设置其值,这样效率太低。这样我们就要运用到发布订阅模式

5、发布者-订阅者模式

​ 发布者-订阅者模式定义了对象间的一种一对多的依赖关系,只要当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题。

​ (ps:我们去商店买可乐时被老板告诉可乐售罄,但是老板告知你们可以添加到商店的微信群中,等可乐到货后,我在通知你们。)

这就是一个简单的发布者-订阅者模式,可乐是观察对象,我们是订阅者,老板是观察者,微信群是订阅器,当老板知道可乐到货后,就在微信群中通知我们,我们就回去买可乐。

同理vue也是这样做的:

​ 我们new vue({})传入的data就是我们监听器(Observer )的观察对象,当初始化的时候,我们要把data的值默认渲染在dom中,在dom中使用({{}},v-model,v-bind)data的值就是订阅者,在初始化的时候就要把订阅者添加到订阅器(Dep)中,当data的值发生的改变时,会通知到去告诉订阅者们(Watcher)更新数据,最后指令解析器( Compile)解析对应的指令,进而会执行对应的更新函数,从而更新视图。

​ 1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者

​ 2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数

​ 3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图

6、各部分实现

6、1监听器Observer
​ 监听器的作用就是去监听数据的每一个属性,我们上面也说了使用 Object.defineProperty 方法,当我们监听到属性发生变化之后我们需要通知 Watcher 订阅者执行更新函数去更新视图,在这个过程中我们可能会有很多个订阅者 Watcher 所以我们要创建一个容器 Dep 去做一个统一的管理。

class Dep {constructor() {this.watchers = [];}//添加add(Watcher) {this.watchers.push(Watcher);}//通知notify() {this.watchers.forEach(watcher => {watcher.update();})}
}
function observer(data) {if (!data || typeof data !== "object") {return;}Object.keys(data).forEach(key => {initData(data, key, data[key]);});
}function initData(data,key,value) {observer(value);let reactiveValue = value;const dep = new Dep();Object.defineProperty(this, key, {configurable: true,enumerable:true,get() {Dep.target && dep.add(Dep.target)return reactiveValue;},set(newVal) {console.log("监听成功");if(newVal != reactiveValue) {reactiveValue = newVal;//触发通知dep.notify();}}})
}observer({a:'11'
})

以上我们就创建了一个监听器 Observer,我们现在可以尝试一下给一个对象添加监听然后改变属性会有何变化。这是侯监听a,并修改a的值就会打印监听成功

6.2、订阅者Watcher
Watcher 主要是接受属性变化的通知,然后去执行更新函数去更新视图,所以我们做的主要是有两步:

  1. 把 Watcher 添加到 Dep 容器中
  2. 接收到通知,执行更新函数。
class Watcher {constructor(vm, key, callback) {this.callback = callback;this.vm = vm;this.key = key;Dep.target = thisthis.value = this.vm[this.key]Dep.target = null}update() {this.callback();}
}

6、3解析器Compile
Compile 的主要作用一个是用来解析指令初始化模板,一个是用来添加添加订阅者,绑定更新函数。

 initVModel() {const nodes = this.dom.querySelectorAll('[v-model]');nodes.forEach(node =>{const key = node.getAttribute('v-model');node.value = this[key];new Watcher(this,key,()=>{node.value = this[key]; })node.addEventListener('input', ev=>{this[key] = ev.target.value;})})}

7、具体实现

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app"><input type="text" v-model="msg"></div></body>
<script>//订阅器class Dep {constructor() {this.watchers = [];}//添加add(Watcher) {this.watchers.push(Watcher);}//通知notify() {this.watchers.forEach(watcher => {watcher.update();})}}//观察者class Watcher {constructor(vm, key, callback) {this.callback = callback;this.vm = vm;this.key = key;Dep.target = thisthis.value = this.vm[this.key];Dep.target = null}update() {this.callback();}}class myVue {constructor({el,data}) {this.dom = document.querySelector(el);this.data = data;//初始化数据this.initData();//初始化v-modelthis.initVModel();}initData() {Object.entries(this.data).forEach(([key, value]) => {let reactiveValue = value;const dep = new Dep();Object.defineProperty(this, key, {configurable: true,enumerable:true,get() {Dep.target && dep.add(Dep.target)return reactiveValue;},set(newVal) {if(newVal != reactiveValue) {reactiveValue = newVal;//触发通知dep.notify();}}})});}initVModel() {const nodes = this.dom.querySelectorAll('[v-model]');nodes.forEach(node =>{const key = node.getAttribute('v-model');node.value = this[key];new Watcher(this,key,()=>{node.value = this[key]; })node.addEventListener('input', ev=>{this[key] = ev.target.value;})})}}</script><script>const vm =  new myVue({el: "#app",data: {msg: "abc"}})
</script></html>

8、总结

​ 首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;
然后在编译的时候在该属性的数组dep中添加订阅者,Vue中的v-model会添加一个订阅者,{{}}也会,v-bind也会;
最后修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。

【vue双向绑定原理浅析】相关推荐

  1. vue双向绑定原理及实现

    vue双向绑定原理及实现 一.MVC模式 二.MVVM模式 三.双向绑定原理 1.实现一个Observer 2.实现一个Watcher 3.实现一个Compile 4.实现一个MVVM 四.最后写一个 ...

  2. 前端技巧|vue双向绑定原理,助你面试成功

    在面试一些大厂的时候,面试官可能会问到你vue双向数据绑定的原理是什么?有些小伙伴不知道是什么东西,这样你在面试官的眼里就大打折扣了.今天小千就来给大家介绍一下vue的双向绑定原理,千万不要错过啦. ...

  3. 浅谈vue双向绑定原理

    简析mvvm框架 目前angular,reat和vue都是mvvm类型的框架 以vue为例 这里的vm 就是vue框架,它相当于中间枢纽的作用,连接着model 和view. 当前台显示的view发生 ...

  4. Vue双向绑定原理代码实现

    1.代码实现Vue双向绑定与事件绑定,v-bind v-model v-on DOM结构准备 <body><div id="app"><form> ...

  5. 浅聊vue双向绑定原理Object.defineProperty-/-Proxy

    什么是双向绑定呢?vue又是怎么做的我们接下来就聊一聊 什么是双向绑定? 当数据模型data变化时,页面视图会得到响应更新 vue又是怎么做的? vue其实现原理是对data的getter/sette ...

  6. 通俗易懂了解Vue双向绑定原理及实现

    https://www.cnblogs.com/wangjiachen666/p/9883916.html 亲测可用

  7. Vue基础知识总结 6:vue双向绑定原理

  8. Vue数据双向绑定原理(vue2向vue3的过渡)

    众所周知,Vue的两大重要概念: 数据驱动 组件系统 接下来我们浅析数据双向绑定的原理 一.vue2 1.认识defineProperty vue2中的双向绑定是基于defineProperty的ge ...

  9. vue的数据双向绑定原理

    前言: 什么是数据双向绑定? vue是一个mvvm框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化,数据也会跟着同步变化.这也算是vue的精髓之处了.单项数据绑定是使用状 ...

最新文章

  1. T75 大数加法+取模
  2. Python3学习笔记----环境安装及文本编辑器的选择
  3. mysql 事务id_[转]MySQL 5.6 全局事务 ID(GTID)实现原理(一)
  4. linux read命令用法
  5. CDH6.3.2之YARN生产环境调优(yarn 生产必做优化项)
  6. Linux 编译 mysql
  7. 包包的结构制图_15种常见领型的结构制图
  8. python及numpy,pandas易混淆的点
  9. .Net水晶报表的使用总结
  10. ip层和4层的接口实现分析
  11. 利用MSF批量打RMI漏洞
  12. 杭州自学python爬虫_金华自学python网络爬虫直播
  13. 目标检测——SSD的学习笔记
  14. eclipse-4.4.2安装Groovy插件(其他版本eclipse可参考)
  15. python登录斗鱼_Python---20行代码爬取斗鱼平台房间数据(下)
  16. 机器视觉软件入门(2)
  17. Hive-内置函数:根据身份证规则从身份证号提取年龄和性别
  18. IntelliJ IDEA2020安装使用(保姆级)
  19. 微博和微信的区别之处在哪里?
  20. 【千里之行,始于足下】大数据高频面试题——Hive篇(一)

热门文章

  1. Navicat:显示的行数与表中实际的行数不一致
  2. WSL2 Ubuntu 换源
  3. python全栈开发之正则表达式和python的re模块
  4. 安装nginx-kafka插件ngx_kafka_module
  5. 2008年华尔街金融风暴
  6. html 自动滚动通知,Marquee标签实现跑马灯、滚动公告、通知效果
  7. 如何为WordPress Gutenberg准备您的网站
  8. Qt之使用setWindowFlags方法遇到的问题
  9. 《如何有效阅读》藤原和博 读书笔记
  10. Nature作图也出错 单细胞UMAP/TSNE图的ggplot做法与修饰