【vue2】框架响应式数据实现的研究分析

数据要在获取时收集依赖,在设置时触发更新依赖,核心方法defineReactive

function defineReactive(data, key, value) {
  // 存放依赖的数组
  let dep = []
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      // 收集依赖,这个依赖就是挂载在全局上的方法
      dep.push(window.target)
      return value
    },
    set(newValue) {
      if (val === newValue) return
      // 赋值时,更新所有的依赖
      for (let i = 0; i < dep.length; i++) {
        dep[i](newValue, val)
      }
      val = newValue
    }
  })
}

封装的收集依赖的辅助方法

class Dep {
  constructor() {
    this.subs = []
  }
  addSub(sub) {
    this.subs.push(sub)
  }
  removeSub(sub) {
    remove(this.subs, sub)
  }
  depend() {
    if (window.target) {
      this.addSub(window.target)
    }
  }
  notify() {
    const subs = this.subs.slice()
    for (let i = 0; i < subs.length; i++) {
      subs[i].update()
    }
  }
}

function remove(arr, item) {
  if (arr.length) {
    let index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}

改写响应式数据劫持的方法

function defineReactive(data, key, value) {
  let dep = new Dep()
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      dep.depend()
      return value
    },
    set(newValue) {
      if (val === newValue) return
      val = newValue
      dep.notify()
    }
  })
}

依赖谁谁?就是上面的 window.target,那么他到底是啥?其实就是要通知用到这个状态的地方,
它有可能是个模块,也有能是一个 watch,那么我们就需要封装这样一个集中处理不同依赖类型的方法,
我们先通知这个方法,然后这个方法内部再去通知其他地方。Watcher

class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm
    this.getter = parsePath(expOrFn)
    this.cb = cb
    this.value = this.get()
  }
  get() {
    window.target = this
    let value = this.getter.call(this.vm, this.vm)
    window.target = undefined
    return value
  }
  update() {
    const oldValue = this.value
    this.value = this.get()
    this.cb.call(this.vm, this.value, oldValue)
  }
}

// 解析简单路径
const bailRE = /[^\w.$]/
export function parsePath(path) {
  if (bailRE.test(path)) return

  // data.a.b.c => [data,a,b,c]
  const segment = path.split('.')
  return function (obj) {
    for (let index = 0; index < segment.length; index++) {
      if (!obj) return
      const item = segment[index]
      obj = obj[item]
    }
    return obj
  }
}

递归把 obj 的所有转化为响应式数据

class Observer {
  constructor(value) {
    this.value = value
    if (!Array.isArray(value)) {
      this.walk(value)
    }
  }
  walk(obj) {
    const keys = Object.keys(obj)
    for (let index = 0; index < keys.length; index++) {
      const key = keys[index]
      defineReactive(obj, key, obj[key])
    }
  }
}

修改defineReactive方法,将一个正常的 object 转化为被侦测的 object,其中要判断数据类型,只有是 Object 类型的数据才会调用 walk 方法将每一个属性转化为 getter/setter 的形式来侦测变化

function defineReactive(data, key, value) {
  // 递归所有的子属性
  if (typeof val === 'object') {
    new Observer(val)
  }
  let dep = new Dep()
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      dep.depend()
      return value
    },
    set(newValue) {
      if (val === newValue) return
      val = newValue
      dep.notify()
    }
  })
}

关于 Object 中的问题

由于前面的追踪方式局限,导致有些语法即使是数据发生了变化,数据也不会更新

new Vue({
  el: '#app',
  template: '<div>xxxx</div>',
  methods:{
    actions:{
        this.obj.name = 'nelsen'
    }
  },
  data:{
    obj:{

    }
  }
})

action 方法中,我们在 obj 上面新增了一个 name 的属性,框架是无法侦测到这个变化的,
所以就不会向依赖发送通知。同理删除一个属性也是同样的

new Vue({
  el: '#app',
  template: '<div>xxxx</div>',
  methods:{
    actions:{
       delete this.obj.name
    }
  },
  data:{
    obj:{
      name:"nelsen"
    }
  }
})

