2024年前端最新【面试题】手写简单vue3响应式原理_vue3响应式原理面试,让人茅塞顿开

总结

三套“算法宝典”

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

28天读完349页,这份阿里面试通关手册,助我闯进字节跳动

算法刷题LeetCode中文版(为例)

人与人存在很大的不同,我们都拥有各自的目标,在一线城市漂泊的我偶尔也会羡慕在老家踏踏实实开开心心养老的人,但是我深刻知道自己想要的是一年比一年有进步。

最后,我想说的是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!

由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注

true
复制代码



> 
> target:参数表示所要拦截的目标对象
> 
> 
> handler:参数也是一个对象,用来定制拦截行为
> 
> 
> 


注意:


* **this** 关键字表示的是代理的 handler 对象,**所以不能使用this而是要用 receiver传递**,`receiver`代表当前proxy对象 或者 继承proxy的对象,它保证传递正确的 this 给 getter,setter
* `set` 和 `deleteProperty` 也需要返回(添加`return` ),返回的是一个布尔值,设置/删除成功返回true,反之返回false


### reactive


了解了上面的Proxy和Reflect,我们来看一下reactive的实现,reactive,返回proxy对象,这个reactive可以深层次递归,如果发现子元素存在引用类型,递归处理。



// 判断是否为对象 ,注意 null 也是对象
const isObject = val => val !== null && typeof val === ‘object’
// 判断key是否存在
const hasOwn = (target, key) => Object.prototype.hasOwnProperty.call(target, key)

export function reactive(target) {
// 首先先判断是否为对象
if (!isObject(target)) return target

const handler = {
    get(target, key, receiver) {
        console.log(`获取对象属性${key}值`)
        // ... 这里还需要收集依赖,先空着

        const result = Reflect.get(target, key, receiver)
        // 递归判断的关键, 如果发现子元素存在引用类型,递归处理。
        if (isObject(result)) {
            return reactive(result)
        }
        return result
    },

    set(target, key, value, receiver) {
        console.log(`设置对象属性${key}值`)

        // 首先先获取旧值
        const oldValue = Reflect.get(target, key, reactive)

        // set 是需要返回 布尔值的
        let result = true
        // 判断新值和旧值是否一样来决定是否更新setter
        if (oldValue !== value) {
            result = Reflect.set(target, key, value, receiver)
            // 更新操作 等下再补
        }
        return result
    },

    deleteProperty(target, key) {
        console.log(`删除对象属性${key}值`)

        // 先判断是否有key
        const hadKey = hasOwn(target, key)
        const result = Reflect.deleteProperty(target, key)

        if (hadKey && result) {
            // 更新操作 等下再补
        }

        return result
    },
}
return new Proxy(target, handler)

}

复制代码


测试



复制代码


![](https://img-blog.csdnimg.cn/img_convert/8808ef60f3ea2348e7df99f6a0522175.webp?x-oss-process=image/format,png)


### 收集依赖/触发更新


上面我们还有get中收集依赖没有完成,收集依赖涉及道track , effect 还有依赖地图,下面我给出一张图先介绍一下effect和track是如何收集依赖的


![](https://img-blog.csdnimg.cn/img_convert/78522997ccd33465aeb5a24482d019e9.webp?x-oss-process=image/format,png)


响应式顺序:effect > track > trigger > effect


在组件渲染过程中,一个 effect 会会触发get,从而对值进行 track,当值发生改变,就会进行 trigge,执行 effect 来完成一个响应


那么先来实现 effect


#### effect



> 
> effect的实现很简单
> 
> 
> 



// activeEffect 表示当前正在走的 effect
let activeEffect = null
export function effect(callback) {
activeEffect = callback
callback()
activeEffect = null
}
复制代码


#### track



> 
> 然后就是对 track 的实现
> 
> 
> 



// targetMap 表里每个key都是一个普通对象 对应他们的 depsMap
let targetMap = new WeakMap()

export function track(target, key) {
// 如果当前没有effect就不执行追踪
if (!activeEffect) return
// 获取当前对象的依赖图
let depsMap = targetMap.get(target)
// 不存在就新建
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// 根据key 从 依赖图 里获取到到 effect 集合
let dep = depsMap.get(key)
// 不存在就新建
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// 如果当前effectc 不存在,才注册到 dep里
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
}
}

复制代码



> 
> 最后添加到hander 里 get 中
> 
> 
> 



get(target, key, receiver) {
// …
// 收集依赖
track(target, key)

// ...

},
复制代码


#### 触发更新


通过上面的图,我们知道在set中使用trigger函数来触发更新,我们来实现一下吧



// trigger 响应式触发
export function trigger(target, key) {
// 拿到 依赖图
const depsMap = targetMap.get(target)
if (!depsMap) {
// 没有被追踪,直接 return
return
}
// 拿到了 视图渲染effect 就可以进行排队更新 effect 了
const dep = depsMap.get(key)

// 遍历 dep 集合执行里面 effect 副作用方法
if (dep) {
    dep.forEach(effect => {
        effect()
    })
}

}
复制代码



> 
> 最后添加到hander 的 set 和 deleteProperty 中
> 
> 
> 



set(target, key, value, receiver) {
// …
if (oldValue !== value) {
result = Reflect.set(target, key, value, receiver)
trigger(target, key)
}
// …
},

deleteProperty(target, key) {
// …
if (hadKey && result) {
// 更新操作
trigger(target, key)
}
// …
}
复制代码


### ref


把一个基础类型包装成一个有value响应式对象(这里是使用`get/set` 存取器,来进行追踪和触发),如果是普通对象就调用 reactive 来创建响应式对象



// 判断是否是一个对象,是就用 reactive 来代理
const convert = val => (isObject(val) ? reactive(val) : val)

class RefImpl {
constructor(_rawValue) {
this._rawValue = _rawValue
this.__v_isRef = true
// 判断 _rawValue 是否是一个对象
// 如果是对象调用reactive使用 proxy来代理
// 不是返回 _rawValue 本身
this._value = convert(_rawValue)
}
// 使用get/set 存取器,来进行追踪和触发
get value() {
// 追踪依赖
track(this, ‘value’)
// 当然 get 得返回 this._value
return this._value
}
set value(newValue) {
// 判断旧值和新值是否一直
if (newValue !== this._value) {
this._rawValue = newValue
// 设置新值的时候也得使用 convert 处理一下,判断新值是否是对象
this._value = convert(this._rawValue)
// 触发依赖
trigger(this, ‘value’)
}
}
}

export function ref(rawValue) {
// __v_isRef 用来标识是否是 一个 ref 如果是直接返回,不用再转
if (isObject(rawValue) && rawValue.__v_isRef) return rawValue

return new RefImpl(rawValue)

}
复制代码


### toRef



> 
> `toRef`传入两个参数,**目标对象**,**对象当中的属性名**,它的返回结果就是**属性名**的可响应式数据,就是将对象中的某个值转化为响应式数据 toRef(obj,key)
> 
> 
> 那么简单来实现一下
> 
> 
> 

#### 总结

三套“算法宝典”

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

![28天读完349页,这份阿里面试通关手册,助我闯进字节跳动](https://img-blog.csdnimg.cn/img_convert/921d61c147522637cff31846545fe430.png)



算法刷题LeetCode中文版(为例)

人与人存在很大的不同,我们都拥有各自的目标,在一线城市漂泊的我偶尔也会羡慕在老家踏踏实实开开心心养老的人,但是我深刻知道自己想要的是一年比一年有进步。

最后,我想说的是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!

由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注
------------------------------------------------------------------

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值