当前位置: 首页 > news >正文

泰州市网站建设/整站排名优化公司

泰州市网站建设,整站排名优化公司,wordpress点击查看原图,wordpress 淘宝客网站前言 Vue响应式原理由以下三个部分组成: 数据劫持:Vue通过Object.defineProperty()方法对data中的每个属性进行拦截,当属性值发生变化时,会触发setter方法,通知依赖更新。发布-订阅模式:Vue使用发布-订阅…

前言

Vue响应式原理由以下三个部分组成:

  1. 数据劫持:Vue通过Object.defineProperty()方法对data中的每个属性进行拦截,当属性值发生变化时,会触发setter方法,通知依赖更新。
  2. 发布-订阅模式:Vue使用发布-订阅模式来实现数据的响应式更新。当数据发生变化时,会通知依赖进行更新。
  3. 依赖收集:Vue在渲染组件时,会对模板中使用到的数据进行依赖收集,将组件中使用到的数据和对应的Watcher对象建立关联,当数据发生变化时,会通知相关的Watcher对象进行更新。

实现流程

手写Vue响应式原理可以分为以下几个步骤:

  1. 实现Observer类:通过Object.defineProperty()方法对data中的每个属性进行拦截,当属性值发生变化时,会触发setter方法,通知依赖更新。
  2. 实现Dep类:用于管理Watcher对象,包括添加和删除Watcher对象,以及通知Watcher对象进行更新。
  3. 实现Watcher类:用于建立视图与数据之间的联系,当数据发生变化时,会通知Watcher对象进行更新。
  4. 实现Compile类:用于解析模板指令,将指令对应的数据渲染到视图中,并建立视图与数据的联系。
  5. 实现Vue类:将Observer、Watcher、Compile类进行整合,实现Vue的响应式更新机制。

总的来说,手写Vue响应式原理主要由Observer、Dep、Watcher、Compile、Vue这几个组成部分构成。其中Observer用于拦截数据变化,Dep用于管理Watcher对象,Watcher用于建立视图与数据之间的联系,Compile用于解析模板指令,Vue将这些类进行整合,实现了Vue的响应式更新机制。

创建一个Dep类

我们使用递归来遍历数据对象中的所有属性,对每个属性使用Object.defineProperty()方法进行定义。在defineReactive()方法中,我们还创建了一个Dep类,Dep类用于管理所有订阅者(Watcher)和通知它们更新

class Dep {constructor() {this.subs = []; // 存储依赖的数组}// 添加依赖addSub(sub) {if (sub && sub.update) {this.subs.push(sub);}}// 通知依赖更新notify() {this.subs.forEach(sub => {sub.update();});}
}Dep.target = null; // 静态属性 target,用于保存当前的 Watcher 对象

上面的代码定义了一个名为Dep的类,它有以下几个方法:

