深入解析vue.js响应式原理与实现
vue.js响应式原理解析与实现。angularjs是通过脏检查来实现数据监测以及页面更新渲染。之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面。vue.js响应式原理解析与实现
Object.defineProperty
es5新增了Object.defineProperty这个api,它可以允许我们为对象的属性来设定getter和setter,从而我们可以劫持用户对对象属性的取值和赋值。比如以下代码:
const obj = { }; let val = 'cjg'; Object.defineProperty(obj, 'name', { get() { console.log('劫持了你的取值操作啦'); return val; }, set(newVal) { console.log('劫持了你的赋值操作啦'); val = newVal; } }); console.log(obj.name); obj.name = 'cwc'; console.log(obj.name); //欢迎加入全栈开发交流圈一起学习交流:864305860
我们通过Object.defineProperty劫持了obj[name]的取值和赋值操作,我们可以在obj[name]被赋值的时候触发更新页面操作。
发布订阅模式
当事件发生的时候,发布者通知所有订阅该事件的订阅者。我们来看一个例子了解下。
class Dep { constructor() { this.subs = []; } // 增加订阅者 addSub(sub) { if (this.subs.indexOf(sub) < 0) { this.subs.push(sub); } } // 通知订阅者 notify() { this.subs.forEach((sub) => { sub.update(); }) } } const dep = new Dep(); const sub = { update() { console.log('sub1 update') } } const sub1 = { update() { console.log('sub2 update'); } } dep.addSub(sub); dep.addSub(sub1); dep.notify(); // 通知订阅者事件发生,触发他们的更新函数
vue.js首先通过Object.defineProperty来对要监听的数据进行getter和setter劫持,当数据的属性被赋值/取值的时候,vue.js就可以察觉到并做相应的处理。
class Observer { constructor(data) { // 如果不是对象,则返回 if (!data || typeof data !== 'object') { return; } this.data = data; this.walk(); } // 对传入的数据进行数据劫持 walk() { for (let key in this.data) { this.defineReactive(this.data, key, this.data[key]); } } // 创建当前属性的一个发布实例,使用Object.defineProperty来对当前属性进行数据劫持。 defineReactive(obj, key, val) { // 创建当前属性的发布者 const dep = new Dep(); /* * 递归对子属性的值进行数据劫持,比如说对以下数据 * let data = { * name: 'cjg', * obj: { * name: 'zht', * age: 22, * obj: { * name: 'cjg', * age: 22, * } * }, * }; * 我们先对data最外层的name和obj进行数据劫持,之后再对obj对象的子属性obj.name,obj.age, obj.obj进行数据劫持,层层递归下去,直到所有的数据都完成了数据劫持工作。 */ new Observer(val); Object.defineProperty(obj, key, { get() { // 若当前有对该属性的依赖项,则将其加入到发布者的订阅者队列里 if (Dep.target) { dep.addSub(Dep.target); } return val; }, set(newVal) { if (val === newVal) { return; } val = newVal; new Observer(newVal); dep.notify(); } }) } } // 发布者,将依赖该属性的watcher都加入subs数组,当该属性改变的时候,则调用所有依赖该属性的watcher的更新函数,触发更新。 class Dep { constructor() { this.subs = []; } addSub(sub) { if (this.subs.indexOf(sub) < 0) { this.subs.push(sub); } } notify() { this.subs.forEach((sub) => { sub.update(); }) } } Dep.target = null; // 观察者 class Watcher { /** *Creates an instance of Watcher. * @param {*} vm * @param {*} keys * @param {*} updateCb * @memberof Watcher */ constructor(vm, keys, updateCb) { this.vm = vm; this.keys = keys; this.updateCb = updateCb; this.value = null; this.get(); } // 根据vm和keys获取到最新的观察值 get() { Dep.target = this; const keys = this.keys.split('.'); let value = this.vm; keys.forEach(_key => { value = value[_key]; }); this.value = value; Dep.target = null; return this.value; }//欢迎加入全栈开发交流圈一起学习交流:864305860 update() { const oldValue = this.value; const newValue = this.get(); if (oldValue !== newValue) { this.updateCb(oldValue, newValue); } } } let data = { name: 'cjg', obj: { name: 'zht', }, }; new Observer(data); // 监听data对象的name属性,当data.name发现变化的时候,触发cb函数 new Watcher(data, 'name', (oldValue, newValue) => { console.log(oldValue, newValue); }) data.name = 'zht'; // 监听data对象的obj.name属性,当data.obj.name发现变化的时候,触发cb函数 new Watcher(data, 'obj.name', (oldValue, newValue) => { console.log(oldValue, newValue); }) data.obj.name = 'cwc'; data.obj.name = 'dmh';
这样,一个简单的响应式数据监听就完成了。当然,这个也只是一个简单的demo,来说明vue.js响应式的原理,真实的vue.js源码会更加复杂,因为加了很多其他逻辑。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
单元测试优化的实践(Generative Testing)
首先为什么要写单元测试? 因为对于任何一个软件来说,“满足需求”是他存在的必要条件,也是软件的价值体现。单元测试一定是为它服务的。所以很容易知道写单元测试的两个动机:驱动(如:TDD)和验证功能实现。另外,软件需求“易变”的特征决定了修改代码成为必然,在这种情况下,单元测试能保护已有的功能不被破坏。 基于以上两点共识,我们看看传统的单元测试有什么特征? 基于用例的测试(By Example) 单元测试最常见的套路就是Given、When、Then三部曲。 Given:初始状态或前置条件 When:行为发生 Then:断言结果 编写时,我们会精心准备(Given)一组输入数据,然后在调用行为后,断言返回的结果与预期相符。这种基于用例的测试方式在开发(包括TDD)过程中十分好用。因为它清晰地定义了输入输出,而且大部分情况下体量都很小、容易理解。 但这样的测试方式也有坏处。 第一点在于测试的意图。用例太过具体,我们就很容易忽略自己的测试意图。比如我曾经看过有人在写计算器kata程序的时候,将其中的一个测试命名为“return 3 when add 1 and 2”,这样的命名其实掩盖了测试用...
- 下一篇
nmap端口检测命令总结&kali自带wafw00f工具
使用nmap目的是为了检测目标机开放的端口情况。 一、检测端口情况 第一步:检测目标机是否开启(nmap -sn * 命令) 第二步:检测目标机开放的端口,分别向1000个TCP端口发送探测包,若有回应则说明端口是开放的,也就说明对应服务是运行状态。(nmap -O * 命令) 第三步:查询正在运行的服务的版本。(nmap -sV -O * 命令) 二、nmap包含的其他工具 1、测试waf是否存在(nmap -p80,443 --script=http-waf-detect * 命令) 从图中可以看到waf是存在的,接下来还可以精确定位所使用的waf产品(nmap -p 80,443 --script=http-waf-fingerprint * 命令),但是利用这个命令我并没有测试出来什么有效的信息 2、还有一个kali linux自带的脚本wafw00f可以帮助我们检测waf版本(wafw00f * 命令) 可以看到获取了该服务器使用的防火墙是OWASP CRS。 该工具的原理:发送一些基本的恶意包,根据响应信息查找其中具有标识性的信息。
相关文章
文章评论
共有0条评论来说两句吧...