feat: v-memo

This commit is contained in:
Evan You
2021-07-09 21:41:44 -04:00
parent 5cea9a1d4e
commit 3b64508e3b
23 changed files with 563 additions and 131 deletions

View File

@@ -1,4 +1,4 @@
import { VNodeChild } from '../vnode'
import { VNode, VNodeChild } from '../vnode'
import { isArray, isString, isObject } from '@vue/shared'
import { warn } from '../warning'
@@ -52,13 +52,17 @@ export function renderList<T>(
*/
export function renderList(
source: any,
renderItem: (...args: any[]) => VNodeChild
renderItem: (...args: any[]) => VNodeChild,
cache?: any[],
index?: number
): VNodeChild[] {
let ret: VNodeChild[]
const cached = (cache && cache[index!]) as VNode[] | undefined
if (isArray(source) || isString(source)) {
ret = new Array(source.length)
for (let i = 0, l = source.length; i < l; i++) {
ret[i] = renderItem(source[i], i)
ret[i] = renderItem(source[i], i, undefined, cached && cached[i])
}
} else if (typeof source === 'number') {
if (__DEV__ && !Number.isInteger(source)) {
@@ -71,17 +75,23 @@ export function renderList(
}
} else if (isObject(source)) {
if (source[Symbol.iterator as any]) {
ret = Array.from(source as Iterable<any>, renderItem)
ret = Array.from(source as Iterable<any>, (item, i) =>
renderItem(item, i, undefined, cached && cached[i])
)
} else {
const keys = Object.keys(source)
ret = new Array(keys.length)
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
ret[i] = renderItem(source[key], key, i)
ret[i] = renderItem(source[key], key, i, cached && cached[i])
}
}
} else {
ret = []
}
if (cache) {
cache[index!] = ret
}
return ret
}

View File

@@ -0,0 +1,29 @@
import { currentBlock, isBlockTreeEnabled, VNode } from '../vnode'
export function withMemo(
memo: any[],
render: () => VNode,
cache: any[],
index: number
) {
const cached = cache[index] as VNode | undefined
if (cached && isMemoSame(cached.memo!, memo)) {
// make sure to let parent block track it when returning cached
if (isBlockTreeEnabled > 0 && currentBlock) {
currentBlock.push(cached)
}
return cached
}
const ret = render()
ret.memo = memo
return (cache[index] = ret)
}
export function isMemoSame(prev: any[], next: any[]) {
for (let i = 0; i < prev.length; i++) {
if (prev[i] !== next[i]) {
return false
}
}
return true
}

View File

@@ -264,6 +264,7 @@ export { renderList } from './helpers/renderList'
export { toHandlers } from './helpers/toHandlers'
export { renderSlot } from './helpers/renderSlot'
export { createSlots } from './helpers/createSlots'
export { withMemo, isMemoSame } from './helpers/withMemo'
export {
openBlock,
createBlock,

View File

@@ -182,6 +182,9 @@ export interface VNode<
// application root node only
appContext: AppContext | null
// v-for memo
memo?: any[]
}
// Since v-if and v-for are the two possible ways node structure can dynamically
@@ -221,7 +224,7 @@ export function closeBlock() {
// Only tracks when this value is > 0
// We are not using a simple boolean because this value may need to be
// incremented/decremented by nested usage of v-once (see below)
let isBlockTreeEnabled = 1
export let isBlockTreeEnabled = 1
/**
* Block tracking sometimes needs to be disabled, for example during the
@@ -692,7 +695,7 @@ export function normalizeVNode(child: VNodeChild): VNode {
// optimized normalization for template-compiled render fns
export function cloneIfMounted(child: VNode): VNode {
return child.el === null ? child : cloneVNode(child)
return child.el === null || child.memo ? child : cloneVNode(child)
}
export function normalizeChildren(vnode: VNode, children: unknown) {