vue设计与实现合理的触发响应

正文

前文中讲完了如何实现对Object的响应,包含了读取属性,修改值,删除属性.但这里缺少很多优化,我们只能说是简单的实现了功能,但是有很多边际条件我们并没有考虑到。

现在跟着作者,看看我们还需要考虑什么。

  • 当值没有发生变化时 比如这样
const p = reactive({a:1})
effect(()=>{
    console.log(p.a)
})
p.a=1

p.a的初始值就是1,然后你再次赋值为1,理论上我们就不需要再执行副作用了,但是显然在以前的代码还是会执行的。

我们考虑到了相同的值,这时候应该不触发响应。于是你简单的思考下,写下了如下代码

new Proxy(obj,{
 set(target,key,newVal,receivere){
     const oldVal = target[key]
     if(oldVal !== newVal){
         // do sth
     }
 }
})

感觉上没什么毛病,但是如果你的oldValnewVal都等于NaN呢?NaN!==NaN,完全满足不全等的条件,因此你还是会触发一次响应。

在书中,作者的解决办法是,这样去排除NaN

     const oldVal = target[key]
     if(oldVal !== newVal && (oldVal === oldVal && newVal === newVal)){
         // do sth
     }

但是我觉得也可以这样子const checkNaN = (any)=> typeof any === 'number' ? isNaN(any) : false.

这样就解决了,当值没有发生变化时的响应。

  • 原型链继承问题

这个其实是一种比较特殊的情况,真不知道当时是怎么测试出来的。首先,我们把new Proxy封装为一个函数

const handlers = {/*这里暂时省略*/}
const reactive = (obj)=>new Proxy(obj,handlers)
const obj = {}
const obj2 = {a:1}
const robj = reactive({})
const robj2 = reactive{a:1})
Object.setPrototypeOf(robj,robj2)
effect(()=>{
    console.log(robj.a)
})
robj.a++ //这里导致effect执行2次

在这段代码里,我们创建了2个响应式对象,同时通过Object.setPrototypeOf设置一个指定的对象的原型( 即,内部[[Prototype]] 属性),这里我认为就是强制指定了一种继承关系,一个Proxy实例继承了另一个Proxy实例,同时被继承的那个实例则有一个副作用。

这个问题其实解释起来蛮复杂的,首先第一步,我们先看robj这个对象,对象上并没有a这个属性,那么熟悉原型链的各位大佬读者们,肯定指定,js会沿着原型链依次向上查找,就会找到robj2,并执行[[Get]]去获取这个属性,然后这个动作就会被Proxy拦截。

回忆一下我们之前的代码,首先以target为key,建立一个依赖关系,收集所有的副作用,再通过key获取具体的副作用函数,然后再根据一定条件触发。

那么,当我们获取robj.a时,js查找到robj2,这时候就会同时以robj2robj2为key,建立两个依赖关系,最后导致副作用执行2次。 在前面讲Proxy的时候,我们写过这样一句话.其中target指向的原始对象,receiver则是Proxy实例,也是this的指向。也就是在这个时候,this被改成了robj2

new Proxy(obj,{
    set(target,key,newVal,receiver){
        //do sth
        Reflect.set(target,key,newVal,receiver)
    }
})

当我分析到这里的时候,我已经大概能知道作者如何解决了。我们应当保留最外层的Proxy,然后让其原型链上的其他Proxy指向原始对象。

也就是判断receiver是不是来自robj,如果不是我们不能去收集和触发依赖。也就是说,这个问题需要同时修改Proxy的get和set去解决。 这时候我想到了vue3中的ref,如果你直接打印一下,可以发现它有着这样的结构

那么我们也就这样试一下

new Proxy(obj,{
    get(target,key,receiver){
        // 不进行后面的依赖收集
        if(key === '_rawValue'){
         return target
        }
        //其他逻辑不变
    },
    set(target,key,newVal,receiver){
        // 不触发副作用
         if(target === receiver._rawValue){
             if(oldVal !== newVal && (oldVal === oldVal && newVal === newVal)){
                 // do sth
             }
         }
        //其他逻辑不变
    },
})

这样新增了一个字段,修复了这个触发2次副作用的问题。我相信,在vue3中会有更多的判断和处理,作者只是挑了2个典型来写,这也是为什么上几个章节会科普ProxyReflect的原因。

下一章会讲到浅响应与深响应,对应Vue3中的API就是shallowReactivereactive

