位置: IT常识 - 正文

模拟Vue实现响应式数据(vue实现响应式的原理)

编辑:rootadmin
1. 预期效果 当数据变动时,触发自定义的回调函数。 2. 思路 对对象 object 的 setter 进行设置,使 setter 在赋值之后执行回调函数 callback()。 3.细节 3.1 设置 setter 和 getter JS提供了 1. 预期效果

推荐整理分享模拟Vue实现响应式数据(vue实现响应式的原理),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue怎么做响应式,vue 响应,vue如何实现响应式,vue如何实现响应式,vue2.0响应式原理,vue怎么写响应式布局,vue3.0响应式原理,vue怎么写响应式布局,内容如对您有帮助,希望把文章链接给更多的朋友!

当数据变动时,触发自定义的回调函数。

2. 思路

对对象 object 的 setter 进行设置,使 setter 在赋值之后执行回调函数 callback()。

3.细节3.1 设置 setter 和 getter

JS提供了 [Object.defineProperty()](Object.defineProperty() - JavaScript | MDN (mozilla.org)) 这个API来定义对象属性的设置,这些设置就包括了 getter 和 setter。注意,在这些属性中,如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。

Object.defineProperty(obj, "key", { enumerable: false, // 是否可枚举 configurable: false, // 是否可配置 writable: false, // 是否可写 value: "static"});

我们可以利用JS的 [闭包](闭包 - JavaScript | MDN (mozilla.org)),给 getter 和 setter 创造一个共同的环境,来保存和操作数据 value 和 callback 。同时,还可以在 setter 中检测值的变化。

// task1.jsconst defineReactive = function(data, key, value, cb) { Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { console.log('getter') return value }, set(newValue) { if (newValue !== value) { value = newValue console.log('setter: value change') cb(newValue) } } });}const task = function() { console.log('running task 1...') const obj = {} const callback = function(newVal) { console.log('callback: new value is ' + newVal) } defineReactive(obj, 'a', 1, callback) console.log(obj.a) obj.a = 2 obj.a = 3 obj.a = 4}task()

至此我们监控了 value ,可以感知到它的变化并执行回调函数。

模拟Vue实现响应式数据(vue实现响应式的原理)

3.2 递归监听对象的值

上面的 defineRective() 在 value 为对象的时候,当修改深层键值,则无法响应到。因此通过循环递归的方法来对每一个键值赋予响应式。这里可以通过 observe() 和 Observer 类来实现这种递归:

// observe.jsimport { Observer } from "./Observer.js"// 为数据添加响应式特性export default function(value) { console.log('type of obj: ', typeof value) if (typeof value !== 'object') { // typeof 数组 = object return } if (typeof value.__ob__ !== 'undefined') { return value.__ob__ } return new Observer(value)}// Observer.jsimport { defineReactive } from './defineReactive.js'import { def } from './util.js';export class Observer { constructor(obj) { // 注意设置成不可枚举,不然会在walk()中循环调用 def(obj, '__ob__', this, false) this.walk(obj) } walk(obj) { for (const key in obj) { defineReactive(obj, key) } }}

在这里包装了一个 def() 函数,用于配置对象属性,把 __ob__ 属性设置成不可枚举,因为 __ob__ 类型指向自身,设置成不可枚举可以放置遍历对象时死循环

// util.jsexport const def = function(obj, key, value, enumerable) { Object.defineProperty(obj, key, { value, enumerable, writable: true, configurable: true })}3.3 检测数组

从需求出发,对于响应式,我们对数组和对象的要求不同,对于对象,我们一般要求检测其成员的修改;对于数组,不仅要检测元素的修改,还要检测其增删(比如网页中的表格)

对由于数组没有 key ,所以不能通过 defineReactive() 来设置响应式,同时为了满足响应数组的增删改,所以 Vue 的方法是,通过包装 Array 的方法来实现响应式,当调用 push()、poll()、splice() 等方法时,会执行自己设置的响应式方法

使用 Object.create(obj) 方法可以 obj 对象为原型(prototype)创建一个对象,因此我们可以以数组原型 Array.prototype 为原型创建一个新的数组对象,在这个对象中响应式包装原来的 push()、pop()、splice()等数组

// array.jsimport { def } from "./util.js"export const arrayMethods = Object.create(Array.prototype)const methodNameNeedChange = [ 'pop', 'push', 'splice', 'shift', 'unshift', 'sort', 'reverse']methodNameNeedChange.forEach(methodName => { const original = Array.prototype[methodName] def(arrayMethods, methodName, function() { // 响应式处理 console.log('call ' + methodName) const res = original.apply(this, arguments) const args = [...arguments] let inserted = [] const ob = this.__ob__ switch (methodName) { case 'push': case 'unshift': inserted = args case 'splice': inserted = args.slice(2) } ob.observeArray(inserted) return res })})// Observer.jsimport { arrayMethods } from './array.js'import { defineReactive } from './defineReactive.js'import observe from './observe.js'import { def } from './util.js'export class Observer { constructor(obj) { console.log('Observer', obj) // 注意设置成不可枚举,不然会在walk()中循环调用 def(obj, '__ob__', this, false) if (Array.isArray(obj)) { // 将数组方法设置为响应式 Object.setPrototypeOf(obj, arrayMethods) this.observeArray(obj) } else { this.walk(obj) } } // 遍历对象成员并设置为响应式 walk(obj) { for (const key in obj) { defineReactive(obj, key) } } // 遍历数组成员并设置为响应式 observeArray(arr) { for (let i = 0, l = arr.length; i < l; i++) { observe(arr[i]) } }}3.5 Watcher 和 Dep 类

设置多个观察者检测同一个数据

// Dep.jsvar uid = 0export default class Dep { constructor() { this.id = uid++ // console.log('construct Dep ' + this.id) this.subs = [] } addSub(sub) { this.subs.push(sub) } depend() { if (Dep.target) { if (this.subs.some((sub) => { sub.id === Dep.target.id })) { return } this.addSub(Dep.target) } } notify() { const s = this.subs.slice(); for (let i = 0, l = s.length; i < l; i++) { s[i].update() } }}// Watcher.jsimport Dep from "./Dep.js"var uid = 0export default class Watcher { constructor(target, expression, callback) { this.id = uid++ this.target = target this.getter = parsePath(expression) this.callback = callback this.value = this.get() } get() { Dep.target = this const obj = this.target let value try { value = this.getter(obj) } finally { Dep.target = null } return value } update() { this.run() } run() { this.getAndInvoke(this.callback) } getAndInvoke(cb) { const obj = this.target const newValue = this.get() if (this.value !== newValue || typeof newValue === 'object') { const oldValue = this.value this.value = newValue cb.call(obj, newValue, newValue, oldValue) } }}function parsePath(str) { var segments = str.split('.'); return (obj) => { for (let i = 0; i < segments.length; i++) { if (!obj) return; obj = obj[segments[i]] } return obj; };}// task2.jsimport observe from "../observe.js";import Watcher from "../Watcher.js";const task2 = function() { const a = { b: { c: { d: { h: 1 } } }, e: { f: 2 }, g: [ 1, 2, 3, { k: 1 }] } const ob_a = observe(a) const w_a = new Watcher(a, 'b.c.d.h', (val) => { console.log('1111111111') }) a.b.c.d.h = 10 a.b.c.d.h = 10 console.log(a)}task2()

执行结果如下,可以看到成功响应了数据变化

本文链接地址:https://www.jiuchutong.com/zhishi/313197.html 转载请保留说明!

上一篇:dedecms自动生成RSS和网站地图(dedecms采集怎么用)

下一篇:phpcms不支持缩略图和水印怎么办

  • 第五人格怎么玩(第五人格怎么玩新手教学)

    第五人格怎么玩(第五人格怎么玩新手教学)

  • 情侣空间解除多少天清零天数(情侣空间解除多少次就不能恢复了)

    情侣空间解除多少天清零天数(情侣空间解除多少次就不能恢复了)

  • 华为GT心率灯能不能关(华为gt心率灯能不能关)

    华为GT心率灯能不能关(华为gt心率灯能不能关)

  • openstack和vmware区别(openstack和虚拟机的区别)

    openstack和vmware区别(openstack和虚拟机的区别)

  • 三星黑屏震动开不了机(三星黑屏震动开关在哪)

    三星黑屏震动开不了机(三星黑屏震动开关在哪)

  • 电视qq音乐网络歌曲链接无效(电视qq音乐网络连接失败)

    电视qq音乐网络歌曲链接无效(电视qq音乐网络连接失败)

  • 公屏是什么意思(抖音展开公屏是什么意思)

    公屏是什么意思(抖音展开公屏是什么意思)

  • 笔记本电脑没有电池能开机吗(笔记本电脑没有wifi连接图标不见了)

    笔记本电脑没有电池能开机吗(笔记本电脑没有wifi连接图标不见了)

  • 蓝牙双耳机不能一起响(双耳的蓝牙耳机为什么不能一起播放音乐了)

    蓝牙双耳机不能一起响(双耳的蓝牙耳机为什么不能一起播放音乐了)

  • 电子秤怎么充电(tcl电子秤怎么充电)

    电子秤怎么充电(tcl电子秤怎么充电)

  • 微信朋友圈发图片怎么配音乐(微信朋友圈发图片的方法)

    微信朋友圈发图片怎么配音乐(微信朋友圈发图片的方法)

  • 赛扬和酷睿的区别(赛扬和酷睿哪个性能好)

    赛扬和酷睿的区别(赛扬和酷睿哪个性能好)

  • pchm10是什么型号手机(pchm10是什么手机)

    pchm10是什么型号手机(pchm10是什么手机)

  • 小米手机横屏竖屏怎么调整(小米手机横屏竖屏设置)

    小米手机横屏竖屏怎么调整(小米手机横屏竖屏设置)

  • 看情侣空间能不能隐身(情侣空间可以看访客吗)

    看情侣空间能不能隐身(情侣空间可以看访客吗)

  • 小米9支持lhdc吗(小米九支持lhdc吗)

    小米9支持lhdc吗(小米九支持lhdc吗)

  • 怎么激活苹果11(怎么激活苹果14pro)

    怎么激活苹果11(怎么激活苹果14pro)

  • 手机淘宝查号在哪里查(淘宝号查询在哪里查询)

    手机淘宝查号在哪里查(淘宝号查询在哪里查询)

  • 文本框怎么居中对齐(文本框怎么居中显示)

    文本框怎么居中对齐(文本框怎么居中显示)

  • icloud内存满了怎么删(icloud内存满了怎么看照片)

    icloud内存满了怎么删(icloud内存满了怎么看照片)

  • vivo怎么看电话卡的号码(vivo怎么看电话录音)

    vivo怎么看电话卡的号码(vivo怎么看电话录音)

  • w10没激活有什么坏处(w10没有激活会不会影响上网)

    w10没激活有什么坏处(w10没有激活会不会影响上网)

  • 投诉了骑手骑手知道吗(投诉了骑手骑手停职了会报复吗)

    投诉了骑手骑手知道吗(投诉了骑手骑手停职了会报复吗)

  • 华为nova5的电池容量是多少(华为nova5的电池容量)

    华为nova5的电池容量是多少(华为nova5的电池容量)

  • 微信视频连接失败是怎么回事(微信视频连接失败对方可以收到吗)

    微信视频连接失败是怎么回事(微信视频连接失败对方可以收到吗)

  • 开发听书app包括哪些功能(听书开源app)

    开发听书app包括哪些功能(听书开源app)

  • 手机流量设置在哪里找(手机设置流量在哪)

    手机流量设置在哪里找(手机设置流量在哪)

  • 上网速度变慢甚至打不开网页如何防止别人蹭我们的网(网速变慢了)

    上网速度变慢甚至打不开网页如何防止别人蹭我们的网(网速变慢了)

  • 小规模纳税人和一般纳税人哪个划算
  • 企业中秋晚会活动方案
  • 销售收入不含税怎么计算增值税额
  • 企业所得税税前扣除管理办法
  • 个人所得税如何查询工资
  • 结转未交增值税会计科目怎么写
  • 增值税专用发票有效期是多长时间
  • 17点是多少点
  • 经营范围预付卡是什么
  • 零售商业企业经营的特点主要在于
  • 有两种税率的如何填申报表
  • 变更企业性质企业怎么办
  • 非福利企业残疾人用工优惠
  • 企业利息收入会计分录怎么做
  • 承兑汇票私人贴现账务怎么处理
  • 发票勾选 发票认证
  • 资本公积转增资本会引起什么变化
  • 增值税专用发票和普通发票的区别
  • 财务费用利息收入在损益表中怎么填
  • 留存收益账务处理视频
  • 个人向公司借贷需要交税吗
  • 国税退税怎么做账
  • 疫情期间企业应该承担哪些责任
  • 营业外收入属于什么会计要素
  • 土地开垦费怎么征收
  • 土地无形资产摊销年限怎么确定
  • 合伙人退伙资产清算需要交税吗
  • 跨年应收账款账务错误怎么调整
  • 工龄补偿款
  • 要看网怎么找
  • 电脑故障检测与维护方法
  • 贴现短期无息应付票据
  • Linux下使用quota命令管理磁盘空间的实例教程
  • 无私有住房证明怎么开
  • 公司出租房屋如何给对方单位开发票
  • 银行本票存款和银行存款的区别
  • 固定资产管理系统多少钱
  • 建筑业预缴的个人所得税怎么在申报表体现
  • 基于网络创新形成的大数据的最突出特征是什么?( )
  • php怎么修改当前用户的密码
  • phpcms数据库文件
  • SQL SERVER 2008 64位系统无法导入ACCESS/EXCEL怎么办
  • 增值税抵扣比例是多少
  • 固定资产投资额在财务报表中怎么体现
  • 什么情况下需要做心脏造影
  • 公允价值怎么读
  • 收到以前年度增值税退税账务处理怎么做账
  • 债权投资相关科目
  • 简易征收应纳税额
  • 财务费用为什么是向债权人支付的现金流量
  • 进项税怎么记账
  • 预付款属于什么会计分录
  • 运费少给怎么办
  • 工程违约金账务处理规定
  • 企业设备维修
  • 计提工资的会计处理
  • 开房租租赁发票怎么记账?
  • 应付账款和预收账款是负债吗
  • 净资产总计等于什么
  • 企业没有期初数据,会计怎么做账
  • mysql复制命令
  • xp系统许多网页打不开
  • 安装win7系统需要注意什么
  • win10日历怎么设置
  • centos安装图形oracle11g
  • centos inode
  • dvd-rom drive是什么意思
  • sesvc.exe是什么
  • linux tar -zxf
  • win7电脑屏幕设置常亮不黑屏
  • 如何进行js 的测试
  • 简介生成
  • 请问木瓜
  • 浅谈Jquery中Ajax异步请求中的async参数的作用
  • 企业所得税税率10%
  • 耕地占用税 湖北
  • 党费减免规定
  • 红星新闻河南郑州
  • 普洱房产管理局官网
  • 如何鼓励互联网企业发展
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设