前言

计算属性,功能和vue2.x基本等同,本文主要对其功能进行模拟实现

一.完整代码

const reactMap = new WeakMap();
const ReactiveFlags = {IS_REACTIVE : "isReactive"
}let activeEffect = undefined;
class ReactiveEffect {active = true;deps = [];constructor(fn,scheduler) {this.fn = fn;this.scheduler = scheduler;}run() {if(!this.active) {this.fn()};try {activeEffect = this;return this.fn()} finally {activeEffect = undefined;}}
}class ComputedRefImpl {effect;_value;dep = new Set();_dirty = true;_v_isReadOnly = true;_v_isRef = true;constructor(getter,setter) {this.effect = new ReactiveEffect(getter, () => {// 稍后依赖的属性变化会执行此调度函数if(!this._dirty) {this._dirty = true;triggerEffect(this.dep)}})}get value() {trackEffect(this.dep)if(this._dirty) {this._dirty = false;this._value = this.effect.run()}return this._value}}function effect(fn) {const _effect = new ReactiveEffect(fn);_effect.run();// computed新增const runner = _effect.run.bind(_effect);runner.effect = runner;return runner
}
// 将数据转化成响应式的数据,只能做对象的代理
function reactive(target) {if(!(typeof target === 'object' && target !== null)) {return;}if(target[ReactiveFlags.IS_REACTIVE]) {return target}let exisProxy = reactMap.get(target);if(exisProxy) {return exisProxy}const proxy = new Proxy(target,{get(target,key,receiver) {if(key === ReactiveFlags.IS_REACTIVE) {return true}track(target,'get',key)return Reflect.get(target,key,receiver)},set(target,key,value,receiver) {let oldValue = target[key];let result = Reflect.set(target,key,value,receiver);if(oldValue !== value) {trigger(target,'set',key,oldValue,value)}return result }});reactMap.set(target,proxy)return proxy
}// 计算属性
function computed(getterOrOptions) {let onlyGetter = typeof getterOrOptions === 'function'let getter;let setter;if(onlyGetter) {getter = getterOrOptions;setter = () => {console.warn('no set')}} else {const {get,set} =getterOrOptions;[getter,setter] = [get,set]}return new ComputedRefImpl(getter,setter)
}const targetMap = new WeakMap()function trigger(target,type,key,oldValue,value) {const depsMap = targetMap.get(target);if(!depsMap) return;const effects = depsMap.get(key);triggerEffect(effects,'effects')
}function triggerEffect(effects) {effects && effects.forEach(effect => {if(effect.scheduler) {effect.scheduler()} else {effect.run()}})
}function track(target,type,key) {if(!activeEffect) return ;let depsMap = targetMap.get(target);if(!depsMap) {targetMap.set(target,(depsMap = new Map()))}let dep = depsMap.get(key);if(!dep) {depsMap.set(key,(dep = new Set()))}trackEffect(dep)
}function trackEffect(dep) {if(activeEffect) {let shouldTrack =!dep.has(activeEffect);if(shouldTrack) {dep.add(activeEffect)activeEffect.deps.push(dep);}}
}const target ={name: 'sandy',age: 18,height: 195};
const r1 = reactive(target);const formatData = computed (() => {const {name,age} = r1;return `format123 ${name},${age}`
})
effect(() => {const { value } = formatData;console.log(`effect ${value}`)
})
setTimeout(() => {r1.name = 'wendy'
},2000)// setTimeout(() => {//     console.log(formatData.value,'formatData.value')
// },3000)

二. 调度器

为的是方便于当监听值改变后,自定义逻辑代码

//声明constructor(getter,setter) {this.effect = new ReactiveEffect(getter, () => {// 稍后依赖的属性变化会执行此调度函数if(!this._dirty) {this._dirty = true;triggerEffect(this.dep)}})}//调用function triggerEffect(effects) {effects && effects.forEach(effect => {if(effect.scheduler) {effect.scheduler()} else {effect.run()}})
}

三. 计算属性逻辑梳理

  1. 相关调用代码
const target ={name: 'sandy',age: 18,height: 195};
const r1 = reactive(target);const formatData = computed (() => {const {name,age} = r1;return `format123 ${name},${age}`
})
effect(() => {const { value } = formatData;console.log(`effect ${value}`)
})
setTimeout(() => {r1.name = 'wendy'
},2000)
  1. 逻辑梳理及触发顺序

    1. computed里面的函数执行,并完成初始化操作,此时fn和调度器均已完成初始化操作;
    2. effect里面的函数立即执行一次,因为函数里面访问了计算属性的value属性,所以会先访问ComputedRefImpl类中的get;
    3. trackEffect(this.dep) 和 this._value = this.effect.run()完成了响应数据属性的依赖收集;
    4. 当2s之后改变属性值之后,会先触发proxy中的set,调用完成的trigger函数,然后再触发ComputedRefImpl类中的调度器函 数;
    5. 调度器函数执行完之后,则computed计算属性更新完毕,则继续执行effcet中函数的代码逻辑。

总结:因为effcet函数作用域使用了计算属性的值,所以核心代码逻辑是 :响应数据更改 ——》计算属性值更改 ——》effect执行完毕

四. 计算属性的缓存特性实现

声明了_dirty属性,_dirty默认为true,当_dirty为true时,则表明需重新计算,为false时,不需要重新计算。
当属性值改变时,在调度器中将_dirty设置为true,表示之后访问时需要重新计算;
当访问了计算属性的value之后,表示已经访问过了,不需要重新计算,将_dirty设置为false

Vue源码系列4:模拟实现vue3.x中的计算属性相关推荐

  1. 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事

    作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...

  2. Vue源码系列 - 前言

    Vue源码系列 前言 - Why 离职前立了 flag,希望正式入职之后可以在组内进行一次 Vue 的源码分享.为了让小旗子不要那么容易就倒下,也希望自己能有所收获,努力来总结一些自己的心得. 参考资 ...

  3. 大白话Vue源码系列(01):万事开头难

    阅读目录 Vue 的源码目录结构 预备知识 先捡软的捏 Angular 是 Google 亲儿子,React 是 Facebook 小正太,那咱为啥偏偏选择了 Vue 下手,一句话,Vue 是咱见过的 ...

  4. vue3.0中使用计算属性时报错

    原因:在组件使用了双向绑定,绑定的值是vuex的state的一个值.vuex是单项数据流,在计算属性中只有get可以获取参数值,没有set不能改变参数值. crowdmodel:{get(){retu ...

  5. 03-做一个通读Vue源码的计划吧

    先梳理一下此时我的理解. src目录: core Vue的核心,专注于数据驱动,响应式 platform 各个平台的移植,负责模板的真正渲染工作 compiler 编译器,把字符串模板转换成rende ...

  6. diff算法_深入剖析Vue源码 - 来,跟我一起实现diff算法!__Vue.js

    这一节,依然是深入剖析Vue源码系列,上几节内容介绍了Virtual DOM是Vue在渲染机制上做的优化,而渲染的核心在于数据变化时,如何高效的更新节点,这就是diff算法.由于源码中关于diff算法 ...

  7. Vue源码流程图(函数名与源码对应)

    这里写目录标题 概览 1. 变化侦查 1.1 Observer流程图 2. vdom虚拟DOM 2.1 创建节点createElm 2.2 更新节点patchVnode 2.3 更新子节点 updat ...

  8. Vue源码学习之Computed与Watcher原理

    前言  computed与watch是我们在vue中常用的操作,computed是一个惰性求值观察者,具有缓存性,只有当依赖发生变化,第一次访问computed属性,才会计算新的值.而watch则是当 ...

  9. 【Vue原理】Vue源码阅读总结大会

    专注 Vue 源码分享,为了方便大家理解,分为了白话版和 源码版,白话版可以轻松理解工作原理和设计思想,源码版可以更清楚内部操作和 Vue的美,喜欢我就关注我的公众号,好吧兄弟,不会让你失望的 阅读源 ...

最新文章

  1. Hibernate学习之hibernate状态
  2. 受限玻尔兹曼机——用在推荐系统里
  3. 【ARM】ARM汇编程序设计(六) stm和ldm
  4. Android使用的设计模式2——策略模式
  5. echarts画中国地图!
  6. 想要学习UI动效设计?从这些软件入手
  7. linux kernel下输入输出console如何实现
  8. Java文档对象_java从文件读取对象
  9. ubuntu常见问题有效解决办法
  10. 如何用iMazing备份和恢复辐射避难所
  11. CentOS安装中文包
  12. 华为 会议室分配时间最长_解决方案—会议室预约多入口超融合
  13. CFA大起底:三百六十度无死角详解CFA到底是个啥?
  14. 据说大学录取率是90%,我对大米的期许是考上高中
  15. Overvoltage category (过电压类别, 过电压等级)
  16. Mac 下 Docker搭建RAP2 记录
  17. pytorch中的MSELoss函数
  18. 实时日志监控系统-全览
  19. 关于Oralce OAM/OIM及与P6/Unifier 集成SSO的想法
  20. 数据中台实战(一):以B2B电商亿订为例,谈谈产品经理视角下的数据埋点

热门文章

  1. aria2 txt导入_Aria2使用教程
  2. 如何做一场年会直播?
  3. 微信如何批量添加好友?
  4. 斯坦福机器学习Coursera课程:第八次作业--推荐系统
  5. 从手Q与微信之争,看腾讯内在的真实矛盾与战略
  6. Arduino开发实例-旋转编码器RGB-LED调光
  7. HT74153 6V/2A/1.2MHz 同步降压转换器 IC
  8. 阿里云OSS集成百度Ueidtor
  9. 将Materials Studio导出的pdb文件转换成LAMMPS所用的data坐标文件(含程序)
  10. 网亚机房管理软件系统 v6.0.9.1 免费