Vue - 响应式原理(一)

引言

Vue的响应式系统是框架的重要组成部分,之后会在资料的辅助下逐步实现一个简洁的响应式数据结构

副作用函数Effect

首先要了解一下副作用函数的概念,在函数式编程中副作用函数指在一定作用上对,对外部环境造成了一定的影响,如:

  1. 修改了函数作用域以外数据
let count = 0;
const add = () => {
	count++
}
  1. 抛出异常
const throwErr = () => {
	throw new Error()
}

响应式数据

当我们想要一个数据进行改变时,所有依赖于它的函数等,重新执行,如一个绑定于DOM的数据,随之发生改变,这个数据我们就希望他是响应式的,Vue2中通过Object.defineProperty对数据进行劫持实现,Vue3中则通过Proxy对象实现

// vue2
// 原始数据
const data = {
  age: 18,
}
let value = data.age
Object.defineProperty(data, "age", {
  get() {
    return value
  },
  set(newValue) {
    value = newValue
  },
})
// 修改数据
setTimeout(() => {
  data.age++
}, 2000) 

上面代码在两秒后会改变age属性的值,并且触发Setter

下面是Vue3响应数据的基础实现

// Vue3
// 原始数据
const data = {
  age: 18,
}
const proxy = new Proxy(data, {
  get(target, key: keyof typeof data) {
    return target[key]
  },
  set(target, key: keyof typeof data, newValue: never) {
    target[key] = newValue
    console.log("=========", target[key])
    return true
  },
})
// 修改数据
setTimeout(() => {
  proxy.age++
}, 2000)

上面对原始数据data进行了代理,并设置了SetterGetter,之后在使用代理后的对象数据进行操作即可

实现基础操作

接下来结合二者完成一个基础的响应式操作(Vue3):

  1. HTML中创建两个元素#app用于展示数据,.btn用于修改数据
<!-- dom元素 -->
<div id="app"></div>
<button class="btn">改变数据</button>
  1. 创建对象并进行代理,使用代理后的数据展示于#app
// 原始数据
const data = {
  name: "zhangsan",
  age: 18,
  sex: "男",
}
// 进行代理 Vue3
const proxy = new Proxy(data, {
  get(target, key: keyof typeof data) {
    return target[key]
  },
  set(
    target,
    key: keyof typeof data,
    newValue: (typeof data)[keyof typeof data]
  ) {
    ;(target[key] as any) = newValue
    console.log("=========", target[key])
    return true
  },
})
const app = document.querySelector("#app")!
app.innerHTML = `<div>name: ${proxy.name}: age: ${proxy.age}</div>`
// 修改数据
const btn = document.querySelector(".btn")!
btn.addEventListener("click", () => {
  proxy.age++
})

到这儿已经可以改变age的值了,但是还不能促使DOM的更新,因为并没有对修改DOM的操作进行触发,所以接下来需要存储一下操作,并在修改属性值时进行调用

  1. 创建副作用函数,并进行存储,最后代码如下
// 存储副作用函数的桶
const buket = new Set<Function>()
// Getter中添加effect,Setter中执行effect
const data = {
  name: "zhangsan",
  age: 18,
  sex: "男",
}
const proxy = new Proxy(data, {
  get(target, key: keyof typeof data) {
    buket.add(effect) // 添加副作用函数
    return target[key]
  },
  set(
    target,
    key: keyof typeof data,
    newValue: (typeof data)[keyof typeof data]
  ) {
    ;(target[key] as any) = newValue
    buket.forEach((fn) => fn()) // 执行所有的副作用函数
    return true
  },
})
const effect = () => {
  app.innerHTML = `<div>name: ${proxy.name}: age: ${proxy.age}</div>`
}
effect()
// 修改数据
const btn = document.querySelector(".btn")!
btn.addEventListener("click", () => {
  proxy.age++
})

效果如下
在这里插入图片描述
点击按钮时,age属性值发生改变,并且视图也发生变化

结束语

这实现了最基本的一个响应式效果,但还存在很多问题,例如属性没对应上,effect函数固定,后面再学习完善哇
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值