响应式特点

  • 数据响应式

修改数据时,视图自动更新,避免繁琐Dom操作,提高开发效率

  • 双向绑定

数据改变,视图随之改变。视图改变,数据随之改变

  • 数据驱动

开发时仅需要关注数据本身,不需要关心数据如何渲染到视图

官方教程: https://cn.vuejs.org/v2/guide/reactivity.html

MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

vue 2.x 基于 defineProperty 实现数据捕捉

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。

Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

下面是一段模仿 vue 实现数据捕捉的代码

interface Vue{
data: {
[prop: string]: any
}
[prop: string]: any
} let vm: Vue = {
data: {
name: 'Tom',
age: 22,
},
} //数据劫持
function proxyData(vm: Vue){
Object.keys(vm.data).forEach(key => {
console.log(key, vm.data[key])
vm[key] = vm.data[key];
Object.defineProperty(vm, key, {
enumerable: true, //可枚举
configurable: true, //可配置:删除或重定义
get(){
console.log('getter:', vm.data[key]);
return vm.data[key];
},
set(newVal){
console.log('setter', newVal);
if (newVal === vm.data[key]){
return;
}
vm.data[key] = newVal;
document.querySelector('#app')!.textContent = vm.data[key];
}
})
})
} proxyData(vm);
vm.name = 'karolina'; //模拟数据发生改变,视图改变
console.log(vm);
// {
// name: "karolina"
// age: 33
// data:{
// name: "karolina"
// age: 33
// }
// }

Vue 3.x 基于 Proxy 代理捕捉数据

MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

ES6 提供 Proxy 捕捉器, 相比于 Object.defineProperty 代理整个对象而非属性,代码上更简洁,性能上由浏览器优化更快

同样下面是一段模仿 vue 实现数据捕捉的代码

let data ={
name: 'Tom',
age: 22,
}; let vm = new Proxy(data, {
get(target: any, key){
if (key in target){
console.log('getter: ',key, target[key]);
return target[key];
}
},
set(target: any, key, newVal,){
console.log('setter: ',key, target[key]);
if (target[key] === newVal){
return false;
}
target[key] = newVal;
document.querySelector('#app')!.textContent = target[key];
return true;
},
}) vm.name = 'Karolina';
console.log(vm);
console.log(vm.age);

发布订阅模式

在“发布者-订阅者”模式中,称为发布者的消息发送者不会将消息编程为直接发送给称为订阅者的特定接收者。

这意味着发布者和订阅者不知道彼此的存在。存在第三个组件,称为代理或消息代理或事件总线,它由发布者和订阅者都知道,它过滤所有传入的消息并相应地分发它们。

换句话说,pub-sub是用于在不同系统组件之间传递消息的模式,而这些组件不知道关于彼此身份的任何信息。经纪人如何过滤所有消息?实际上,有几个消息过滤过程。最常用的方法有:基于主题和基于内容的。

  • 订阅者(subscriber)需要在 事件中心 注册事件
  • 发布者(publisher)需要于 事件中心 触发事件
  • 订阅者和发布者无需知道对方身份

Vue中的发布订阅模式

https://cn.vuejs.org/v2/guide/migration.html#dispatch-和-broadcast-替换

下面是Vue的发布订阅伪代码,用于兄弟组件之间通信

//事件中心
let eventHub = new Vue(); //ComponetA.vue 订阅者
willDo: function(){
eventHub.$on('will-do', (text)=>{console.log(text)});
} //ComponetB.vue 发布者
willDo: function(){
eventHub.$emit('will-do', {text: 'Hello'});
}

下面手写代码来模拟Vue的发布订阅模式实现

//存储主题和句柄,主题为事件名,句柄为hanlder
interface ITopicMap {
[prop: string]: Array<Function>,
} //事件中心,封装订阅和发布事件
class EventCenter {
public topicMap:ITopicMap = {}; $on(topic: string, handler: Function): void{
this.topicMap[topic] = this.topicMap[topic] || [];
this.topicMap[topic].push(handler);
} $emit(topic: string, ...params: any){
if (topic in this.topicMap){
this.topicMap[topic].forEach((handler)=>{
handler(...params);
})
}
}
} //测试
let hub: EventCenter = new EventCenter(); //订阅者注册事件
hub.$on('click', ()=>{console.log('you click me')});
hub.$on('custom', (name: string, age: 12)=>{console.log(`your name is ${name}, and age is ${age}`)}); //发布者触发事件
hub.$emit('click'); //you click me
hub.$emit('custom', 'Tom', 22); //your name is Tom, and age is 22

Vue中的观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

  • 观察者Watcher Observer通过update()描述当事件发生时需要做的事情

  • 目标Dep subject需要添加认识观察者,通过notify()通知触发观察者事件

  • 没有事件中心,观察者和目标需要知道对方身份,抽象耦合

  • 手写代码模拟vue中观察者模式实现


class Observer {
constructor(public update: Function){}
} class Dep {
public observerList: Array<Observer> = []; addObserver(observer: Observer){
this.observerList.push(observer);
} notify(...params: any){
this.observerList.forEach(observer => {
observer.update(...params);
})
}
} //创建事件目标
let dep = new Dep();
let observer = new Observer(
(name:string)=>{console.log(`my name is ${name}`)}
); //目标添加观察者对象
dep.addObserver(observer); //事件触发通知
dep.notify('Tim'); //my name is Tim

