用一段代码明白vue3响应式

vue的响应式的逻辑简单的说明:对象get时进行添加副作用函数,set时执行副作用函数

当我们对obj.text进行改变时需要页面刷新,我们需要重新执行这句代码,那么这就是副作用函数

javascript 复制代码
// 副作用函数
function effect(){
	document.body.textContent = obj.text
}

副作用函数执行,创建proxy触发get收集副作用函数,set时执行副作用函数,

javascript 复制代码
let bucket = []
function reactive (target) {
    return new Proxy(target, {
        get (target, key, receiver) {
            bucket.push(effect)
            return target[key]
        },
        set (target, key, newVal, receiver) {
            target[key] = newVal
            bucket.forEach(effect =>{
                effect()
            })
            return true
        }
    })
}

但是副作用函数需要变为多更为灵活,所以改写effect,定义全局变量activeEffect用于记录副作用函数

javascript 复制代码
let activeEffect 
let bucket = []
function effect(fn){
    activeEffect = fn
    // 思考为什么要执行一次
    fn()
}
function reactive (target) {
    return new Proxy(target, {
        get (target, key, receiver) {
            bucket.push(activeEffect)
            return target[key]
        },
        set (target, key, newVal, receiver) {
            target[key] = newVal
            bucket.forEach(effect =>{
                effect()
            })
            return true
        }
    })
}

但是使用会发现一个问题,就是bucket会重复收集副作用函数,因此我们需要使用Set()来避免重复记录副作用函数,而且如果同时存在两个key都有副作用函数,我们是无法分辨的

javascript 复制代码
function effect1(){
	document.getElementById('text').textContent = _obj.text
}
function effect2(){
	document.getElementById('text2').textContent = _obj.text2
}

此时我们的副作用函数是要与key做对应

css 复制代码
_obj
   |
   ----text---effect1
   |
   ----text2---effect2

改写如下:

javascript 复制代码
let activeEffect 
let bucket = new WeakMap()
function effect(fn){
    activeEffect = fn
    fn()
}
function reactive (target) {
    return new Proxy(target, {
        get (target, key, receiver) {
            if (!bucket.get(target)) {
                bucket.set(target, new Map())
            }
            if (!bucket.get(target).get(key)) {
                bucket.get(target).set(key, new Set())
            }
            bucket.get(target).get(key).add(activeEffect)
            return target[key]
        },
        set (target, key, newVal, receiver) {
            target[key] = newVal
            if (!bucket.get(target)) return
            if (!bucket.get(target).get(key)) return
            bucket.get(target).get(key).forEach(effect => {
                effect()
            })
            return true
        }
    })
}

WeakMap可以搜一下它的解释,简单来说就是它可以对不用的key触发垃圾回收机制,Map则不会

我们提取get里面的处理逻辑到track函数中,set里面的处理逻辑到trigger中

javascript 复制代码
function effect (fn) {
    activeEffect = fn
    fn()
}
function tarck (target, key) {
    if (!bucket.get(target)) {
        bucket.set(target, new Map())
    }
    if (!bucket.get(target).get(key)) {
        bucket.get(target).set(key, new Set())
    }
    bucket.get(target).get(key).add(activeEffect)
}
function trigger (target, key) {
    if (!bucket.get(target)) return
    if (!bucket.get(target).get(key)) return
    bucket.get(target).get(key).forEach(effect => {
        effect()
    })
}
function reactive (target) {
    return new Proxy(target, {
        get (target, key, receiver) {
            tarck(target, key, receiver)
            return target[key]
        },
        set (target, key, newVal, receiver) {
            target[key] = newVal
            trigger(target, key, newVal, receiver)
            return true
        }
    })
}

使用:

javascript 复制代码
let _obj = reactive({
    text: '123',
    text2: '123'
})
effect(() => {
    document.getElementById('text').textContent = _obj.text
    document.getElementById('text2').textContent = _obj.text2
})
相关推荐
web小白成长日记1 天前
在Vue样式中使用JavaScript 变量(CSS 变量注入)
前端·javascript·css·vue.js
全栈前端老曹1 天前
【前端路由】Vue Router 嵌套路由 - 配置父子级路由、命名视图、动态路径匹配
前端·javascript·vue.js·node.js·ecmascript·vue-router·前端路由
张雨zy1 天前
Vue 项目管理数据时,Cookie、Pinia 和 LocalStorage 三种常见的工具的选择
前端·javascript·vue.js
五月君_1 天前
Nuxt UI v4.3 发布:原生 AI 富文本编辑器来了,Vue 生态又添一员猛将!
前端·javascript·vue.js·人工智能·ui
源码获取_wx:Fegn08951 天前
基于springboot + vue小区人脸识别门禁系统
java·开发语言·vue.js·spring boot·后端·spring
千寻girling1 天前
面试官 : “ Vue 选项式api 和 组合式api 什么区别? “
前端·vue.js·面试
华仔啊1 天前
Vue 组件通信的 8 种最佳实践,你知道几种?
前端·vue.js
Ahtacca1 天前
Linux环境下前后端分离项目(Spring Boot + Vue)手动部署全流程指南
linux·运维·服务器·vue.js·spring boot·笔记
计算机毕设VX:Fegn08951 天前
计算机毕业设计|基于springboot + vue小区人脸识别门禁系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
让我上个超影吧2 天前
基于SpringBoot和Vue实现CAS单点登录
前端·vue.js·spring boot