vue 响应式原理
Vue 采用声明式编程替代过去的类 Jquery 的命令式编程,并且能够侦测数据的变化,更新视图。这使得我们可以只关注数据本身,而不用手动处理数据到视图的渲染,避免了繁琐的 DOM 操作,提高了开发效率。不过理解其工作原理同样重要,这样可以回避一些常见的问题,下面我们来介绍一下 Vue 是如何侦测数据并响应视图的。
Object.defineProperty
Vue 数据响应核心就是使用了 Object.defineProperty
方法( IE9 + ) 。
var obj = {};
Object.defineProperty(obj, 'msg', {
get () {
console.log('get');
},
set (newVal) {
console.log('set', newVal);
}
});
obj.msg // get
obj.msg = 'hello world' // set hello world
取 obj 对象中 msg 的值时会调用 get 方法,给 msg 赋值时会调用 set 方法,并接收新值作为其参数。
这里提一句,在 Vue 中我们调用数据是直接 this.xxx ,而数据其实是 this.data.xxx,原来 Vue 在初始化数据的时候会遍历 data 并代理这些数据。
Object.keys(this.data).forEach((key) => {
this.proxyKeys(key);
}); proxyKeys (key) {
Object.defineProperty(this, key, {
enumerable: false,
configurable: true,
get() {
return this.data[key];
},
set(newVal) {
this.data[key] = newVal;
}
});
}
上面可以看到,取 this.key 的值其实是取 this.data.key 的值,赋值同理。
现在,我们已经知道如何去检测数据的变化,并且做出一些响应了。
观察者模式 ( 发布者-订阅者模式 )
vue 的响应式系统依赖于三个重要的类:Dep 类、Watcher 类、Observer 类。
Dep 类作为发布者的角色,Watcher 类作为订阅者的角色,Observer 类则是连接发布者和订阅者的纽带,决定订阅和发布的时机。
我们先看下面的代码,来对发布者和订阅者有个初步的了解。
class Dep {
constructor() {
this.subs = [];
} addSub(watcher) {
this.subs.push(watcher);
} notify() {
this.subs.forEach(watcher => {
watcher.update();
});
}
} class Watcher {
constructor() {
} update() {
// 接收通知后的处理方法
}
} const dep = new Dep(); // 发布者 dep
const watcher1 = new Watcher(); // 订阅者1 watcher1
const watcher2 = new Watcher(); // 订阅者2 watcher2
dep.addSub(watcher1); // watcher1 订阅 dep
dep.addSub(watcher2); // watcher2 订阅 dep
dep.notify(); // dep 发送通知
上面我们定义了一个发布者 dep,两个订阅者 watcher1、watcher2。让 watcher1、watcher2 都订阅 dep,当 dep 发送通知时,watcher1、watcher2 都能做出各自的响应。
现在我们已经了解了发布者和订阅者的关系,那么剩下的就是订阅和发布的时机。什么时候订阅?什么时候发布?想到上面提到的 Object.defineProperty ,想必你已经有了答案。
我们来看 Observer 类的实现:
class Observer {
constructor(data) {
this.data = data;
this.walk();
} walk() {
Object.keys(this.data).forEach(key => {
this.defineReactive(this.data, key, this.data[key]);
});
} defineReactive(data, key, value) {
const dep = new Dep(); if ( value && typeof value === 'object' ) {
new Observer(value);
} Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
if (Dep.target) {
dep.addSub(Dep.target); // 订阅者订阅 Dep.target 即当前 Watcher 类的实例(订阅者)
}
return value;
},
set(newVal) {
if (newVal === value) {
return false;
}
value = newVal;
dep.notify(); // 发布者发送通知
}
});
}
}
在 Observer 类中,为 data 的每个属性都实例化一个 Dep 类,即发布者。并且在取值时让订阅者(有多个,因为 data 中的每个属性都可以被应用在多个地方)订阅,在赋值时发布者发布通知,让订阅者做出各自的响应。
这里需要提的是 Dep.target,这其实是 Watcher 类的实例,我们可以看看 Watcher 的详细代码:
class Watcher {
constructor(vm, exp, cb) {
this.vm = vm;
this.exp = exp; // data 属性名
this.cb = cb; // 回调函数 // 将自己添加到订阅器
this.value = this.getValue();
} update() {
const value = this.vm.data[this.exp];
const oldValue = this.value;
if (value !== oldValue) {
this.value = value;
this.cb.call(this.vm, value, oldValue); // 执行回调函数
}
} getValue() {
Dep.target = this; // 将自己赋值给 Dep.target
const value = this.vm.data[this.exp]; // 取值操作触发订阅者订阅
Dep.target = null;
return value;
}
}
Watcher 类在构造函数中执行了一个 getValue 方法,将自己赋值给 Dep.target ,并且执行了取值操作,这样就成功的完成了订阅操作。一旦数据发生变化,即有了赋值操作,发布者就会发送通知,订阅者就会执行自己的 update 方法来响应这次数据变化。
数据的双向绑定
数据的双向绑定即数据和视图之间的同步,视图随着数据变化而变化,反之亦然。我们知道 Vue 是支持数据的双向绑定的,主要应用于表单,是通过 v-model 指令来实现的。而通过上面介绍的知识我们是可以知道如何实现视图随着数据变化的,那么如何让数据也随着视图变化而变化呢?其实也很简单,只要给有 v-model 指令的节点监听相应的事件即可,在事件回调中来改变相应的数据。这一切都 Compile 类中完成,假设有一个 input 标签应用了 v-model 指令,在开始编译模板时,遇到 v-model 指令时会执行:更新 dom 节点的值,订阅者订阅,事件监听。
compileModel (node, vm, exp) {
let val = vm[exp]; // 更新内容
this.modelUpdater(node, val); // 添加订阅
new Watcher(vm, exp, (value) => {
// 数据改变时的回调函数
this.modelUpdater(node, value);
}); // 事件监听
node.addEventListener('input', (e) => {
const newValue = e.target.value;
if (val === newValue) {
return false;
}
vm[exp] = newValue;
val = newValue;
});
}
当我们在文本框中输入数据时,会给原有 data 中的某个属性 a 赋值,这时候会触发发布者发起通知,那么所有属性 a 的订阅者都能够同步到最新的数据。
最后,附上一个小 demo
更多精彩内容,欢迎关注微信公众号~
vue 响应式原理的更多相关文章
- 深度解析 Vue 响应式原理
深度解析 Vue 响应式原理 该文章内容节选自团队的开源项目 InterviewMap.项目目前内容包含了 JS.网络.浏览器相关.性能优化.安全.框架.Git.数据结构.算法等内容,无论是基础还是进 ...
- Vue源码--解读vue响应式原理
原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...
- 详解Vue响应式原理
摘要: 搞懂Vue响应式原理! 作者:浪里行舟 原文:深入浅出Vue响应式原理 Fundebug经授权转载,版权归原作者所有. 前言 Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是 ...
- vue响应式原理,去掉优化,只看核心
Vue响应式原理 作为写业务的码农,几乎不必知道原理.但是当你去找工作的时候,可是需要造原子弹的,什么都得知道一些才行.所以找工作之前可以先复习下,只要是关于vue的,必定会问响应式原理. 核心: / ...
- 深入Vue响应式原理
深入Vue.js响应式原理 一.创建一个Vue应用 new Vue({ data() { return { name: 'yjh', }; }, router, store, render: h =& ...
- vue响应式原理解析
# Vue响应式原理解析 首先定义了四个核心的js文件 - 1. observer.js 观察者函数,用来设置data的get和set函数,并且把watcher存放在dep中 - 2. watcher ...
- 浅析Vue响应式原理(三)
Vue响应式原理之defineReactive defineReactive 不论如何,最终响应式数据都要通过defineReactive来实现,实际要借助ES5新增的Object.definePro ...
- 深入解析vue响应式原理
摘要:本文主要通过结合vue官方文档及源码,对vue响应式原理进行深入分析. 1.定义 作为vue最独特的特性,响应式可以说是vue的灵魂了,表面上看就是数据发生变化后,对应的界面会重新渲染,那么响应 ...
- 浅谈vue响应式原理及发布订阅模式和观察者模式
一.Vue响应式原理 首先要了解几个概念: 数据响应式:数据模型仅仅是普通的Javascript对象,而我们修改数据时,视图会进行更新,避免了繁琐的DOM操作,提高开发效率. 双向绑定:数据改变,视图 ...
- Vue响应式原理及总结
Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 ...
随机推荐
- C "right-left" 从左到右
葵花宝典:http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html C混带代码大赛
- http content-type accept的区别
1.Accept属于请求头, Content-Type属于实体头. Http报头分为通用报头,请求报头,响应报头和实体报头. 请求方的http报头结构:通用报头|请求报头|实体报头 响应方的http报 ...
- 基于maven创建和部署Webx项目
1.准备工作 下载 Webx Maven 项目的目录结构Artifact插件. archetype-webx-quickstart-1.0.tar.gz插件:http://central.maven. ...
- Problem 2144 Shooting Game fzu
Problem 2144 Shooting Game Accept: 99 Submit: 465Time Limit: 1000 mSec Memory Limit : 32768 KB ...
- NOIP2017SummerTraining0712
个人感受:打了三个小时不到的第一题,然后也就没有多少时间去搞第二题了,特别是第二题还看到了期望这样的东西,这个难以理解,第三题的树分治,myx大佬说50分好拿,但是我觉得也挺难拿的. 单词检索 时间限 ...
- 用python爬虫爬取去哪儿4500个热门景点,看看国庆不能去哪儿
前言:本文建议有一定Python基础和前端(html,js)基础的盆友阅读. 金秋九月,丹桂飘香,在这秋高气爽,阳光灿烂的收获季节里,我们送走了一个个暑假余额耗尽哭着走向校园的孩籽们,又即将迎来一年一 ...
- python之路第五篇之递归(进阶篇:续:经典例子剖析)
递归 在函数内部,可以调用其他函数; 如果一个函数在内部调用自身本身,这个函数就是递归函数. 例如,我们来计算阶乘: n! = 1 x 2 x 3 x ... x n, 用函数f1(n)表示,可以看出 ...
- Python实战之set学习笔记及简单练习
['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__' ...
- java Web Servlet学习笔记-1 HttpServletQequest对象的学习
HttpServletQequest对象的学习 HttpServletRequest HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求 ...
- Window window = Window.GetWindow(控件)
Window window = Window.GetWindow(控件)