  1. constructor():构造函数,初始化订阅者数组subs为空数组。
  2. addSub(sub):添加订阅者的方法,将传入的订阅者对象sub添加到subs数组中。
  3. notify():通知所有订阅者更新的方法,遍历subs数组,对每个订阅者调用其update()方法。
  4. target:定义一个全局变量target,用于存储当前的订阅者对象。

在Vue中,每个响应式数据(如data中的属性)都会对应一个Dep对象。当这个属性被读取时,会将当前的订阅者对象存储到Dep.target中,然后在属性的getter方法中,将Dep.target添加到当前属性的Dep对象的订阅者数组中;当属性的值被修改时,会调用该属性的Dep对象的notify()方法,通知所有订阅者更新。

创建一个Watcher类

接下来,我们需要创建一个Watcher类,它的主要作用是在数据发生变化时,触发视图的更新操作。在Watcher类中,我们首先需要保存更新视图所需的回调函数,并将Watcher实例添加到数据的订阅列表中。在数据发生变化时,我们遍历订阅列表,并依次调用回调函数来更新视图。

// 创建一个Watcher类,用于管理依赖与视图的更新
class Watcher {constructor(vm, key, cb) { this.vm = vm; this.key = key; this.cb = cb;// 将当前Watcher实例指定为Dep.target Dep.target = this; // 获取数据的值,触发数据的get方法,从而将当前Watcher实例添加到Dep中 this.oldValue = vm[key]; Dep.target = null;}// 更新视图 
update() {const newValue = this.vm[this.key]; if (this.oldValue === newValue) { return; } this.cb(newValue); this.oldValue = newValue; }
}

创建一个Observer类

Observer 类:该类用于对数据进行监听和响应式处理,主要实现了 walk 和 defineReactive 两个方法。walk 方法遍历对象中所有属性,对每个属性调用 defineReactive 方法进行响应式处理;defineReactive 方法利用 Object.defineProperty 给每个属性添加 getter 和 setter,当属性被访问或修改时,会触发相应的依赖更新。

class Observer {constructor(data) {this.walk(data);}// 对数据对象进行递归遍历,为每个属性添加getter和setterwalk(data) {Object.keys(data).forEach(key => {this.defineReactive(data, key, data[key]);});}defineReactive(obj, key, val) {const dep = new Dep(); // 创建一个依赖收集器Object.defineProperty(obj, key, {enumerable: true, // 可枚举configurable: true, // 可配置get() {// 添加依赖if (Dep.target) {dep.depend();}return val;},set(newVal) {if (val === newVal) {return;}val = newVal;// 触发依赖更新dep.notify();}});}
}

创建一个Compile类

Compile类的代码。在Compile类中,我们首先需要遍历模板中的节点,并根据节点的类型来处理它们。对于普通节点,我们将对它们的文本内容进行处理,对于包含指令的节点,我们将创建Watcher实例,并将它们添加到订阅列表中。

class Compile {constructor(el, vm) {this.el = document.querySelector(el); // 获取根节点this.vm = vm; // 保存 Vue 实例this.compile(this.el); // 编译模板}compile(el) {const childNodes = el.childNodes; // 获取根节点的子节点列表Array.from(childNodes).forEach(node => {if (node.nodeType === 1) { // 元素节点this.compileElement(node);} else if (this.isInterpolation(node)) { // 文本节点且包含插值语法this.compileText(node);}// 递归编译子节点if (node.childNodes && node.childNodes.length > 0) {this.compile(node);}});}compileElement(node) {const attrs = node.attributes; // 获取元素节点的属性列表Array.from(attrs).forEach(attr => {const attrName = attr.name;const exp = attr.value;if (attrName.startsWith("v-")) { // 匹配指令const dir = attrName.substring(2); // 获取指令名称this[dir] && this[dir](node, exp); // 调用对应的指令函数}});}compileText(node) {const exp = node.textContent; // 获取插值语法中的表达式node.textContent = this.getVMValue(exp); // 将插值语法替换为表达式的值}isInterpolation(node) {return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent); // 文本节点且包含插值语法}getVMValue(exp) {let value = this.vm;exp.split(".").forEach(key => {value = value[key];});return value;}// v-model 指令model(node, exp) {this.bind(node, exp, "model");node.addEventListener("input", e => {const newValue = e.target.value;this.setVMValue(exp, newValue);});}// v-bind 指令bind(node, exp, dir) {const updaterFn = this[dir + "Updater"];updaterFn && updaterFn(node, this.getVMValue(exp));new Watcher(this.vm, exp, value => {updaterFn && updaterFn(node, value);});}// model 指令更新视图modelUpdater(node, value) {node.value = value;}// v-text 指令text(node, exp) {this.bind(node, exp, "text");}// text 指令更新视图textUpdater(node, value) {node.textContent = value;}setVMValue(exp, value) {let vm = this.vm;const keys = exp.split(".");keys.forEach((key, index) => {if (index < keys.length - 1) {vm = vm[key];} else {vm[key] = value;}});}
}

Compile 类的实例化需要传入两个参数:el 和 vm。其中,el 是根节点的选择器,vm 是 Vue 实例。

Compile 类主要实现了以下功能:

