位置: 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不支持缩略图和水印怎么办

  • 浅谈旅游景点的微博营销3大策略(浅谈旅游景点的营销策略)

    浅谈旅游景点的微博营销3大策略(浅谈旅游景点的营销策略)

  • 支付宝怎么查身份证办理进度(支付宝怎么查看证件照片)

    支付宝怎么查身份证办理进度(支付宝怎么查看证件照片)

  • 卡2开数据卡1就没信号了(卡2开数据卡1就没信号了华为)

    卡2开数据卡1就没信号了(卡2开数据卡1就没信号了华为)

  • 华为p40空调遥控器怎么开(华为p40空调遥控器怎么设置定时关机)

    华为p40空调遥控器怎么开(华为p40空调遥控器怎么设置定时关机)

  • 光纤线可以代替网线吗(光纤线可以代替电话线吗)

    光纤线可以代替网线吗(光纤线可以代替电话线吗)

  • 朋友圈最多翻到多少天(朋友圈最多翻到朋友多少天动态)

    朋友圈最多翻到多少天(朋友圈最多翻到朋友多少天动态)

  • 在设置中切换输出设备是什么意思(在设置中切换输出设备怎么弄)

    在设置中切换输出设备是什么意思(在设置中切换输出设备怎么弄)

  • 乐视x620是什么型号(乐视x622)

    乐视x620是什么型号(乐视x622)

  • 微信读书属于阅文集团吗(微信读书是谁开发的)

    微信读书属于阅文集团吗(微信读书是谁开发的)

  • 全景声和5.1声道有什么区别(全景声和5.1声道哪个好听)

    全景声和5.1声道有什么区别(全景声和5.1声道哪个好听)

  • 注销微信会在别人的列表删除么(注销微信会在别人手机吗)

    注销微信会在别人的列表删除么(注销微信会在别人手机吗)

  • 华为nova7和荣耀30有什么区别(华为nova7和荣耀30的区别)

    华为nova7和荣耀30有什么区别(华为nova7和荣耀30的区别)

  • 京东实名认证审核多久(京东实名审核)

    京东实名认证审核多久(京东实名审核)

  • 老年机充电显示出错怎么办(老年机充电显示充电出错)

    老年机充电显示出错怎么办(老年机充电显示充电出错)

  • 华为手机屏幕变成黑色怎么恢复(华为手机屏幕变绿色了怎么回事)

    华为手机屏幕变成黑色怎么恢复(华为手机屏幕变绿色了怎么回事)

  • oppor11s运行内存越来越少怎么办 (oppor11s运行内存多少)

    oppor11s运行内存越来越少怎么办 (oppor11s运行内存多少)

  • 对方微信号被限制登录是什么意思啊(对方微信号被限制登录多久能解封)

    对方微信号被限制登录是什么意思啊(对方微信号被限制登录多久能解封)

  • opporeno后面的绿点是什么(opporenoz后面的绿点)

    opporeno后面的绿点是什么(opporenoz后面的绿点)

  • 手机插上耳机有声音拔掉没有了是什么原因(手机插上耳机有滋滋的声音是手机的问题吗)

    手机插上耳机有声音拔掉没有了是什么原因(手机插上耳机有滋滋的声音是手机的问题吗)

  • 土豆视频大王卡免流吗(大王卡优酷视频免流量吗)

    土豆视频大王卡免流吗(大王卡优酷视频免流量吗)

  • 手机短号怎么打(手机短号怎么打电话)

    手机短号怎么打(手机短号怎么打电话)

  • 苹果xs参数配置详情(苹果xs参数配置尺寸)

    苹果xs参数配置详情(苹果xs参数配置尺寸)

  • 苹果x怎么设置拍照静音(苹果x怎么设置指纹密码锁屏)

    苹果x怎么设置拍照静音(苹果x怎么设置指纹密码锁屏)

  • 饿了吗可以异地订餐吗(饿了吗可以异地点外卖给朋友吗)

    饿了吗可以异地订餐吗(饿了吗可以异地点外卖给朋友吗)

  • 小米ml4lte是全网通吗(小米mi4lte支持电信卡吗)

    小米ml4lte是全网通吗(小米mi4lte支持电信卡吗)

  • 荣耀20怎么退出后台(荣耀20怎么退出纯净模式)

    荣耀20怎么退出后台(荣耀20怎么退出纯净模式)

  • 滴滴如何删除地址推荐(滴滴如何删除地点记录)

    滴滴如何删除地址推荐(滴滴如何删除地点记录)

  • 跨年度的租金收入如何入账
  • 累计预扣法计算公式
  • 单位当月未扣社保怎么处理分录?
  • 企业所得税视同销售的会计处理
  • 残保金计入管理费用还是营业税金及附加
  • 季节性临时工是不是可以不上社保
  • 总公司如何分配股份
  • 管理费用怎么结转成本
  • 缴纳个人社保在哪里可以缴
  • 预收和应收可以冲销吗
  • 小微企业城建税优惠政策2022
  • 支付货款未收到产品算货值吗
  • 不动产租赁发票怎么开
  • 个体行业哪些不需要交税
  • 招行理财产品哪些可靠
  • 继承安置房需要过户吗?
  • 租赁场地费入什么科目
  • 增值税季度报税
  • 企业接受大额捐赠,可以分次确认收入缴纳企业所得税吗?
  • 旅游业是一个怎样的产业
  • 企业会计准则基本准则的主要内容
  • key介质费入哪个科目?
  • 同期对比和同比增长
  • 以前年度损益调整怎么做账
  • 预收款项的减少
  • acer笔记本如何关闭键盘数字键
  • 最早在哪一年手机可以上网
  • 补缴以前年度公积金误入当年费怎么办
  • 国有土地使用证到期了怎么办
  • php7异常处理
  • php文本分割
  • ts基础类型
  • vite vuex
  • 鬓角头发剃光了多久能长好
  • 基于中国国情的中国式现代化具有哪些特色
  • bert模型能做什么
  • opencv 方框识别
  • nfs4挂载
  • 进口关税账务处理办法
  • 增值税发票扩版申请说明
  • 在sysservers中找不到服务器
  • 发票隔月可以重开吗
  • 企业研发费用率怎么算
  • 小规模纳税人劳务派遣可以差额征税吗
  • 出口不报关账务处理
  • 以前年度漏扣个税怎么处理
  • 商业地产收税
  • 固定资产多入账怎么写情况说明
  • 收到税务局退增值税怎么入账
  • 年底销项税额和进项税额要转出吗
  • 手工现金日记账范本
  • 会计账簿按账页不同可以分为
  • 原始凭证的审核要求有哪些
  • vim入门教程
  • centos安装后配置
  • docker部署zookeeper集群
  • ghost还原文件
  • gwservice进程
  • linux 操作系统
  • explorer.exe进程出错
  • reald是什么格式
  • Linux中SSH的SCP传输命令运用详解
  • linux系统叫啥
  • win8怎么用
  • [置顶] 关于在vs2013中配置opengl红宝书第八版环境
  • jquery点击切换div
  • windows安装node.js
  • javascript语言介绍
  • jquery操作数组
  • document.getElementById()为null
  • 适合当前端的手机
  • Unity for Absolute Beginners(二)
  • 法线贴图使用
  • 安徽省马鞍山地区代码
  • 税务局的经济类型
  • 企业所得税税率10%
  • 重庆国税电子税务局手机版
  • 12366纳税服务热线工作时间
  • 浙江省个人医保累计自费多少可以报销
  • 税务总局2013年65号公告
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设