我们说到如果监听的属性是个对象呢?那么这个对象中的其他属性岂不就是监听不了了吗?如下:

  倘若user中的name、age属性变化,如何知道它们变化了呢?今儿,就来解决这一问题。

  通过走读Vue源码,发现他是利用Observer构造函数为每个对象创建一个Observer对象,来监听数据的,如果数据中的属性又是一个对象,那么就又通过Observer来监听嘛。

  其实,核心思想就是树的先序遍历。如我们将上述Demo中的data数据,图形化一下,就更加明白了,如图:

  理清了大体思路,下面我们就一起来创建一个Observer。

  Observer整体结构如下:

function Observer(data) {
//如若this不是Observer对象,即创建一个
if (!(this instanceof Observer)) {
return new Observer(data);
}
this.data = data;
this.walk(data);
} let p = Observer.prototype = Object.create(null); p.walk = function(data) {
/*
TODO:监听data数据中的所有属性,
并查看data中属性值是否为对象,
若为对象,就创建一个Observer实例
*/
} p.convert = function(key, val) {
//TODO:通过Object.defineProperty监听数据
}

walk

  首先,我们在walk方法中实现对data对象中的所有属性监听,如下:

p.walk = function(data) {
let keys = Object.keys(data);
keys.forEach(key => {
let val = data[key];
this.convert(key, val);
});
}

  由于属性中可能又会是一个对象,那么,我们就有必要监听它们。怎么办呢?如果是个对象,再次利用Observer构造函数,处理它不就完了么。如下:

p.walk = function(data) {
let keys = Object.keys(data);
keys.forEach(key => {
let val = data[key];
//如果val为对象,则交给Observer处理
if (typeof val === 'object') {
Observer(val);
}
this.convert(key, val);
});
}

  你可能会有这样的疑问,如果直接利用Observer处理对象,那么不就与父对象失去关联了么?

  然而并没有,因为JavaScript对于对象是指向地址关系,所以怎么会失去关联呢。

convert

  对于convert方法,就比较简单了,一如既往就是利用Object.defineProperty监听数据,如下:

p.convert = function(key, val) {
Object.defineProperty(this.data, key, {
get: () => {
console.log('访问了' + key + ' 值为' + val);
return val;
},
set: (newVal) => {
console.log('设置了' + key + ' 值为' + newVal);
if (newVal !== val) {
val = newVal;
}
}
});
}

  完整代码:

function Observer(data) {
//如若this不是Observer对象,即创建一个
if (!(this instanceof Observer)) {
return new Observer(data);
}
this.data = data;
this.walk(data);
} let p = Observer.prototype = Object.create(null); p.walk = function(data) {
let keys = Object.keys(data);
keys.forEach(key => {
let val = data[key];
//如果val为对象,则交给Observer处理
if (typeof val === 'object') {
Observer(val);
}
this.convert(key, val);
});
} p.convert = function(key, val) {
Object.defineProperty(this.data, key, {
get: () => {
console.log('访问了' + key + ' 值为' + val);
return val;
},
set: (newVal) => {
console.log('设置了' + key + ' 值为' + newVal);
if (newVal !== val) {
val = newVal;
}
}
});
}

  使用:

<script>
let data = {
user: {
name: 'Monkey',
age:
},
lover: {
name: 'Dorie',
age:
}
};
Observer(data);
</script>

模拟源码深入理解Vue数据驱动原理(2)的更多相关文章

  1. 模拟源码深入理解Vue数据驱动原理(1)

    Vue有一核心就是数据驱动(Data Driven),允许我们采用简洁的模板语法来声明式的将数据渲染进DOM,且数据与DOM是绑定在一起的,这样当我们改变Vue实例的数据时,对应的DOM元素也就会改变 ...

  2. 挖掘隐藏在源码中的Vue技巧!

    前言 最近关于Vue的技巧文章大热,我自己也写过一篇(vue开发中的"骚操作"),但这篇文章的技巧是能在Vue的文档中找到蛛丝马迹的,而有些文章说的技巧在Vue文档中根本找不到踪迹 ...

  3. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  4. Vue源码学习1——Vue构造函数

    Vue源码学习1--Vue构造函数 这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手.Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪.现在也只 ...

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

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

  6. storm源码之理解Storm中Worker、Executor、Task关系 + 并发度详解

    本文导读: 1 Worker.Executor.task详解 2 配置拓扑的并发度 3 拓扑示例 4 动态配置拓扑并发度 Worker.Executor.Task详解: Storm在集群上运行一个To ...

  7. 老李推荐:第14章9节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-遍历控件树查找控件

    老李推荐:第14章9节<MonkeyRunner源码剖析> HierarchyViewer实现原理-遍历控件树查找控件   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员 ...

  8. 老李推荐:第14章5节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-查询ViewServer运行状态

    老李推荐:第14章5节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-查询ViewServer运行状态   poptest是国内唯一 ...

  9. 老李推荐:第14章6节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-启动ViewServer

    老李推荐:第14章6节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-启动ViewServer   poptest是国内唯一一家培养 ...

随机推荐

  1. redis之(六)redis的列表类型的命令

    [一]向列表两端添加元素 -->命令:LPUSH key value [value ...] -->向列表的左侧添加元素,返回值表示增加元素后列表的长度 -->命令:RPUSH ke ...

  2. Python安装scikit-learn包

    我先是按照网上说的下载了个setuptools,然后直接用这个工具去安装,可是安装scikit-learn包的时候确老是有错误,也不知道错误是啥,所以就不用setuptools来安装了. 我直接下载了 ...

  3. NOIP模拟2017.6.11解题报告

    T1: 水题: 代码: #include <cstdio> #include <iostream> #include <algorithm> using names ...

  4. DotNetCore 微服务上传附件

    后台接口升级成netcore 2.0了,然后之前的上传图片的接口就不再使用了.新的接口形式 #region IFormCollection /// <summary> /// IFormC ...

  5. WebDriver自动化测试工具(1)---环境搭建

    Webdriver是一个前端自动化测试工具,可以模拟用户点击链接,填写表单,点击按钮等操作,下面介绍其使用 一.下载WebdriverC#类库以及对应浏览器驱动 http://www.selenium ...

  6. ubuntu16.04 安装composer和 laravel

    一.安装composer $ sudo apt-get update $ sudo apt-get install wget 下载composer.phar $ wget https://getcom ...

  7. 剑指offer-树中两个节点的最低公共祖先

    普通二叉树 /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; ...

  8. HDU 6280 From Tree to Graph(2018 湘潭邀请 E题,树的返祖边)

    其实打返祖边就相当于$x$到祖先这一段点(不包括两端)答案都要减$1$. 然后每个点最多减$1$次$1$. #include <bits/stdc++.h> using namespace ...

  9. Oracle REGEXP

    ORACLE中的支持正则表达式的函数主要有下面四个: 1,REGEXP_LIKE :与LIKE的功能相似 2,REGEXP_INSTR :与INSTR的功能相似 3,REGEXP_SUBSTR :与S ...

  10. WQS二分题集

    WQS二分,一种优化一类特殊DP的方法. 很多最优化问题都是形如“一堆物品,取与不取之间有限制.现在规定只取k个,最大/小化总收益”. 这类问题最自然的想法是:设f[i][j]表示前i个取j个的最大收 ...