详解Vue 事件驱动和依赖追踪

网友投稿 223 2023-05-21


详解Vue 事件驱动和依赖追踪

之前关于 vue 数据绑定原理的一点分析,最近需要回顾,就顺便发到随笔上了

在之前实现一个自己的Mvvm中,用 setter 来观测model,将界面上所有的 viewModel 绑定到 model 上。 当model改变,更新所有的viewModel,将新值渲染到界面上 。同时监听界面上通过v-model 绑定的所有 input,并通过 addEventListener事件将新值更新到 model 上,以此来完成双向绑定 。

但是那段程序除了用来理解 defineProperty,其它一文不值。

没有编译节点 。

没有处理表达式依赖 。

这里我将解决表达式依赖这个问题,vue 模板的编译我会在下一节介绍 。

为数据定义 getter & setter

class Observer {

constructor(data) {

this._data = data;

this.walk(this._data);

}

walk(data) {

Object.keys(data).forEach((key) => { this.defineRective(data, key, data[key]) })

};

defineRective(vm, key, value) {

var self = this;

if (value && typeof value === "object") {

this.walk(value);

}

Object.defineProperty(vm, key, {

get: function() {

return value;

},

set: function(newVal) {

if (value != newVal) {

if (newVal && typeof newVal === "object") {

self.walk(newVal);

}

value = newVal;

}

}

})

}

}

module.exports = Observer;

这样,就为每个属性添加了 getter 和 setter ,当属性是一个对象,那么就递归添加。

一旦获取属性值或者为属性赋值就会触发 get 或 set ,当触发了 set,即model变化,就可以发布一个消息,通知所有viewModel 更新。

defineRective(vm, key, value) {

// 将这个属性的依赖表达式存储在闭包中。

var dep = new Dep();

var self = this;

if (value && typeof value === "object") {

this.walk(value);

}

Object.defineProperty(vm, key, {

get: function() {

return value;

},

set: function(newVal) {

if (value != newVal) {

if (newVal && typeof newVal === "object") {

self.walk(newVal);

}

value = newVal;

// 通知所有的 viewModel 更新

dep.notify();

}

}

})

}

那么怎么定义 Dep 呢??

class Dep {

constructor() {

// 依赖列表

this.dependences = [];

}

// 添加依赖

addDep(watcher) {

if (watcher) {

this.dependences.push(watcher);

}

}

// 通知所有依赖更新

notify() {

this.dependences.forEach((watcher) => {

watcher.update();

})

}

}

module.exports = Dep;

这里的每个依赖就是一个Watcher 。

看看如何定义 Watcher

这里每一个http:// Watcher 都会有一个唯一的id号,它拥有一个表达式和一个回调函数 。

比如 表达式 a +b ; 会在get 计算时 访问 a 与 b , 由于 javascript是单线程,任一时刻只有一处javaScript代码在执行, 用Dep.target 作为一个全局变量来表示当前 Watcher 的表达式,然后通过 compute 访问 a ,b ,触发 a 与b 的getter,在 getter 里面将 Dep.target 添加为依赖 。

一旦 a 与 b 的set 触发,调用 update 函数,更新依赖的值 。

var uid = 0;

class Watcher {

constructor(viewModel, exp, callback) {

this.viewModel = viewModel;

this.id = uid++;

this.exp = exp;

this.callback = callback;

this.oldValue = "";

this.update();

}

get() {

Dep.target = this;

var res = this.compute(this.viewModel, this.exp);

Dep.target = null;

return res;

}

update() {

var newValue = this.get();

if (this.oldValue === newValue) {

return;

}

// callback 里进行Dom 的更新操作

this.callback(newValue, this.oldValue);

this.oldValue = newValue;

}

compute(viewModel, exp) {

var res = replaceWith(viewModel, exp);

return res;

}

}

module.exports = Watcher;

由于当前表达式需要在 当前的model下面执行,所以 采用replaceWith 函数来代替 with 。

通过get 添加依赖

Object.defineProperty(vm, key, {

get: function() {

var watcher = Dep.target;

if (watcher && !dep.dependences[watcher.id]) {

dep.addDep(watcher);

}

return value;

},

set: function(newVal) {

if (value != newVal) {

if (newVal && typeof newVal === "object") {

self.walk(newVal);

}

value = newVal;

dep.notify();

}

}

})

这种添加依赖的方式实在太巧妙了 。

这里我画了一个图来描述

最后通过一段代码简单测试一下

const Observer = require('./Observer.js');

const Watcher = require('./watcher.js');

var data = {

a: 10,

b: {

c: 5,

d: {

e: 20,

}

}

}

var observe = new ObserMaGCoOfoCSver(data);

var watcher = new Watcher(data, "a+b.c", function(newValue, oldValue) {

console.log("new value is " + newValue);

console.log("oldValue is " + oldValue);

});

console.log("\r\n");

console.log("a has changed to 50,then the expr should has value 55");

data.a = 50;

console.log("\r\n");

console.log("b.c has changed to 50,then the expr should has value 122");

data.b.c = 72;;

console.log("\r\n");

console.log("b.c has reseted an object,then the expr should has value 80");

data.b = { c: 30 }

OK 大功告成


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Vue form 表单提交+ajax异步请求+分页效果
下一篇:Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~