以上就是vue设计与实现合理的触发响应的详细内容,更多关于vue合理触发响应的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue响应式原理及双向数据绑定示例分析

    目录 前言 响应式原理 双向数据绑定 前言 之前公司招人,面试了一些的前端同学,因为公司使用的前端技术是Vue,所以免不了问到其响应式原理和Vue的双向数据绑定.但是这边面试到的80%的同学会把两者搞混,通常我要是先问响应式原理再问双向数据绑定原理,来面试的同学大都会认为是一回事,那么这里我们就说一下二者的区别. 响应式原理 是Vue的核心特性之一,数据驱动视图,我们修改数据视图随之响应更新,就很优雅~ Vue2.x是借助Object.defineProperty()实现的,而Vue3.x是借助

  • 手写 Vue3 响应式系统(核心就一个数据结构)

    目录 前言 响应式 总结 前言 响应式是 Vue 的特色,如果你简历里写了 Vue 项目,那基本都会问响应式实现原理.而且不只是 Vue,状态管理库 Mobx 也是基于响应式实现的.那响应式是具体怎么实现的呢?与其空谈原理,不如让我们来手写一个简易版吧. 响应式 首先,什么是响应式呢? 响应式就是被观察的数据变化的时候做一系列联动处理.就像一个社会热点事件,当它有消息更新的时候,各方媒体都会跟进做相关报道.这里社会热点事件就是被观察的目标.那在前端框架里,这个被观察的目标是什么呢?很明显,是状态

  • vue中provide inject的响应式监听解决方案

    目录 provide inject的响应式监听解决 vue监听赋值及provide与inject provide inject的响应式监听解决 提示:provide 和 inject 绑定并不是可响应的.这是刻意为之的.然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的. 所以传值传对象即可 provide(){     return {       provObj: {         uuidList:{}       }     }   }, this._provided.p

  • vue响应式Object代理对象的修改和删除属性

    目录 正文 set delete 正文 上一篇文章我们学习了如何代理对象的读取,下面我们学习如何代理对象的修改和删除属性. set set就是修改代理的属性,按照我们之前写的reactive,它大概是这样的 const ITERATE_KEY=symbol() const p = new Proxy(obj,{ set(target,key,newVal,receiver){ const res = Reflect.set(target,key,newVal,receiver) trigger(

  • vue3 响应式对象如何实现方法的不同点

    目录 vue3响应式对象实现方法的不同点 Vue2和Vue3响应式原理对比 响应式原理实现逻辑 Vue2响应式原理简化 Vue2响应式原理弊端 Vue3响应式原理简化 vue3响应式对象实现方法的不同点 vue 响应式对象是利用 defindeProperty 实现,vue3 则是使用 Proxy 来实现响应式的. 二者,虽然都实现了 响应式的功能,但实现方式不一样,解决问题也有些不同. vue2 中,如果想要实现,数组元素的更新,则是需要 用到检测更新的方法 set 之类的,但 vue3的响应

  • vue3结构赋值失去响应式引发的问题思考

    目录 前言 原始值的响应式系统的实现 为什么ES6 解构,不能随意使用会破坏他的响应式特性 proxy背景 实现原理 解构 props 对象,因为它会失去响应式 直接赋值reactive响应式对象 vuex中组合API赋值 结语 前言 vue3发布以来经历两年风头正盛,现在大有和react 平分天下的势头,我们知道他是基于proxy 实现响应式的能力, 解决了vue2所遗留下来的一些问题,同时也正由于proxy的特性,也提高了运行时的性能 凡事有利有弊, proxy虽然无敌,但是他也有本身的局限

  • vue设计与实现合理的触发响应

    正文 前文中讲完了如何实现对Object的响应,包含了读取属性,修改值,删除属性.但这里缺少很多优化,我们只能说是简单的实现了功能,但是有很多边际条件我们并没有考虑到. 现在跟着作者,看看我们还需要考虑什么. 当值没有发生变化时 比如这样 const p = reactive({a:1}) effect(()=>{ console.log(p.a) }) p.a=1 p.a的初始值就是1,然后你再次赋值为1,理论上我们就不需要再执行副作用了,但是显然在以前的代码还是会执行的. 我们考虑到了相同的

  • 用vue设计一个数据采集器

    场景 在业务上现在有一个场景,当发生业务行为变化时,需要对各个模块的行为进行数据收集,数据用途可以用作回顾,也可以是例如监控这样的场景. 核心问题 说白了这个需求就是需要对各个模块状态变更进行记录,然后再格式化上传到服务端. 解题思路有两种一种是状态监听,第二主动收集. 状态监听 状态监听优势 快速实现利用状态管理和wacth的机制很快就知道不同模块的状态变更,然后就可以获取数据,再格式化数据,发送给服务端 状态监听劣势 wacth的重复监听,只要使用了wacth,不管是不是你所需要的数据,只要

  • vue设计一个倒计时秒杀的组件详解

    简介: 倒计时秒杀组件在电商网站中层出不穷  不过思路万变不离其踪,我自己根据其他资料设计了一个vue版的 核心思路: 1.时间不能是本地客户端的时间  必须是服务器的时间这里用一个settimeout代替 以为时间必须统一 2.开始时间,结束时间通过父组件传入,当服务器时间在这个开始时间和结束时间的范围内  参加活动按钮可以点击,并且参加过活动以后不能再参加, 3.在组件创建的时候 同步得到现在时间服务时间差,并且在这里边设置定时器,每秒都做判断看秒杀是否开始和结束, 4.在更新时间的函数中是

  • vue.js实现输入框输入值内容实时响应变化示例

    本文实例讲述了vue.js实现输入框输入值内容实时响应变化的方法.分享给大家供大家参考,具体如下: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-sca

  • vue中axios防止多次触发终止多次请求的示例代码(防抖)

    需求 例如在搜索框中,并不是你输入一个字就需要渲染一次数据,而是取最后一次的输入内容进行搜索. 连续按下 AAAAA ,只取最后一次按下时搜索框的内容(即:AAAAA)进行搜索. 而不是搜索跟 A(第一次按下),AA(第二次按下),AAA相关联的内容 本文例子:  检测用户输入的值,监测这个值,然后根据值调用接口查询结果 代码: <template> <input type="text" v-model="message"> <temp

  • 用vue设计一个日历表

    日历的功能,我们会经常用到,且逻辑比较复杂,小算法较多,花了半天时间写了个,特此详记. 先贴图 功能阐述:返回本月不多说,设置工作日和节假日是为了公司制度需要,后台会有假日表来记录. 为了适应于vue框架,很多jquery的方法用不上,例如addClass及removeClass,所以可能某些地方做的比较繁琐. <template> <div> <div class="date"> <!-- 年份 月份 --> <div class

  • Vue.js每天必学之内部响应式原理探究

    深入响应式原理 大部分的基础内容我们已经讲到了,现在讲点底层内容.Vue.js 最显著的一个功能是响应系统 -- 模型只是普通对象,修改它则更新视图.这让状态管理非常简单且直观,不过理解它的原理也很重要,可以避免一些常见问题.下面我们开始深挖 Vue.js 响应系统的底层细节. 如何追踪变化 把一个普通对象传给 Vue 实例作为它的 data 选项,Vue.js 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter.这是 ES5 特性,不能打补丁

  • Vue实现双向绑定的原理以及响应式数据的方法

    一.vue中的响应式属性 Vue中的数据实现响应式绑定 1.对象实现响应式: 是在初始化的时候利用definePrototype的定义set和get过滤器,在进行组件模板编译时实现water的监听搜集依赖项,当数据发生变化时在set中通过调用dep.notify进行发布通知,实现视图的更新. 2.数组实现响应式: 对于数组则是通过继承重写数组的方法splice.pop.push.shift.unshift.sort.reverse.等可以修改原数组的方式实现响应式的,但是通过length以及直接

  • vue中eventbus被多次触发以及踩过的坑

    一开始的需求是这样子的,我为了实现两个页面组件之间的数据传递,假设我有页面A,点击页面A上的某一个按钮之后,页面会自动跳转到页面B,同时我希望将页面A上的某一些参数携带过去给页面B.(我知道,小参数的时候可以通过路由的params或者query去传参数,或者大型数据可以用vuex来处理,很遗憾我到现在还没有做很大型的项目,所以还没有用过vuex,接下来会学习一下.) 然后我就想,这不就是不同组件之间的数据传递问题而已吗?直接用bus 巴士事件来传递数据不就行了吗.于是,我就很愉快地进行了.关于v

  • vue实现加载页面自动触发函数(及异步获取数据)

    目录 加载页面自动触发函数 实例 页面加载时,触发某个函数的方法 解决方法如下 加载页面自动触发函数 实例 methods:{ onCreate:async function() { const router = useRouter() const route = useRoute() const { id = '', f = 1 } = route.query console.log("======="+id) const res = await reqGetOrderNumByCl

随机推荐