经过上一篇的介绍,已经实现了观察者模式的基本内容,接下来要完成的就是将上一篇的发布订阅模式运用到 Nue 中,实现数据驱动界面改变。

在监听数据变化的章节当中,根据指定的区域和数据去编译渲染界面 这个步骤处,我写了一个注释,这个注释是这样的:第一步:给外界传入的所有数据都添加get/set方法,第二步就是在第一步的基础上,给所有属性都添加观察者对象,当数据发生变化时,发布订阅触发观察者对象的回调函数重新渲染界面。

先处理下 v-model 的情况,找到 CompilerUtil 中的 model 方法,将其修改添加观察者对象代码:

model: function (node, value, vm) {
// 第二部:在第一次渲染的时候, 就给所有的属性添加观察者
new Watcher(vm, value, (newValue, oldValue) => {
node.value = newValue;
}); node.value = this.getValue(vm, value);
},

这样就完成了第二步,接下来第三步就是将当前属性的所有观察者对象都放到当前属性的发布订阅对象中管理起来

在创建观察者对象的时候,在构造函数当中,会调用 getOldValue 方法,会调用 CompilerUtil.getValue 方法,这个方法就是用于获取属性值的,在编译模板之前已经给所有属性添加了 get/set 方法,所以在获取属性值的时候,就会触发 get 方法,我们就可以在 get 方法中将当前属性的观察者对象添加到当前属性的发布订阅对象中管理起来。

在 Observer 类中的 defineRecative 方法中添加如下代码:

defineReactive(obj, attr, value) {
this.observer(value); // 第三步:将当前属性的所有观察者对象都放到当前属性的发布订阅对象中管理起来
// 创建属于当前属性的发布订阅对象
let dep = new Dep(); Object.defineProperty(obj, attr, {
get() {
Dep.target && dep.addSub(Dep.target);
return value;
},
set: (newValue) => {
if (value !== newValue) {
this.observer(newValue);
value = newValue;
dep.notify();
console.log('监听到数据的变化, 需要去更新UI');
}
}
})
}

在上述代码中,创建了一个属于当前属性的发布订阅对象,然后在 get 方法中,判断 Dep.target 是否存在,如果存在,就将当前属性的观察者对象添加到当前属性的发布订阅对象中管理起来。Dep.target 就是当前属性的观察者对象,这里该需要在改造一下观察者的类,将观察者对象添加到 Dep.target 中,放在全局中管理起来。等到所有的属性都添加完观察者对象之后,就将 Dep.target 置为 null。

改造观察者类中的 getOldValue 方法, 这样在 get 方法中就可以将当前属性的观察者对象添加到当前属性的发布订阅对象中管理起来了:

getOldValue() {
Dep.target = this;
let oldValue = CompilerUtil.getValue(this.vm, this.attr);
Dep.target = null;
return oldValue;
}

这样就完成了数据驱动界面改变的功能,接下来我们就来测试一下,打开浏览器控制台,更改下数据,看看是否会触发界面的重新渲染,如下图所示:

好了到此为止,我们已经完成了 v-model 数据驱动界面改变的功能。

下面我将以 debugger 的形式来讲解一下整个数据驱动界面改变的过程, 在 defineReactive get 方法中打上断点,如下图所示:

返回浏览器,主要关注调用栈,如下图所示:

自己从下依次往上看,就可以看到整个数据驱动界面改变的过程了,这里我就不一一截图了,大家可以自己去看一下。

如上是 get 方法代码的执行流程,那么 set 的我也可以说明一下,set 方法的 debugger 不是打在 defineReactive 中,而是打在 Watcher 类中的 update 方法中,所执行的回调函数当中,如下图所示:

返回浏览器,打开控制台更改数据触发 set 方法,发布订阅触发 update 方法:

这次也是主要关注调用栈,自己从下依次往上看,就可以看到整个数据驱动界面改变的过程了,这里我就不一一截图了,大家可以自己去看一下,如下图所示:

