Vue双向绑定原理详解
前言:Vue最核心的功能之一就是响应式的数据绑定模式,即view与model任意一方改变都会同步到另一方,而不需要手动进行DOM操作,本文主要探究此功能背后的原理。
思路分析
以下是一个最简单的双向绑定的例子:
<body>
<div id="app">
<input type="text" v-model="msg">
<p>{{msg}}</p>
</div>
</body>
<script src="https://unpkg.com/vue@2.3.4/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"msg"
}
})
</script>
观察以上代码,我们可以看出双向绑定的涉及到的3个元素:input,data.msg和{{msg}}。双向绑定实现的就是:
1)改变input时,data.msg和{{msg}}都会跟着变化;
(监听input,使其发生变化时改变data.msg,实现了2,{{msg}}也就跟着改变了)
2)改变data.msg时,input和{{msg}}都会变化;
(监听data.msg,使其发生变化时改变input和{{msg}})
第1件事容易实现,只需要找到相应的input,为其添加监听即可,但第2件事有两个关键点:一是如何监听到data.msg的变化;而是如何把data.msg的变化告知所有的{{msg}}。
下面我们一个个来解决如上问题。
实现数据监听observer
JavaScript中有一个特别的方法Object.defineProperty()可以监听到数据的变化,并执行相应的操作。不熟悉此方法的可以参Object.defineProperty()用法。
我们对数据的属性遍历,逐个添加getter和setter
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
// 取出所有属性遍历
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
});
}; function defineReactive(data, key, val) {
observe(val); // 监听子属性
Object.defineProperty(data, key, {
get: function() {
return val;
},
set: function(newVal) {
if (val === newVal) return;
val = newVal;
console.log("监听到变化了!");
}
});
}
var data = {msg: 'hello'};
observe(data);
data.msg="hello world";//监听到变化了
通过遍历对data的每个属性添加getter和setter,当数据发生改变时,就会监听到变化并执行相应的操作了。变化监听到了,如何告诉{{msg}}和input呢?
订阅/发布模式(subscribe&publish)
Vue采用发布/订阅的方式来实现信息(此处即数据的变化)的传达:为每个{{msg}}添加一个订阅data.msg的订阅器,当数据data.msg发生改变时,发布这个变化通知,{{msg}}就会收到这个变化通知,从而自身做出相应的变化。
首先我们得告诉data.msg,它对应了哪些节点,即有哪些节点订阅了msg,然后再将这个变化传播给对应的订阅者
通过一个数组dep保存data.msg对应的订阅者,当data.msg发生改变调用setter时,发布这个变化通知。那dep数组中的订阅者是如何加入进来的呢?
我们可以在属性的getter中实现订阅者的加入,这样只要在新建订阅者时触发getter就可以将订阅者加入对应的属性中了。
Watcher为订阅者类,Dep.target为全局属性,每次new Watcher()时,Dep.target指向当前订阅者,并通过update出发data.msg的getter,从而实现将订阅者将入到data.msg的dep中。
有了以上步骤,接下来我们就只需要遍历节点,对指定的节点新建订阅者Watcher,就可以接收到来自属性变化发布的通知了。
实现complie
compile主要做的事情是解析模板指令,将模板中的变量替换成数据,并为节点添加订阅者,一旦数据变化,可以接收到通知。
到这里,input和{{msg}}就能接收到data.msg的变化了,即实现了model->view的同步变化。此时我们再为input添加上监听,使其改变时将新值赋给data.msg从而触发data.msg的setter,就实现了view->model的同步变化了。
因为遍历解析的过程有多次操作dom节点,为了提高性能,Vue采用DocumentFragment,将挂载目标的所有子节点劫持(通过 append 方法,DOM 中的节点会被自动删除)到 DocumentFragment 中,经过一番处理后,再将 DocumentFragment 整体返回插入挂载目标。
function nodeToFragment(node,vm){
var fragment = document.createDocumentFragment();
var child;
while(child = node.firstChild){
complie(child,vm);//解析节点
fragment.append(child);//将节点劫持到fragment
}
return fragment ;
}
function Vue(options){
this.data = options.data;
observe(this.data,this);
var dom =nodeToFragment(document.getElementById(options.el),this);
document.getElementById(options.el).appendChild(dom);
}
实现
最后,我们新建一个实例,将以上代码放入index.js中:
<body>
<div id="app">
<input type="text" v-model="msg">
<p>{{msg}}</p>
</div>
</body>
<script src="index.js"></script>
<script>
var vm = new Vue({
el:"app",
data:{
msg:"哈哈"
}
})
</script>
打开浏览器,即可发现无论是改变input还是在控制台改变data.msg,input、{{msg}}和data.msg这三者均为同步变化。
小结
根据以上的思路,可以总结如下:
1)通过Object.defineproperty()中setter实现对数据的监听,并在数据改变后项所有订阅者发布这个变化通知;
2)通过订阅/发布的方式实现信息(变化)的传达,每new一个订阅者即触发在data.msg的getter,从而实现将当前订阅者添加进data.msg的订阅者数组中;
3)通过遍历节点,为data.msg对应的input和所有{{msg}}添加订阅者,从而可以收到来自data.msg发布变化通知;同时为input添加监听,实现data.msg跟随input改变;
4)为了提高性能,采用数据劫持的方式,将挂载目标劫持(不是复制,是截取)出来,实现完所有操作后再返回挂载目标。
为了方便理解,文中代码部分采用图片形式,完整代码见源码。
Vue双向绑定原理详解的更多相关文章
- Vue双向绑定原理,教你一步一步实现双向绑定
当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...
- vue双向绑定原理分析
当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/jiangzhenf ...
- vue双向绑定原理及实现
vue双向绑定原理及实现 一.总结 一句话总结:vue中的双向绑定主要是通过发布者-订阅者模式来实现的 发布 订阅 1.单向绑定和双向绑定的区别是什么? model view 更新 单向绑定:mode ...
- Vue双向绑定原理(源码解析)---getter setter
Vue双向绑定原理 大部分都知道Vue是采用的是对象的get 和set方法来实现数据的双向绑定的过程,本章将讨论他是怎么利用他实现的. vue双向绑定其实是采用的观察者模式,get和s ...
- vue 学习二 深入vue双向绑定原理
vue双向绑定原理 请示总体来讲 就是为data的中的每个属性字段添加一个getter/seter属性 以此来追踪数据的变化,而执行这部操作,依赖的就是js的Object.defineProperty ...
- [Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅
有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫 ...
- 通俗易懂了解Vue双向绑定原理及实现
看到一篇文章,觉得写得挺好的,拿过来给大家分享一下,刚好解答了一些困扰我的一些疑惑!!! 1. 前言 每当被问到Vue数据双向绑定原理的时候,大家可能都会脱口而出:Vue内部通过Object.defi ...
- vue双向绑定原理
要了解vue的双向绑定原理,首先得了解Object.defineProperty()方法,因为访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过 Object.definePrope ...
- Vue双向绑定原理及其实现
在之前面试的时候被面试官问到是否了解Vue双向绑定的原理,其实自己之前看过双向绑定的原理,但也就是粗略的了解,但是没有深入.面试官当时让我手写一个原理,但是就蒙了
随机推荐
- Windows搭建Nexus3私服
1. Nexus 简介 Nexus 是一个强大的 Maven 仓库管理器 , 它极大地简化了自己内部仓库的维护和外部仓库的访问 ; 利用 Nexus 你可以只在一个地方就能够完全控制访问和部署在你所维 ...
- Storm-kafka源码分析之Config相关类
要创建一个KafkaSpout对象,必须要传入一个SpoutConfig对象,KafkaSpout的构造函数定义如下: public KafkaSpout(SpoutConfig spoutConf) ...
- 2019 OO第一单元总结(表达式求导)
一. 基于度量的程序结构分析 1. 第一次作业 这次作业是我上手的第一个java程序,使用了4个类来实现功能.多项式采用两个arraylist来存,系数和幂指数一一对应. private ArrayL ...
- Ajax与XMLHttpRequest随笔
1.XMLHttpRequest对象 创建XHR对象:let xhr = new XMLHttpRequest(); open():启动一个请求准备发送 open()接收3个参数:请求类型('GET' ...
- 【xsy1058】 单词 乱搞
题目大意:给你$n$个长度为$m$的字符串,字符集仅为{x,y,z}三个字符,定义两个字符串$(s_i,s_j)$的相似度为$\sum_{k=1}^{m} [s_i[k]==s_j[k]]$. 从$0 ...
- 百度地图笔记_覆盖物(标注marker,折线polyline,多边形polygon)的点击弹窗和右键菜单
利用绘制工具绘制点线面,并在执行绘制完成回调事件给相应覆盖物添加事件操作,提供标注的点击弹窗和标注.折线.多边形的右键删除 效果图如下: 完整代码如下:html+js <!DOCTYPE htm ...
- CAS优缺点
cas优点:如一描述在并发量不是很高时cas机制会提高效率.cas缺点:1.cpu开销大,在高并发下,许多线程,更新一变量,多次更新不成功,循环反复,给cpu带来大量压力.2.只是一个变量的原子性操作 ...
- KBEngine 安装
其实这篇的内容官方文档都有, 但是既然打算记录一下学习笔记, 也就先从安装开始了. 一 下载源代码 进入github下载最新release的源码压缩包. windows选择zip, 下载完成之后右键解 ...
- (转)Mysql常用命令行
原文:http://www.cnblogs.com/TsengYuen/archive/2012/01/11/2319034.html Mysql常用命令行 Mysql经常使用号令行大全 熬头招.my ...
- 在webpack中配置vue.js
在webpack中配置vue.js 这里有两种在webpack中配置vue.js的方法,如下: 1.在main.js中引入vue的包: index.html: <!DOCTYPE html> ...