Skip to content

Commit

Permalink
feat: v-memo for renderEffect
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed Sep 19, 2024
1 parent e42c9c0 commit 3fbf98c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
11 changes: 10 additions & 1 deletion packages/runtime-vapor/src/apiCreateFor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { currentInstance } from './component'
import { componentKey } from './component'
import type { DynamicSlot } from './componentSlots'
import { renderEffect } from './renderEffect'
import { withMemo } from './memo'

interface ForBlock extends Fragment {
scope: EffectScope
Expand Down Expand Up @@ -264,7 +265,15 @@ export const createFor = (
memo: getMemo && getMemo(item, key, index),
[fragmentKey]: true,
})
block.nodes = scope.run(() => renderItem(state))!
block.nodes = scope.run(() => {
if (getMemo) {
return withMemo(
() => block.memo!,
() => renderItem(state),
)
}
return renderItem(state)
})!

// TODO v-memo
// if (getMemo) block.update()
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-vapor/src/memo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const memoStack: Array<() => any[]> = []

export function withMemo<T>(memo: () => any[], callback: () => T): T {
memoStack.push(memo)
const res = callback()
memoStack.pop()
return res
}
30 changes: 30 additions & 0 deletions packages/runtime-vapor/src/renderEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
queuePostFlushCb,
} from './scheduler'
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
import { memoStack } from './memo'

export function renderEffect(cb: () => void): void {
const instance = getCurrentInstance()
Expand All @@ -32,6 +33,13 @@ export function renderEffect(cb: () => void): void {
job.id = instance.uid
}

let memos: (() => any[])[] | undefined
let memoCaches: any[][]
if (memoStack.length) {
memos = Array.from(memoStack)
memoCaches = memos.map(memo => memo())
}

const effect = new ReactiveEffect(() =>
callWithAsyncErrorHandling(cb, instance, VaporErrorCodes.RENDER_FUNCTION),
)
Expand All @@ -52,6 +60,28 @@ export function renderEffect(cb: () => void): void {
return
}

if (memos) {
let dirty: boolean | undefined
for (let i = 0; i < memos.length; i++) {
const memo = memos[i]
const cache = memoCaches[i]
const value = memo()

for (let j = 0; j < Math.max(value.length, cache.length); j++) {
if (value[j] !== cache[j]) {
dirty = true
break
}
}

memoCaches[i] = value
}

if (!dirty) {
return
}
}

const reset = instance && setCurrentInstance(instance)

if (instance && instance.isMounted && !instance.isUpdating) {
Expand Down
23 changes: 23 additions & 0 deletions playground/src/for-memo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import { reactive, ref } from 'vue'
const arr = reactive(['foo', 'bar', 'baz', 'qux'])
const selected = ref('foo')
</script>

<template>
<div
v-for="item of arr"
v-memo="[selected === item]"
:class="{ danger: selected === item }"
@click="selected = item"
>
{{ item }}
</div>
</template>

<style>
.danger {
color: red;
}
</style>

0 comments on commit 3fbf98c

Please sign in to comment.