Vue3的Composition API简易实现-computed__Vue.js
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利
主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green
贡献主题:https://github.com/xitu/juejin-markdown-themes
theme: smartblue highlight: atom-one-dark
前言
vue3的reactivity源码地址
- reactivity 响应式系统
- 实现的composition API有:
- computed
- 默认不执行, 通过
lazy = true
是去判断 - 多次取值时要实现缓存, 通过
dirty = true
, 判断是脏的 就去执行传递的函数, 然后dirty = false
- 将传递的函数封装成effect, 将其将函数里的依赖的响应式变量 收集computed effect
- computed取值时要做依赖收集track(vue2 是依赖值做的全部收集 即收集computed watcher , 也要收集渲染 watcher)
- 当依赖值发生变化时, 发布effect时 判断当前effect是 computed effect
effect.options.scheduler
- 将
dirty = true
, 并且trigger, computed取值时收集的effect - 具体以下方示例为主
示例
<script src="../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
<div id="app"></div>
<script>
let { effect, reactive, ref, shallowRef, toRef, toRefs, computed } = VueReactivity
const age = ref(18)
// 此方法默认不会被执行
const myAge = computed(() => {
return age.value + 10
})
// 另一种写法
// const myAge = computed({
// get(){},
// set(){}
// })
// 当访问属性的时候执行 第二次不执行 实现缓存
// console.log(myAge.value)
// console.log(myAge.value)
// age.value = 100 // 更新age,myAge不会立刻重新计算
// myAge.value // 再次计算最新值
effect(() => {
app.innerHTML = myAge.value
})
setTimeout(() => {
age.value = 100
}, 2000)
</script>
+---------------------+ +----------------------+
| | | |
| 28 +--->| 110 +
| | | |
+---------------------+ +----------------------+
shared
vue3的shared源码地址
- shared: 多个包之间共享的内容
// 是不是个对象
export const isObject = (value) => typeof value == 'object' && value !== null
// 合并对象
export const extend = Object.assign
// 是不是数组
export const isArray = Array.isArray
// 是不是函数
export const isFunction = (value) => typeof value == 'function'
// 是不是数字
export const isNumber = (value) => typeof value == 'number'
// 是不是字符
export const isString = (value) => typeof value === 'string'
// 是不是正整数
export const isIntegerKey = (key) => parseInt(key) + '' === key
// 是不是自己的属性
let hasOwnpRroperty = Object.prototype.hasOwnProperty
export const hasOwn = (target, key) => hasOwnpRroperty.call(target, key)
// 是不是同一个值
export const hasChanged = (oldValue,value) => oldValue !== value
computed.ts
import { isFunction } from "@vue/shared/src"
import { effect, track, trigger } from "./effect"
import { TrackOpTypes, TriggerOrTypes } from "./operators"
class ComputedRefImpl {
public _dirty = true // 用于缓存
public _value;
public effect;
constructor(getter, public setter) {
this.effect = effect(getter, {
lazy: true, // 默认不执行
scheduler: () => {
if (!this._dirty) {
this._dirty = true
trigger(this, TriggerOrTypes.SET, 'value')
}
}
})
}
get value() {
if (this._dirty) {
this._value = this.effect()
this._dirty = false
}
track(this, TrackOpTypes.GET, 'value')
return this._value
}
set value(newValue) {
this.setter(newValue)
}
}
export function computed(getterOrOptions) {
let getter;
let setter;
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = () => {
console.warn('computed value must be readonly')
}
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
return new ComputedRefImpl(getter, setter)
}
/**
* @description 寻找属性对应的effect 让其执行(这里只有数组和对象)
* @param target 目标
* @param type 类型 新增 或者 修改
* @param key key
* @param newValue 新值
* @param oldValue 老值
*/
export function trigger(target, type, key?, newValue?, oldValue?) {
const depsMap = targetMap.get(target)
if (!depsMap) return
// 要发布的effect去重
// 将所有的要执行的effect 全部存到一个新的集合中 最终一起执行
const effects = new Set()
const add = (effectsToAdd) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => effects.add(effect))
}
}
// 数组 修改长度 traget.length = newValue
// 如 [1,2,3,4,5] => [1,2,3,4,5].length = 1
if (key === 'length' && isArray(target)) {
depsMap.forEach((dep, key) => {
if (key === 'length' || key > newValue) {
add(dep)
}
})
} else {
// 可能是对象
// 这里肯定是修改 不能是新增
// 如果是新增 depsMap.get(key) -> undefined
if (key !== undefined) {
add(depsMap.get(key))
}
// 如果修改数组中的某个索引
// 如 arr=[1,2,3] -> arr[100]=1
switch (type) {
case TriggerOrTypes.ADD:
if (isArray(target) && isIntegerKey(key)) {
add(depsMap.get('length'))
}
break;
}
}
// 发布 看这里
effects.forEach((effect: any) => {
if(effect.options.scheduler){
effect.options.scheduler(effect)
}else{
effect()
}
})
}
完
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 小桂summer 原文链接:https://juejin.im/post/6933852528987602957