1. 首页

vue3 reactive 思路引导

首先这是一个精简版,所以有很多缺陷。但这篇文章仅仅是用来理清为什么,而不是去实现一个完备的reactive 模块。 如果对reactive 比较熟悉,这篇文章大可不必浪费时间。

假设设计一个 晚餐价格计算的功能。 total 总价 = dinnerPrice 晚餐费用 + tip 小费

待改造代码

render函数

function paint(){
    document.getElementById('app').innerHTML = `
        <p>${dinnerPrice}</p>
        <p>${tip}</p>
        <p>${total}</p>
    `
}

render 函数需要的变量

render 函数中,需要三个变量为dinnerPrice,tip, total


let dinnerPrice = 100; let tip = 10; let total = 0;

更新函数

视图和数据定义完了后,还需要定义更新函数


function updateTotal(){ total = dinnerPrice * tip } //Js中文网,一个神奇的网站

原始调用方式

这样一个基本雏形就有了,调用方式如下

  1. 修改dinner 或者 tip dinnerPrice = 200
  2. 手动调用updateTotal 更新数据 updateTotal()
  3. 手动调用paint 来更新视图paint()

reactive

修改dinnerPrice 或者 tips 自动更新total

引入proxy 对数据进行代理

如果想用proxy 进行代理数据,则数据必须是Object,因此数据需要调整为Object 结构

function paint(){
    document.getElementById('app').innerHTML = `
        <p>${data.dinnerPrice}</p>
        <p>${data.tip}</p>
        <p>${data.total}</p>
    `
}
const rawData = {
  dinnerPrice:100,
  tip:10,
  total:0
}

const data = proxy(rawData,{})

function updateTotal(){
  data.total = data.dinnerPrice * data.tip
}

set trap

我们想要实现:修改数据,视图自动更新

这就需要定义handler.set trap,来实现代理操作


const data = proxy(rawData,{ get(){}, set(obj,key,value){ // 赋值操作 obj[key] = value//为简化操作,这里不使用Reflict // 如果key 是 dinnerPrice 或者 tip,那么说明我们需要执行updateTotal函数 // 如果不进行判断,会爆栈,原因:设置dinnerPrice 或者tip会 触发total set 操作,从而无限set if(key === 'dinnerPrice' || key === 'tip'){ updateTotal() } } }) //Js中文网,一个神奇的网站

这样,就能够实现当设置dinnerPrice 或者 tip 自动更新total。

引入deps 概念

但是,这里的key是固定死的,可以进行进一步的优化。方法:将key 列表通过一个数据结构来进行维护,如果data的某个key进行了set操作,并且这个数据结构中有这个key , 那么就执行对应的更新函数


// 为新的数据结构起名为 deps (dependences) const deps = { tip:[updateTotal], dinnerPrice:[updateTotal] } const data = proxy(rawData,{ ..., set(obj,key,value){ obj[key] = value; deps[key] && deps[key].forEach(fn=>fn()) } }) //Js中文网,一个神奇的网站

优化了代码结构后,代码结构清晰了不少,但是我们目标是修改数据,自动更新视图,这里的deps必须自动手动添加。

添加动态dep

想要动态添加dep,需要用到handler.get 方法。handler.get 会拦截当对象的读取操作。在这里可以对dep 进行动态添加。


const deps = {} //如果key 存在,那么则将更新函数加入它的依赖中 const data = proxy(rawData,{ get(obj,key){ deps[key] = !!deps[key] ? [...deps[key],updateTotal] : [] } return obj[key] }) //Js中文网,一个神奇的网站

引入watcher 概念

上述的方法,实现了为deps 动态添加updateTotal 这个函数,但是问题很大,每次对象的读取操作执行都会重复添加依赖。我们只需要执行一次即可,并且能够自主添加不同的更新函数,所以,进行一下修改


let runningFn = null; function watcher(target){ runningFn = target target() runningFn = null } watcher(updateTotal)// == watcher(()=>data.total = data.dinnerPrice + data.tip) //Js中文网,一个神奇的网站

使用watcher 进行依赖收集

我们使用watcher 对需要的更新操作进行watch ,执行watcher 就会触发对应的get操作

修改proxy


const data = proxy(rawData,{ get(obj,key){ //此处的依赖收集为简化板,存在缺陷,key可能存在相同的,导致错误。vue3 的依赖收集为两层 deps[obj][key] const collectDep = (key)=>deps[key] = !!deps[key] ? [...deps[key],updateTotal] : [] //如果是wathcher 执行的操作,那么进行依赖收集 !!ruuningFn && collectDep(key) return obj[key] }, ... }) //Js中文网,一个神奇的网站

完整代码

这样一个reactive 就基本写完了,下面进行封装

//封装proxy
const observe = dataObj => new Proxy(dataObj,{
  get(obj,key){
    //此处的依赖收集为简化板,存在缺陷,key可能存在相同的,导致错误。vue3 的依赖收集为两层 deps[obj][key]
    const collectDep = (key)=>deps[key] = !!deps[key] ? [...deps[key],updateTotal] : []
    //如果是wathcher 执行的操作,那么进行依赖收集
    !!ruuningFn && collectDep(key)
    return obj[key]
  },
  set(obj,key,value){
    obj[key] = value;
    deps[key] && deps[key].forEach(fn=>fn())
  }
})

let runningFn = null
const watcher = target=>{
  runningFn = target;
  target()
  runningFn = null;
}

//对render函数进行依赖收集
watcher(function render(){
    document.getElementById('app').innerHTML = `
        <p>${data.dinnerPrice}</p>
        <p>${data.tip}</p>
        <p>${data.total}</p>
    `
})

// 对数据更新操作进行依赖收集
watcher(()=>data.total = data.dinnerPrice + data.tip)

作者:ShanCW
链接:https://juejin.im/post/6844904131216277511

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:vue3 reactive 思路引导

链接:https://www.javascriptc.com/4138.html

« 我敢打赌!这是全网最全的 Git 分支开发规范手册
LeetCode 040. 组合总和 II»
Flutter 中文教程资源

相关推荐

QR code