之前关于 vue 数据绑定原理的一点分析,最近需要回顾,就顺便发到随笔上了
在之前实现一个自己的 Mvvm 中,用
来观测
- setter
,将界面上所有的
- model
绑定到
- viewModel
上。 当 model 改变,更新所有的
- model
,将新值渲染到界面上 。同时监听界面上通过
- viewModel
绑定的所有
- v-model
,并通过
- input
事件将新值更新到
- addEventListener
上,以此来完成双向绑定 。
- model
但是那段程序除了用来理解
,其它一文不值。
- defineProperty
这里我将解决表达式依赖这个问题,vue 模板的编译我会在下一节介绍 。
- 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){
- // 将这个属性的依赖表达式存储在闭包中。
- vardep= new Dep();
- varself= this;
- if(value&& typeofvalue=== "object"){
- this.walk(value);
- }
- Object.defineProperty(vm,key, {
- get: function(){
- returnvalue;
- },
- set: function(newVal){
- if(value!=newVal){
- if(newVal&& typeofnewVal=== "object"){
- self.walk(newVal);
- }value=newVal;
- // 通知所有的 viewModel 更新
- dep.notify();
- }
- }
- })}
那么怎么定义
呢??
- Dep
- classDep{
- constructor(){
- // 依赖列表
- this.dependences =[];
- }
- // 添加依赖
- addDep(watcher){
- if(watcher){
- this.dependences.push(watcher);
- }
- }
- // 通知所有依赖更新
- notify(){
- this.dependences.forEach((watcher)=> {
- watcher.update();
- })}
- }
- module.exports =Dep;
这里的每个依赖就是一个
。 看看如何定义
- Watcher
这里每一个
- Watcher
都会有一个唯一的
- Watcher
号,它拥有一个表达式和一个回调函数 。 比如 表达式
- id
; 会在 get 计算时 访问
- a +b
与
- a
, 由于 JavaScript 是单线程,任一时刻只有一处 JavaScript 代码在执行, 用
- b
作为一个全局变量来表示当前
- Dep.target
的表达式,然后通过
- Watcher
访问
- compute
,
- a
,触发 a 与 b 的
- b
,在
- getter
里面将
- getter
添加为依赖 。 一旦 a 与 b 的
- Dep.target
触发,调用
- set
函数,更新依赖的值 。
- update
- varuid= 0;
- classWatcher{
- constructor(viewModel,exp,callback){
- this.viewModel =viewModel;
- this.id =uid++;
- this.exp =exp;
- this.callback =callback;
- this.oldValue = "";
- this.update();
- }
- get(){
- Dep.target = this;
- varres= this.compute(this.viewModel, this.exp);
- Dep.target = null;
- returnres;
- }
- update(){
- varnewValue= this.get();
- if(this.oldValue ===newValue){
- return;
- }
- // callback 里进行Dom 的更新操作
- this.callback(newValue, this.oldValue);
- this.oldValue =newValue;
- }
- compute(viewModel,exp){
- varres= replaceWith(viewModel,exp);
- returnres;
- }
- }
- module.exports =Watcher;
由于当前表达式需要在 当前的 model 下面执行,所以 采用 replaceWith 函数来代替 with ,具体可以查看另一篇随笔 javascript 中 with 的替代语法。
通过 get 添加依赖
- Object.defineProperty(vm,key, {
- get: function(){
- varwatcher= Dep.target;
- if(watcher&& !dep.dependences[watcher.id]){
- dep.addDep(watcher);
- }
- returnvalue;
- },
- set: function(newVal){
- if(value!=newVal){
- if(newVal&& typeofnewVal=== "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 Observer(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 大功告成
来源: http://www.cnblogs.com/likeFlyingFish/p/6744212.html