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

手写vue(二)响应式实现

名词解释:

vm:指Vue实例

一、目标效果

  1. vue定义

(1)新建vm时,可以通过一个data对象,或者data函数,其属性可以通过vm直接访问,而data对象可以通过vm._data获取

(2)修改vm._data.xxx时,等价于修改this.xxx

  1. 响应式对象

(1)能够监听到data对象属性的修改

(2)能够监听到data下对象属性的属性

  1. 响应式数组

(1)能够监听到通过数组方法修改数组

(2)数组中保存的对象,也能被监测修改

(3)不监听数组通过下标修改

测试先行:

    <script src="../dist/vue.js"></script><script>var vm = new Vue({data() {return {student: {name: 'Chicken',age: 17},value: 12,hobby: ['Sing', 'Jump', 'Rap', {play: ['basketball']}]}},})console.log(vm)console.log('vm.value === vm._data.value:',vm.value === vm._data.value);console.log('1、修改data下属性,可触发响应式:');vm.value = 15console.log('2、修改data内对象属性,可触发响应式:');vm.student.name = 'lisi'console.log('3、使用数组方法,可触发响应式:');vm.hobby.push('football')console.log('4、直接通过数组下标修改数组,不触发响应式:');vm.hobby[3] = 'football'</script>

二、Vue实例定义

  1. 参考Vue源码,使用构造函数的方式,通过创建传入一个配置对象生成实例

  1. option传给vue之后,直接通过this.$option挂载到示例上,方便后面,读取配置项进行初始化

  1. 初始化单独在一个init文件中进行

import init from "./init"function Vue(option) {// 挂载到vms上this.$option = optioninit(this)
};export default Vue

三、在Vue实例中定义data属性

data中的值,可以通过多个地方修改,但修改的结果时同步的,此时就不能直接粗暴地挂载在vm上:例如:

要把data.foo挂载到this.foo,如果直接使用this.foo = data.foo,则执行this.foo = newVal之后,data.foo还是旧的值。

由上面的反例可得,我们要挂载的内容不是foo的值,而是data的foo这个属性,通过this.foo修改值,应该同步到data.foo上去

通过Object.defineProperty可以动态地给对象添加属性,读取的值和返回的值都可以通过getter、setter自己定义,所以我们获取、修改值时,直接去操作data对象。

// init.js
function initData(vm) {const opt = vm.$optionlet data = opt.dataif (typeof data === 'function') {data = data()}// 在vm上定义_data和data中的数据defineGlobal(vm, '_data', { _data: data })Object.keys(data).forEach(key => {defineGlobal(vm, key, data)})
}/*** 在vm上定义代理对象* @param {*} vm vue实例* @param {*} key 需要在实例上定义的key* @param {*} source 需要被代理的源对象*/
function defineGlobal(vm, key, source) {Object.defineProperty(vm, key, {configurable: true,enumerable: true,get() {return source[key]},set(v) {source[key] = v}})
}

四、对象监听

与vue挂载data类似,可以使用Object.defineProperty去代理data中的所有属性,并且递归地去代理所有的子属性,通过setter就可以监听属性的修改。

具体实现:创建一个Observer类,需要监听的对象作为参数传入构造函数,然后在这个类的构造函数中去给对象添加响应式。添加响应式监听之后,把类实例对象挂到监听对象中去,作为一个已经被监听的标志位,也直接使用监听的相关方法,否则,其实直接通过方法处理就足够了,并不需要作为一个类。