vue2中通过Object.defineProperty来将对象的 key
转化为getter/setter的形式来追踪依赖,但是getter/setter只能追踪到数据是否被修改,无法追踪新增和删除
属性,这是由于js语言的限制,没有提供元编程的能力
为了解决这个问题,框架给我们提供了vm.$setvm.$delete两个api,来处理对象的新增删除属性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/921937.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows Server 2022 Web2

载入靶机&#xff0c;看到相关描述&#xff1a; 进入虚拟机发现桌面有phpstudy和解题两个软件&#xff1a; 先点击“解题.exe”&#xff1a; 1.攻击者的IP地址&#xff08;两个&#xff09;&#xff1f; 2.攻击者的webshell文件名&#xff1f; 3.攻击者的webshell密码&#x…

学习Prompt Turning

传统的微调因为代价很高&#xff0c;而且一旦权重很大&#xff0c;这种fine 微微的意思是调不动模型的&#xff0c;所以需要这种提示词调 mindnlp直接有 peft config peft_config PromptTuningConfig(task_type“SEQ_CLS”, num_virtual_tokens10) 方便我们进行prompt tunin…

分类算法——基于heart数据集实现

1 heart数据集——描述性统计分析 import matplotlib.pyplot as plt import pandas as pd# Load the dataset heart pd.read_csv(r"heart.csv", sep,)# Check the columns in the DataFrame print(heart.columns)aheart.loc[:, y].value_counts() print(a) heart.l…

POA-CNN-SVM鹈鹕算法优化卷积神经网络结合支持向量机多特征分类预测

分类预测 | Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络结合支持向量机多特征分类预测 目录 分类预测 | Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络结合支持向量机多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现POA-CNN-SVM鹈鹕算法…

【SVN和GIT】版本控制系统详细下载使用教程

文章目录 ** 参考文章一、什么是SVN和GIT二、软件使用介绍1 SVN安装1.1 服务端SVN下载地址1.2 客户端SVN下载地址2 SVN使用2.1 服务端SVN基础使用2.1.1 创建存储库和用户成员2.1.2 为存储库添加访问人员2.2 客户端SVN基础使用2.2.1 在本地下载库中的内容2.2.2 版本文件操作--更…

设计模式:7、策略模式(政策)

目录 0、定义 1、策略模式的三种角色 2、策略模式的UML类图 3、示例代码 0、定义 定义一系列算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 1、策略模式的三种角色 策略&#xff08;Strategy&…

3、集线器、交换机、路由器、ip的关系。

集线器、交换机、路由器三者的关系 1、集线器2、交换机&#xff08;每个交换机是不同的广播域&#xff0c;ip地址起到划分广播域的作用&#xff09;3、 路由器4、ip地址 1、集线器 一开始两台电脑通信就需要网线就可以&#xff0c;但是三台或者更多主机通信时&#xff0c;就需…

mfc100u.dll是什么?分享几种mfc100u.dll丢失的解决方法

mfc100u.dll 是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;属于 Microsoft Foundation Classes (MFC) 库的一部分。MFC 是微软公司开发的一套用于快速开发 Windows 应用程序的 C 类库。mfc100u.dll 文件包含了 MFC 库中一些常用的函数和类的定义&#xff0c;这…

魔众题库系统 v10.0.0 客服条、题目导入、考试导航、日志一大批更新

魔众题库系统基于PHP开发&#xff0c;可以用于题库管理和试卷生成软件&#xff0c;拥有极简界面和强大的功能&#xff0c;用户遍及全国各行各业。 魔众题库系统发布v10.0.0版本&#xff0c;新功能和Bug修复累计30项&#xff0c;客服条、题目导入、考试导航、日志一大批更新。 …

opencv-python 分离边缘粘连的物体(距离变换)

import cv2 import numpy as np# 读取图像&#xff0c;这里添加了判断图像是否读取成功的逻辑 img cv2.imread("./640.png") # 灰度图 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊 gray cv2.GaussianBlur(gray, (5, 5), 0) # 二值化 ret, binary cv2…