  1. 遍历根节点及其子节点,对每个元素节点和包含插值语法的文本节点进行编译。
  2. 对于元素节点,遍历其属性列表,匹配指令并调用对应的指令函数进行处理。
  3. 对于包含插值语法的文本节点,替换为表达式的值。
  4. 实现了 v-model、v-bind 和 v-text 指令的处理。
  5. 实现了响应式数据的处理,通过 Watcher 对数据进行监听,数据发生变化时自动更新视图。

创建一个完整的Vue实例

创建一个Vue类,将Observer、Watcher和Compile类组合在一起,以创建一个完整的Vue实例。

class Vue {constructor(options) {this.$options = options;this.$data = options.data;this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el;// 将 Vue 实例的属性代理到 $data 对象上this._proxyData(this.$data);// 创建Observer实例,监听数据变化new Observer(this.$data);// 创建Compile实例,解析模板指令new Compile(this.$el, this);}//使用_proxyData()方法来将数据代理到Vue实例中,就可以在Vue实例中通过this.key的方式来访问数据_proxyData(data) {Object.keys(data).forEach((key) => {Object.defineProperty(this, key, {enumerable: true,configurable: true,get() {return data[key];},set(newValue) {if (newValue === data[key]) {return;}data[key] = newValue;},});});}
}

到此为止,我们已经完成了手写代码来模拟Vue2.0的响应式数据实现的过程。我们可以通过这个过程来深入理解Vue2.0的响应式数据原理,从而更好地应用Vue2.0开发应用程序。

后续会继续更新vue2.0其他源码系列,包括目前在学习vue3.0源码也会后续更新出来,喜欢的点点关注。

相关文章:

深入vue2.x源码系列:手写代码来模拟Vue2.x的响应式数据实现

前言 Vue响应式原理由以下三个部分组成&#xff1a; 数据劫持&#xff1a;Vue通过Object.defineProperty()方法对data中的每个属性进行拦截&#xff0c;当属性值发生变化时&#xff0c;会触发setter方法&#xff0c;通知依赖更新。发布-订阅模式&#xff1a;Vue使用发布-订阅…...

Linux线程控制

本篇我将学习如何使用多线程。要使用多线程&#xff0c;因为Linux没有给一般用户直接提供操作线程的接口&#xff0c;我们使用的接口&#xff0c;都是系统工程师封装打包成原生线程库中的。那么就需要用到原生线程库。因此&#xff0c;需要引入-lpthread&#xff0c;即连接原生…...

【LeetCode】剑指 Offer(20)

目录 题目&#xff1a;剑指 Offer 38. 字符串的排列 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a; 题目&#xff1a;剑指 Offer 38. 字符串的…...

FutureTask中的outcome字段是如何保证可见性的?

最近在阅读FutureTask的源码是发现了一个问题那就是源码中封装结果的字段并没有使用volatile修饰&#xff0c;源码如下&#xff1a;public class FutureTask<V> implements RunnableFuture<V> {/*** 状态变化路径* Possible state transitions:* NEW -> COMPLET…...

直播回顾 | 聚焦科技自立自强,Bonree ONE 助力国产办公自动化平稳替代

3月5日&#xff0c;两会发布《政府工作报告》&#xff0c;强调科技政策要聚焦自立自强。 统计显示&#xff0c;2022年金融信创项目数同比增长300%&#xff0c;金融领域信创建设当前已进入发展爆发期&#xff0c;由国有大型银行逐渐向中小型银行、非银金融机构不断扩展。信创云…...

深入理解Linux进程

进程参数和环境变量的意义一般情况下&#xff0c;子进程的创建是为了解决某个问题。那么解决问题什么问题呢&#xff1f;这个就需要进程参数和环境变量来进行决定的。子进程解决问题需要父进程的“数据输入”(进程参数 & 环境变量)设计原则&#xff1a;3.1 子进程启动的时候…...

Vue3之组件间的双向绑定

何为组件间双向绑定 我们都知道当父组件改变了某个值后&#xff0c;如果这个值传给了子组件&#xff0c;那么子组件也会自动跟着改变&#xff0c;但是这是单向的&#xff0c;使用v-bind的方式&#xff0c;即子组件可以使用父组件的值&#xff0c;但是不能改变这个值。组件间的…...

Java语法基础(一)

目录 代码注释方法 编码规范 基本数据类型及取值范围 变量和常量的声明与赋值 变量 常量 标识符 基本数据类型的使用 整数类型的使用 浮点类型的使用 布尔类型的使用 字符类型的使用 代码注释方法 单行注释&#xff1a;使用“//”进行单行注释多行注释&#xff1a;使…...

优思学院|零质量控制是什么概念?

零质量控制&#xff08;Zero Quality Control&#xff09;是指一个理想的系统&#xff0c;可以生产没有任何缺陷的产品&#xff0c;因此不需要频繁的检查&#xff0c;从而节省时间和金钱。那些追求过程优化并致力于持续过程改进的组织将零质量控制&#xff08;Zero Quality Con…...

2023-03-09 CMU15445-Query Execution

摘要: CMU15445, Project #3 - Query Execution 参考: Project #3 - Query Execution | CMU 15-445/645 :: Intro to Database Systems (Fall 2022) https://github.com/cmu-db/bustub 要求: OVERVIEW At this point in the semester, you have implemented the internal co…...

vuedraggable的使用

Draggable为基于Sortable.js的vue组件&#xff0c;用以实现拖拽功能。 特性 支持触摸设备 支持拖拽和选择文本 支持智能滚动 支持不同列表之间的拖拽 不以jQuery为基础 和视图模型同步刷新 和vue2的国度动画兼容 支持撤销操作 当需要完全控制时&#xff0c;可以抛出所有变化 可…...

双馈风力发电机-900V直流混合储能并网系统MATLAB仿真

MATLAB2016b主体模型&#xff1a;双馈感应风机模块、采用真实风速数据。混合储能模块、逆变器模块、转子过电流保护模块、整流器控制模块、逆变器控制模块。直流母线电压&#xff1a;有功、无功输出&#xff08;此处忘记乘负一信号输出&#xff09;&#xff0c;所以是负的。蓄电…...

leader选举过程

启动electionTimer&#xff0c;进行leader选举。 一段时间没有leader和follower通信&#xff0c;就会超时&#xff0c;开始选举leader过程。有个超时时间&#xff0c;如果到了这个时间&#xff0c;就会触发一个回调函数。具体如下: private void handleElectionTimeout() {boo…...

建造者模式

介绍 Java中的建造者模式是一种创建型设计模式,它的主要目的是为了通过一系列简单的步骤构建复杂的对象,允许创建复杂对象的不同表示形式,同时隐藏构造细节.它能够逐步构建对象,即先创建基本对象,然后逐步添加更多属性或部件,直到最终构建出完整的对象. 该模式的主要思想是将…...

IO与NIO区别

一、概念 NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。 二、NIO和IO的主要区别 下表总结了Java I…...

无监督循环一致生成式对抗网络:PAN-Sharpening

Unsupervised Cycle-Consistent Generative Adversarial Networks for Pan Sharpening &#xff08;基于无监督循环一致生成式对抗网络的全色锐化&#xff09; 基于深度学习的全色锐化近年来受到了广泛的关注。现有方法大多属于监督学习框架&#xff0c;即对多光谱&#xff0…...

ArrayList源码分析(JDK17)

ArrayList类简介类层次结构构造无参构造有参构造添加元素add&#xff1a;添加/插入一个元素addAll:添加集合中的元素扩容mount与迭代器其他常见方法不常见方法不常见方法的源码和小介绍常见方法的源码和小介绍积累面试题ArrayList是什么&#xff1f;可以用来干嘛&#xff1f;Ar…...

数字IC/FPGA面试笔试准备(自用待填坑)

文章目录 前言常见的IC问题数字电路基础问题Verilog & SV跨时钟域信号处理类综合与时序分析类低功耗方法STA(静态时序分析)RTL设计(包含手撕代码)总线问题AXIAPBAHB体系结构的问题RISCV的问题一些笔试选择题前言 这是实验室师兄面试过程中整理的面试和笔试题目,目前只有题…...

基于多任务融合的圣女果采摘识别算法研究

基于多任务融合的圣女果采摘识别算法研究 1、简介 本文主要解决圣女果生产销售环节中&#xff0c;现有的流程是采摘成熟的圣女果&#xff0c;再对采摘下的果实进行单独的品质分级&#xff0c;不仅费时费力&#xff0c;而且多增加一个环节&#xff0c;也增加了对果实的二次伤害…...

又一个开源第一!飞桨联合百舸,Stable Diffusion推理速度遥遥领先

AIGC(AI Generated Content)&#xff0c;即通过人工智能方法生成内容&#xff0c;是当前深度学习最热门的方向之一。其在绘画、写作等场景的应用也一直层出不穷&#xff0c;其中&#xff0c;AI绘画是大家关注和体验较多的方向。 Diffusion系列文生图模型可以实现AI绘画应用&…...

数据链路层及交换机工作原理

目录 一&#xff0c;帧格式 1.1 帧头类型字段的作用 1.2 MAC地址 1.3 MTU值 二&#xff0c;交换机工作原理 2.1 交换机的端口 2.2 端口状态 三&#xff0c;交换机基本工作模式及命令 3.1 交换机的工作模式&#xff1a; 3.2 命令 一&#xff0c;帧格式 其中类型是指&am…...

VSCode 开发配置,一文搞定(持续更新中...)

一、快速生成页面骨架 文件 > 首选项 > 配置用户代码片段 选择需要的代码片段或者创建一个新的&#xff0c;这里以 vue.json 举例&#xff1a; 下面为我配置的代码片段&#xff0c;仅供参考&#xff1a; {"Print to console": {"prefix": "…...

全网最详细的(CentOS7)MySQL安装

一、环境介绍 操作系统&#xff1a;CentOS 7 MySQL&#xff1a;5.7 二、MySQL卸载 查看软件 rpm -qa|grep mysql 卸载MySQL yum remove -y mysql mysql-libs mysql-common rm -rf /var/lib/mysql rm /etc/my.cnf 查看是否还有 MySQL 软件&#xff0c;有的话继续删除。 软件卸…...

基于LSTM的文本情感分析(Keras版)

一、前言 文本情感分析是自然语言处理中非常基本的任务&#xff0c;我们生活中有很多都是属于这一任务。比如购物网站的好评、差评&#xff0c;垃圾邮件过滤、垃圾短信过滤等。文本情感分析的实现方法也是多种多样的&#xff0c;可以使用传统的朴素贝叶斯、决策树&#xff0c;…...

2023年全国最新机动车签字授权人精选真题及答案17

百分百题库提供机动车签字授权人考试试题、机动车签字授权人考试预测题、机动车签字授权人考试真题、机动车签字授权人证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 三、多选题 1.注册登记安全检验时&#xff0c;送检乘用…...

PowerShell远程代码执行漏洞(CVE-2022-41076)分析与复现

漏洞概述PowerShell&#xff08;包括Windows PowerShell和PowerShell Core&#xff09;是微软公司开发的任务自动化和配置管理程序&#xff0c;最初只是一个 Windows 组件&#xff0c;由命令行 shell 和相关的脚本语言组成。后于2016年8月18日开源并提供跨平台支持。PowerShell…...

Mybatis中的一级缓存和二级缓存

Mybatis作为一款强大的ORM框架&#xff0c;其中也用到了缓存来加速查询&#xff0c;今天我们一起来探讨下。 Mybatis可以使用懒加载来提高查询的效率&#xff0c;并且可以通过缓存来提高查询的效率。其中包括有一级缓存和二级缓存。 一级缓存是sqlSession级别的缓存&#xff0c…...

【Java】SpringBoot中实现异步编程

前言 首先我们来看看在Spring中为什么要使用异步编程&#xff0c;它能解决什么问题&#xff1f; 什么是异步&#xff1f; 首先我们先来看看一个同步的用户注册例子&#xff0c;流程如下&#xff1a; 异步的方式如下&#xff1a; 在用户注册后将成功结果返回&#xff0c;…...

ASCII 文件与 TIFF 文件互转(Python 实现)(2023/03/09)

ASCII 文件与 TIFF 文件互转&#xff08;Python 实现&#xff09; 文章目录ASCII 文件与 TIFF 文件互转&#xff08;Python 实现&#xff09;1. 环境1.1 Linux1.2 Windows2. 代码1. 环境 1.1 Linux $ pip3 install --index-url https://mirrors.aliyun.com/pypi/simple --tru…...

思科模拟器 | 交换机与路由器的配置汇总【收藏备用】

文章目录一、vlan配置【实现同一vlan的主机通信】1、基本配置和接线2、vlan配置与端口连接3、测试连接二、truck配置【实现连接在不同交换机上的同一vlan的主机通信】1、基本配置和接线2、vlan配置与端口连接3、打truck做连接3、测试连接三、静态路由配置1、自定义IP地址2、基本…...