手撕Vue-数据驱动界面改变中的更多相关文章

  1. 手撕代码:统计1到n二进制数中1出现的总次数

    题目描述: 互娱手撕代码题. 统计从1到n这n个数的二进制表示中1出现的次数. 思路分析: 思路一:直接的做法是从1遍历到n,对于每个数和1做与操作,之后,对于这个数不断做右移操作,不断和1做与操作, ...

  2. 面试中的MySQL主从复制|手撕MySQL|对线面试官

    关注微信公众号[程序员白泽],进入白泽的知识分享星球 前言 作为<手撕MySQL>系列的第三篇文章,今天讲解使用bin log实现主从复制的功能.主从复制也是MySQL集群实现高可用.数据 ...

  3. 如何在vue单页应用中使用百度地图

    作为一名开发人员,每次接到开发任务,我们首先应该先分析需求,然后再思考技术方案和解决方案.三思而后行,这是一个好的习惯. 需求:本项目是采用vue组件化开发的单页应用项目,现需要在项目中引入百度的地图 ...

  4. 手撕公司SSO登陆原理

    Single Sign-on SSO是老生常谈的话题了,但部分同学对SSO可能掌握的也是云里雾里,一知半解.本次手撕公司的SSO登陆原理,试图以一种简单,流畅的形式为你提供 有用的SSO登陆原理. 按 ...

  5. Netty实现高性能IOT服务器(Groza)之手撕MQTT协议篇上

    前言 诞生及优势 MQTT由Andy Stanford-Clark(IBM)和Arlen Nipper(Eurotech,现为Cirrus Link)于1999年开发,用于监测穿越沙漠的石油管道.目标 ...

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

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

  7. NN入门,手把手教你用Numpy手撕NN(一)

    前言 这是一篇包含极少数学推导的NN入门文章 大概从今年4月份起就想着学一学NN,但是无奈平时时间不多,而且空闲时间都拿去做比赛或是看动漫去了,所以一拖再拖,直到这8月份才正式开始NN的学习. 这篇文 ...

  8. 理解vue数据驱动

    vue是双向数据绑定的框架,数据驱动是他的灵魂,他的实现原理众所周知是Object.defineProperty方法实现的get.set重写,但是这样说太牵强外门了.本文将宏观介绍他的实现 使用vue ...

  9. 编译原理--05 用C++手撕PL/0

    前言 目录 01 文法和语言.词法分析复习 02 自顶向下.自底向上的LR分析复习 03 语法制导翻译和中间代码生成复习 04 符号表.运行时存储组织和代码优化复习 05 用C++手撕PL/0 在之前 ...

  10. 剖析手写Vue,你也可以手写一个MVVM框架

    剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...

随机推荐

  1. 人人都会Kubernetes(一):告别手写K8s yaml,运维效率提升500%

    1. Kubernetes的普及和重要性 随着云计算的迅速发展,容器化技术已成为构建和运行分布式应用程序的关键.而Kubernetes作为容器编排领域的佼佼者,已经成为了云原生应用的标准.它不仅简化了 ...

  2. Java 网络编程 —— ServerSocket 详解

    构造 ServerSocket ServerSocket 的构造方法有以下几种重载形式 ServerSocket() throws IOException ServerSocket(int port) ...

  3. AtCoder Beginner Contest 217 D~E

    比赛链接:Here ABC水题, D - Cutting Woods 题意:开始一根木棒长度为 \(n\) 并以 \(1\) 为单位在木棒上标记\((1\sim n)\) ,输出 \(q\) 次操作 ...

  4. AtCoder Beginner Contest 214 (D并查集,E反悔贪心,F公共子序列DP)

    题目链接:Here ABC水题, D - Sum of Maximum Weights 上图中最大权 \(9\) 对答案的贡献是这条边两边的连通块的 size 的乘积再乘以 9 受到上面的启发,我们可 ...

  5. P2241

    这么多年不写代码,竟然忘了longlong这茬,我半天没想明白错在哪里,过了好久才反应过来.浪费不少时间,真的得记住longlong 啊.... Code #include <iostream& ...

  6. windows无法远程访问liunx的mysql解决方案(8.0.27版本)

    一.安装后mysql后发现windows上的无法正常访问,报错如下: 不管是navicat软件,还是使用python的pymsql进行连接 1.navicat软件如下:"Access den ...

  7. 容器网络原理分析:veth 和 network namespace

    1. Liunx veth-pair 和 network namespace Docker 中容器的访问需要依赖 veth-pair 和 network namespace 等技术.network n ...

  8. springboot启动流程 (1) 流程概览

    本文将通过阅读源码方式分析SpringBoot应用的启动流程,不涉及Spring启动部分(有相应的文章介绍). 本文不会对各个流程做展开分析,后续会有文章介绍详细流程. SpringApplicati ...

  9. Cortex-M3内核介绍

    目录 Cortex Vendor - ARM介绍 ARM主要提供指令集,需要授权 ARM使用的RSIC结构,功耗比较低 Cortex M3整体架构 核心是Processor Core - 包含寄存器和 ...

  10. uniapp 子页面 滚动监听 是否到底

    主要属性:  handleScrollToLower <template> <view class="menu"> <scroll-view id=& ...