YOLO-FaceV2: A Scale and Occlusion Aware Face Detector

《YOLO-FaceV2:一种尺度与遮挡感知的人脸检测器》 1.引言2.相关工作3.YOLO-FaceV23.1网络结构3.2尺度感知RFE模型3.3遮挡感知排斥损失3.4遮挡感知注意力网络3.5样本加权函数3.6Anchor设计策略3.7 归一化高斯Wasserstein距离 4.实验4.1 数据集4.2 训练4.3 消融实验4.3.1 SEAM块4…

CMake笔记:install(TARGETS target,...)无法安装的Debug/lib下

1. 问题描述 按如下CMake代码&#xff0c;无法将lib文件安装到Debug/lib或Release/lib目录下&#xff0c;始终安装在CMAKE_INSTALL_PREFIX/lib下。 install(TARGETS targetCONFIGURATIONS DebugLIBRARY DESTINATION Debug/lib) install(TARGETS targetCONFIGURATIONS Release…

网络编程 day1.2~day2——TCP和UDP的通信基础(TCP)

笔记脑图 作业&#xff1a; 1、将虚拟机调整到桥接模式联网。 2、TCP客户端服务器实现一遍。 服务器 #include <stdio.h> #include <string.h> #include <myhead.h> #define IP "192.168.60.44" #define PORT 6666 #define BACKLOG 20 int mai…

[Flux.jl] 非线性回归的拟合

调用第三方库 using Flux, Random using Plots设置随机种子以确保结果的可重复性 Random.seed!(1)生成数据集 x_data rand(Float32, 500) *20 .- 10 # 生成100个随机x值&#xff0c;范围在0到20之间 y_data sin.(x_data) ./ x_data # 生成y值 y_data reshape(y_data, …

如何创建一个项目用于研究element-plus的原理

需求&#xff1a;直接使用element-plus未封装成组件的源码&#xff0c;创建一个项目&#xff0c;可以使用任意的element-plus组件&#xff0c;可以深度研究组件的运行。例如研究某一个效果&#xff0c;如果直接在node_modules修改elment-plus打包之后的那些js、mjs代码&#xf…

借助算力云跑模型

算力平台&#xff1a;FunHPC | 算力简单易用 AI乐趣丛生 该文章只讲述了最基本的使用步骤&#xff08;因为我也不熟练&#xff09;。 【注】&#xff1a;进入平台&#xff0c;注册登录账号后&#xff0c;才能租用。学生认证&#xff0b;实名认证会有免费的算力资源&#xff0…

C语言:函数指针精讲

1、函数指针 一个函数总是占用一段连续的内存区域&#xff0c;函数名在表达式中有事也会被转换为该函数所在内存区域的首地址&#xff0c;这和数组名非常类似&#xff0c;我们可以把函数这个首地址&#xff08;或称入口地址&#xff09;赋予一个指针变量&#xff0c;使指针变量…

CPU命名那些事

一、Intel CPU命名 1. 命名结构 Intel CPU 的命名通常包含以下几个部分&#xff1a; 品牌 产品线 系列 代数 具体型号 后缀 例如&#xff1a;Intel Core i7-13700K 2. 各部分含义 品牌 Intel&#xff1a;表示厂商&#xff08;几乎所有命名中都有&#xff09;。不同品…

几个bev模型部署常用的命令

python tools/create_data.py nuscenes --root-path ./data/nuscenes --out-dir ./data/nuscenes --extra-tag nuscenes --version v1.0-mini ##迷你版数据集 python tools/create_data.py nuscenes --root-path ./data/nuscenes --out-dir ./data/nuscenes --extra-tag nuscen…

Vue3-小兔鲜项目出现问题及其解决方法(未写完)

基础操作 &#xff08;1&#xff09;使用create-vue搭建Vue3项目 要保证node -v 版本在16以上 &#xff08;2&#xff09;添加pinia到vue项目 npm init vuelatest npm i pinia //导入creatPiniaimport {createPinia} from pinia//执行方法得到实例const pinia createPinia()…