// init.js
function initData(vm) {...// 观察data中内容observe(data)...
}// observe/index.js
class Observer {constructor(obj) {if (Array.isArray(obj)) {...} else {this.observeObj(obj)}// 在被监听的对象上,定义已被监听的标志位,指向Observer对象自身// 在其它文件中,也方便直接使用监听的相关方法Object.defineProperty(obj, '__ob__', {value: this})}/*** 监听对象* @param {Object} obj 需要观察的对象*/observeObj(obj) {Object.keys(obj).forEach(key => {const val = obj[key]// 尝试监听对象的属性observe(val)// 定义响应式defineReactive(obj, key)})}
}// 导出的方法/*** * @param {Object} obj 需要监听的对象* @returns */
export function observe(obj) {if (typeof obj !== 'object') {return null}if (obj.__ob__) {return obj.__ob__}return new Observer(obj)
}/*** 给对象添加响应式* @param {Object} obj 需要定义响应式的对象* @param {String} key 对象的属性key*/
export function defineReactive(obj, key) {let value = obj[key]Object.defineProperty(obj, key, {configurable: true,enumerable: true,get() {return value},set(val) {console.log('set data', obj, key, val);value = val}})
}

五、数组监听

由于数组长度可能比较大,如果对数组的每个对象都进行响应式监听,则性能损耗太高,比如数组长度为1万,则需要对一万个属性的进行响应式监听:Object.defineProperty(arr, i, {})

由于数组对数组进行操作,主要时通过push、pop、splice等等方法去修改的,通过下标直接修改比较少,因此只监听数组的方法调用进行监听。

具体实现:

举例,我们要监听对象arr的push方法,不能直接重写原型对象方法arr.__proto__.push = XXX,因为arr._proto指向的是Array.prototype, 直接修改后,所有数组对象的push方法都会被修改

利用原型链中会逐级查找属性的特点,我们可以创建一个对象,在这个对象中重新定义push等方法,然后,需要监听的数组,只需要原型对象指我们创建的数组原型重写对象,就可以实现监听

而这个对象的原型对象应该指向Array对象,因为我们并不需要重写所有的数组方法,而是只需要重写一部分会修改原数组的方法,并且,我们重新定义的方法,最后也是要调用Array中的原方法是实现方法逻辑的,相当于对方法进行了一层代理,每次调用时,能够通知到我们,做一些操作。

// array.js
// 需要监听的方法
const observeMethods = ['push','pop','shift','unshift','splice','sort','reverse'
]
const arrayProto = Array.prototype
// 创建一个空对象,原型对象指向Array的原型属性上
const obArrayProto = Object.create(arrayProto)// 重写需要监听的方法
observeMethods.forEach(methodName => {const originalMethod = arrayProto[methodName]obArrayProto[methodName] = function (...args) {let inserted// 新增元素的方法,需要给新元素添加响应式switch (methodName) {case 'push':case 'unshift':inserted = argsbreakcase 'splice':inserted = args.slice(2)breakdefault:break;}const observe = this.__ob__if (inserted) {observe.observeArray(inserted)}console.log(methodName, args);// 需要使用call,因为originalMethod方法没有通过XXX对象去调用,因此,如果直接执行,方法中的this会指向window,在严格模式下会指向undefined// 通过call方法调用,让originalMethod方法内的this指向数组对象,因为这个方法是通过arr.XXX()调用的return originalMethod.call(this, ...args)}})/*** 监听数组方法* @param {Array} arr */
export function observeArrayMethod(arr) {// 数组的原型对象指向重写了部分方法的新的原型对象Object.setPrototypeOf(arr, obArrayProto)
}// ********************** observe/index.js ********************
import { observeArrayMethod } from "./array"class Observer {constructor(obj) {if (Array.isArray(obj)) {this.observeArray(obj)} else {...}...}/*** 监听数组* @param {Array} arr 需要观察的数组*/observeArray(arr) {// 监听数组的修改方法observeArrayMethod(arr)// 尝试监听数组内的所有元素arr.forEach(observe)}
}

总结:

目录结构:

gitee源码地址:

https://gitee.com/ZepngLin/my-vue/tree/%EF%BC%88%E4%BA%8C%EF%BC%89%E5%93%8D%E5%BA%94%E5%BC%8F%E5%AE%9E%E7%8E%B0

相关文章:

手写vue(二)响应式实现

名词解释&#xff1a;vm&#xff1a;指Vue实例一、目标效果vue定义&#xff08;1&#xff09;新建vm时&#xff0c;可以通过一个data对象&#xff0c;或者data函数&#xff0c;其属性可以通过vm直接访问&#xff0c;而data对象可以通过vm._data获取&#xff08;2&#xff09;修…...

mysql数据库常问面试题

1、NOW()和CURRENT_DATE()有什么区别&#xff1f; NOW()命令用于显示当前年份&#xff0c;月份&#xff0c;日期&#xff0c;小时&#xff0c;分钟和秒。 CURRENT_DATE()仅显示当前年份&#xff0c;月份和日期。 2、CHAR和VARCHAR的区别&#xff1f; &#xff08;1&#xff09…...

AI风暴 :文心一言 VS GPT-4

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 文心一言 VS GPT-4 文心一言&#xff1a;知识增强大语言模型百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#…...

VR全景城市,用720全景树立城市形象,打造3D可视化智慧城市

随着城市化进程的加速&#xff0c;城市之间的竞争也日益激烈。城市管理者们需要寻求新的方式来提升城市的品牌形象和吸引力。在这个过程中&#xff0c;VR全景营销为城市提供了一种全新的营销手段&#xff0c;可以帮助提升城市的价值和吸引力。一、城市宣传新方式VR全景营销是一…...

javaweb窗口服务人员分析评价系统ssh

A&#xff09;后台管理员模块&#xff1a;通过该功能模块&#xff0c;管理员可以修改自己的密码&#xff0c;并对管理员进行添加和删除操作。 B&#xff09;注册用户模块&#xff1a;通过该功能模块&#xff0c;管理员可以查看注册用户的基本信息&#xff0c;对存在问题的用户进…...

树莓派Pico W无线开发板UDP协议MicroPython网络编程实践

树莓派Pico W无线开发板&#xff08;简称Pico W&#xff09;是树莓派基金会于2022年6月底推出的搭载无线通信芯片的树莓派Pico开发板。本文在介绍树莓派Pico W无线开发板接口信号和TCP/IP和UDP通信协议基础上&#xff0c;给出Pico W无线开发板的UDP协议MicroPython网络编程实例…...

跨域解决方案

跨域解决方案 1.跨域基本介绍 文档&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS 跨域问题是什么&#xff1f; 一句话&#xff1a;跨域指的是浏览器不能执行其他网站的脚本&#xff0c;它是由浏览器的同源策略造成的&#xff0c;是浏览器对 javascr…...

springboot的统一处理

在处理网络请求时&#xff0c;有一部分功能是需要抽出来统一处理的&#xff0c;与业务隔开。 登录校验 可以利用spring mvc的拦截器Interceptor&#xff0c;实现HandlerInterceptor接口即可。实现该接口后&#xff0c;会在把请求发给Controller之前进行拦截处理。 拦截器的实…...

C/C++每日一练(20230319)

目录 1. 反转链表 II &#x1f31f;&#x1f31f; 2. 解码方法 &#x1f31f;&#x1f31f; 3. 擅长编码的小k &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 …...

GitHub 上有些什么好玩的项目?

前言 各个领域模块的都整理了一下&#xff0c;包含游戏、一些沙雕的工具、实用正经的工具以及一些相关的电商项目&#xff0c;希望他们可以给你学习的路上增加几分的乐趣&#xff0c;我们直接进入正题~ 游戏 1.吃豆人 一款经典的游戏开发案例&#xff0c;包括地图绘制、玩家控…...

蓝桥杯刷题第十二天

问题描述给定一个正整数 n &#xff0c;请问 n 的十进制表示中末尾总共有几个 0 &#xff1f;输入格式输入一行包含一个正整数 n。输出格式输出一个整数&#xff0c;表示答案。评测用例规模与约定对于所有评测用例&#xff0c;1 < n < 1000000000。运行限制最大运行时间&…...

开发也可以很快乐,让VSCode和CodeGPT带给你幸福感

CodeGPT 是一款 Visual Studio Code 扩展&#xff0c;可以通过官方的 OpenAI API 使用 GPT-3 (预训练生成式转换器) 模型&#xff0c;在多种编程语言中生成、解释、重构和文档化代码片段。CodeGPT 可用于各种任务&#xff0c;例如代码自动完成、生成和格式化。它还可以集成到代…...

【Linux】基本指令介绍

前言从今天开始&#xff0c;我们一起来学习Linux的相关知识&#xff0c;今天先来介绍怎么登录Linux&#xff0c;并且介绍一些Linux的基本指令。使用 XShell 远程登录 Linux很多同学的 Linux 启动进入图形化的桌面. 这个东西大家以后就可以忘记了. 以后的工作中 没有机会 使用图…...

JQuery介绍

文章目录一. JQuery介绍二. JQuery使用三. JQuery选择器四. JQuery选择集过滤五.JQuery选择集转移六. JQuery获取和操作标签内容七. JQuery获取和设置元素属性八. JQuery事件九.JQuery事件代理- 事件冒泡- 事件绑定的问题- 事件代理一. JQuery介绍 定义: jquery是JS的一个函数…...

Selenium基础篇之八大元素定位方式

文章目录前言一、如何进行元素定位&#xff1f;1.右击元素-检查2.F12-选择工具点击元素3.借助selenium IDE二、八大元素定位方式1.ID1.1 方法1.2 举例1.3 代码1.4 截图2.NAME2.1 方法2.2 举例2.3 代码2.4 截图3.CLASS_NAME3.1 方法3.2 举例3.3 代码3.4 截图4.TAG_NAME4.1 方法4…...

C语言的灵魂---指针(基础)

C语言灵魂指针1.什么是指针&#xff1f;2.指针的大小3.指针的分类3.1比较常规的指针类型3.2指针的解引用操作3.3野指针野指针的成因&#xff1a;4.指针运算4.1指针加减整数4.2指针-指针1.什么是指针&#xff1f; 这个问题我们通常解释为两种情况&#xff1a; 1.指针本质&#…...

带你一文透彻学习【PyTorch深度学习实践】分篇——线性模型 梯度下降

分享给大家一段我国著名作家、散文家史铁生先生的一段话: 把路想象的越是坎坷就越是害怕,把山想象的越是险峻就越会胆怯,把别人想象的越是优秀就越是不敢去接近。惯于这样想象的人,是天生谦卑的人。 --------史铁生《关于恐惧》 🎯作者主页:追光者♂🔥 �…...

Javascript如何截取含有表情的字符串

Javascript如何截取含有表情的字符串 一、说说背景 社区社交应用中&#xff0c;难免会有输入用户昵称的操作&#xff0c;如果用户老老实实的输入中文汉字或者英文字母&#xff0c;那当然没啥问题&#xff0c;我们能够轻松的处理字符串的截取&#xff0c;产品说按多少字符截取…...

【云原生】prometheus结合jmx exporter 的java agent模式采集tomcat监控实战

前言 大家好&#xff0c;我是沐风晓月&#xff0c;今天我们又来探讨一款使用prometheus监控tomcat的另外一种形式&#xff1a;Java agent模式。 如果你想使用http server模式&#xff0c;请参考&#xff1a;【云原生】prometheus结合jmx exporter 的http server模式采集tomca…...

深度学习应用技巧总结与pytorch框架下训练过程的记忆技巧

大家好&#xff0c;我是微学AI&#xff0c;今天给大家总结一下深度学习模型训练过程中的一些技巧总结&#xff0c;以及pytorch框架下训练过程的记忆技巧&#xff0c;很有用的干货&#xff0c;理解模型训练过程的步骤&#xff0c;让流程难懂&#xff0c;难记忆的过程变得简单&am…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...