老生常谈的 观察者模式发布订阅模式 区别

  1. 在观察者模式中,主体维护观察者列表,因此主体知道当状态发生变化时如何通知观察者。然而,在发布者/订阅者中,发布者和订阅者不需要相互了解。它们只需在中间层消息代理(或消息队列)的帮助下进行通信。
  2. 在发布者/订阅者模式中,组件与观察者模式完全分离。在观察者模式中,主题和观察者松散耦合。
  3. 观察者模式主要是以同步方式实现的,即当发生某些事件时,主题调用其所有观察者的适当方法。发布服务器/订阅服务器模式主要以异步方式实现(使用消息队列)。
  4. 发布者/订阅者模式更像是一种跨应用程序模式。发布服务器和订阅服务器可以驻留在两个不同的应用程序中。它们中的每一个都通过消息代理或消息队列进行通信。

其他行为模式参考

其他行为模式学习网站: https://www.runoob.com/design-pattern/observer-pattern.html

Vue源码分析之数据驱动的更多相关文章

  1. 前端Vue 源码分析-逻辑层

    Vue 源码分析-逻辑层 预期的效果: 监听input的输入,input在输入的时候,会触发 watch与computed函数,并且会更新原始的input的数值.所以直接跟input相关的处理就有3处 ...

  2. [Vue源码分析] v-model实现原理

    最近小组有个关于vue源码分析的分享会,提前准备一下… 前言:我们都知道使用v-model可以实现数据的双向绑定,及实现数据的变化驱动dom的更新,dom的更新影响数据的变化.那么v-model是怎么 ...

  3. Vue源码分析(二) : Vue实例挂载

    Vue源码分析(二) : Vue实例挂载 author: @TiffanysBear 实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-wi ...

  4. Vue源码分析(一) : new Vue() 做了什么

    Vue源码分析(一) : new Vue() 做了什么 author: @TiffanysBear 在了解new Vue做了什么之前,我们先对Vue源码做一些基础的了解,如果你已经对基础的源码目录设计 ...

  5. vue 快速入门 系列 —— 侦测数据的变化 - [vue 源码分析]

    其他章节请看: vue 快速入门 系列 侦测数据的变化 - [vue 源码分析] 本文将 vue 中与数据侦测相关的源码摘了出来,配合上文(侦测数据的变化 - [基本实现]) 一起来分析一下 vue ...

  6. vue源码分析—Vue.js 源码目录设计

    Vue.js 的源码都在 src 目录下,其目录结构如下 src ├── compiler # 编译相关 ├── core # 核心代码 ├── platforms # 不同平台的支持 ├── ser ...

  7. vue源码分析—Vue.js 源码构建

    Vue.js 源码是基于 Rollup 构建的,它的构建相关配置都在 scripts 目录下.(Rollup 中文网和英文网) 构建脚本 通常一个基于 NPM 托管的项目都会有一个 package.j ...

  8. vue源码分析—认识 Flow

    认识 Flow Flow 是 facebook 出品的 JavaScript 静态类型检查⼯具.Vue.js 的源码利⽤了 Flow 做了静态类型检查, 所以了解 Flow 有助于我们阅读源码 Flo ...

  9. Vue 源码分析—— 目录结构

    一,Vue.js 的源码都是在src 目录下,其目录结构如下. 1.compiler 目录包含Vue.js 所有编译相关的代码.它包括把所有模板解析成ast 语法树, ast 语法树优化等功能. 2. ...

随机推荐

  1. Windows 平台做 Python 开发的最佳组合

    在 Windows 上怎样做 Python 开发?是像大神那样使用纯文本编辑器,还是用更加完善的 IDE?到底是用自带的命令行工具,还是需要装新的 Terminal?本文将带你了解如何利用微软官方维护 ...

  2. Lua中 pairs和ipairs的区别

    Lua系列–pairs和ipairsLua中Table的存储方式在看二者的区别之前,我们首先来看一下Lua中的table是如何在内存中进行分配的.Table的组成:1.哈希表 用来存储Key-Valu ...

  3. etcd3.0集群安装

    etcd 是一个分布式一致性键值存储,用于共享配置和服务发现,Go编写,并使用 Raft 一致性算法来管理高可用复制日志. 特性 简单:支持curl方式的用户API(http+json)安全:可选ss ...

  4. Laragon修改配置快速创建其他框架的项目

    配置方式 依葫芦画瓢,如添加thinkPHP: # Thinkphp Thinkphp 3.2=composer create-project topthink/thinkphp %s Thinkph ...

  5. KNN算法基本原理与sklearn实现

    ''' KNN 近邻算法,有监督学习算法 用于分类和回归 思路: 1.在样本空间中查找 k 个最相似或者距离最近的样本 2.根据这 k 个最相似的样本对未知样本进行分类 步骤: 1.对数据进行预处理 ...

  6. Python 字典(Dictionary) fromkeys()方法

    描述 Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值.高佣联盟 www.cgewang.com 语法 from ...

  7. luogu P4516 [JSOI2018]潜入行动

    LINK:潜入行动 初看题感觉很不可做 但是树形dp的状态过于明显. 容易设\(f_{x,j,l,r}\)表示x为根子树内放了j个设备且子树内都被覆盖l表示x是否被覆盖r表示x是否放设备的方案数. 初 ...

  8. intel:spectre&Meltdown侧信道攻击(三)—— raw hammer

    今天介绍raw hammer攻击的原理:这次有点“标题党”了.事实上,raw hammer是基于DRAM内存的攻击:所以理论上,只要是用了DRAM内存的设备,不论是什么cpu(intel.amd,或则 ...

  9. jar包冲突解决

    背景: 新需求需要引入新jar包,引入后发现本地启动没有报错,发到测试环境提示某个bean无法创建,nested exception is java.lang.VerifyError: Bad typ ...

  10. 找工作的你不容错过的45个PHP面试题附答案(下篇)

    找工作的你不容错过的45个PHP面试题附答案(上篇) Q28:你将如何使用PHP创建Singleton类? /** * Singleton class * */ final class UserFac ...