feat: skip constant trees and memoize fn

This commit is contained in:
Evan You 2018-10-28 16:41:29 -04:00
parent d8cda2387f
commit 131936f144
2 changed files with 61 additions and 40 deletions

View File

@ -7,7 +7,6 @@ import {
MountedVNode, MountedVNode,
RenderNode, RenderNode,
createTextVNode, createTextVNode,
cloneVNode,
Ref, Ref,
VNodeChildren VNodeChildren
} from './vdom' } from './vdom'
@ -135,10 +134,6 @@ export function createRenderer(options: RendererOptions) {
endNode: RenderNode | null endNode: RenderNode | null
) { ) {
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
let child = children[i]
if (child.el) {
children[i] = child = cloneVNode(child)
}
mount(children[i], container, contextVNode, isSVG, endNode) mount(children[i], container, contextVNode, isSVG, endNode)
} }
} }
@ -346,6 +341,11 @@ export function createRenderer(options: RendererOptions) {
contextVNode: MountedVNode | null, contextVNode: MountedVNode | null,
isSVG: boolean isSVG: boolean
) { ) {
if (prevVNode === nextVNode) {
nextVNode.el = prevVNode.el
return
}
const nextFlags = nextVNode.flags const nextFlags = nextVNode.flags
const prevFlags = prevVNode.flags const prevFlags = prevVNode.flags
@ -768,19 +768,12 @@ export function createRenderer(options: RendererOptions) {
for (i; i < commonLength; i++) { for (i; i < commonLength; i++) {
nextChild = nextChildren[i] nextChild = nextChildren[i]
prevChild = prevChildren[i] prevChild = prevChildren[i]
if (nextChild.el) {
nextChildren[i] = nextChild = cloneVNode(nextChild)
}
patch(prevChild, nextChild, container, contextVNode, isSVG) patch(prevChild, nextChild, container, contextVNode, isSVG)
prevChildren[i] = nextChild as MountedVNode prevChildren[i] = nextChild as MountedVNode
} }
if (prevLength < nextLength) { if (prevLength < nextLength) {
for (i = commonLength; i < nextLength; i++) { for (i = commonLength; i < nextLength; i++) {
nextChild = nextChildren[i] mount(nextChildren[i], container, contextVNode, isSVG, endNode)
if (nextChild.el) {
nextChildren[i] = nextChild = cloneVNode(nextChild)
}
mount(nextChild, container, contextVNode, isSVG, endNode)
} }
} else if (prevLength > nextLength) { } else if (prevLength > nextLength) {
for (i = commonLength; i < prevLength; i++) { for (i = commonLength; i < prevLength; i++) {
@ -810,9 +803,6 @@ export function createRenderer(options: RendererOptions) {
outer: { outer: {
// Sync nodes with the same key at the beginning. // Sync nodes with the same key at the beginning.
while (prevVNode.key === nextVNode.key) { while (prevVNode.key === nextVNode.key) {
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, contextVNode, isSVG) patch(prevVNode, nextVNode, container, contextVNode, isSVG)
prevChildren[j] = nextVNode as MountedVNode prevChildren[j] = nextVNode as MountedVNode
j++ j++
@ -828,9 +818,6 @@ export function createRenderer(options: RendererOptions) {
// Sync nodes with the same key at the end. // Sync nodes with the same key at the end.
while (prevVNode.key === nextVNode.key) { while (prevVNode.key === nextVNode.key) {
if (nextVNode.el) {
nextChildren[nextEnd] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, contextVNode, isSVG) patch(prevVNode, nextVNode, container, contextVNode, isSVG)
prevChildren[prevEnd] = nextVNode as MountedVNode prevChildren[prevEnd] = nextVNode as MountedVNode
prevEnd-- prevEnd--
@ -849,11 +836,7 @@ export function createRenderer(options: RendererOptions) {
const nextNode = const nextNode =
nextPos < nextLength ? nextChildren[nextPos].el : endNode nextPos < nextLength ? nextChildren[nextPos].el : endNode
while (j <= nextEnd) { while (j <= nextEnd) {
nextVNode = nextChildren[j] nextVNode = nextChildren[j++]
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
j++
mount(nextVNode, container, contextVNode, isSVG, nextNode) mount(nextVNode, container, contextVNode, isSVG, nextNode)
} }
} }
@ -896,9 +879,6 @@ export function createRenderer(options: RendererOptions) {
} else { } else {
pos = j pos = j
} }
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, contextVNode, isSVG) patch(prevVNode, nextVNode, container, contextVNode, isSVG)
patched++ patched++
break break
@ -940,9 +920,6 @@ export function createRenderer(options: RendererOptions) {
} else { } else {
pos = j pos = j
} }
if (nextVNode.el) {
nextChildren[j] = nextVNode = cloneVNode(nextVNode)
}
patch(prevVNode, nextVNode, container, contextVNode, isSVG) patch(prevVNode, nextVNode, container, contextVNode, isSVG)
patched++ patched++
} else if (!canRemoveWholeContent) { } else if (!canRemoveWholeContent) {
@ -971,9 +948,6 @@ export function createRenderer(options: RendererOptions) {
if (sources[i] === 0) { if (sources[i] === 0) {
pos = i + nextStart pos = i + nextStart
nextVNode = nextChildren[pos] nextVNode = nextChildren[pos]
if (nextVNode.el) {
nextChildren[pos] = nextVNode = cloneVNode(nextVNode)
}
nextPos = pos + 1 nextPos = pos + 1
mount( mount(
nextVNode, nextVNode,
@ -1002,9 +976,6 @@ export function createRenderer(options: RendererOptions) {
if (sources[i] === 0) { if (sources[i] === 0) {
pos = i + nextStart pos = i + nextStart
nextVNode = nextChildren[pos] nextVNode = nextChildren[pos]
if (nextVNode.el) {
nextChildren[pos] = nextVNode = cloneVNode(nextVNode)
}
nextPos = pos + 1 nextPos = pos + 1
mount( mount(
nextVNode, nextVNode,
@ -1119,8 +1090,8 @@ export function createRenderer(options: RendererOptions) {
} else { } else {
platformRemoveChild(container, el) platformRemoveChild(container, el)
} }
;(vnode as any).el = null
} }
;(vnode as any).el = null
} }
function removeChildren( function removeChildren(
@ -1394,9 +1365,6 @@ export function createRenderer(options: RendererOptions) {
container: any container: any
): ComponentInstance | null { ): ComponentInstance | null {
const prevVNode = container.vnode const prevVNode = container.vnode
if (vnode && vnode.el) {
vnode = cloneVNode(vnode)
}
if (prevVNode == null) { if (prevVNode == null) {
if (vnode) { if (vnode) {
mount(vnode, container, null, false, null) mount(vnode, container, null, false, null)

View File

@ -0,0 +1,53 @@
// Used for memoizing trees inside render functions.
//
// Example (equivalent of v-once):
//
// render() {
// return memoize(h('div', this.msg), this, 0)
// }
//
// Memoize baesd on keys:
//
// render() {
// return memoize(h('div', this.msg + this.count), this, 0, [this.msg])
// }
import { Component } from '../component'
import { warn } from '../warning'
const memoizeMap = new WeakMap()
export function memoize(
getter: () => any,
instance: Component,
id: number,
keys?: any[]
): any {
if (__DEV__ && !Array.isArray(keys)) {
warn(
`keys passed to v-memo or memoize must be an array. Got ${String(keys)}`
)
}
let storage = memoizeMap.get(instance)
if (!storage) {
storage = []
memoizeMap.set(instance, storage)
}
const record = storage[id]
if (!record) {
const value = getter()
storage[id] = [value, keys]
return value
} else {
const [prevValue, prevKeys] = record
record[1] = keys
if (keys) {
for (let i = 0; i < keys.length; i++) {
if (keys[i] !== prevKeys[i]) {
return (record[0] = getter())
}
}
}
return prevValue
}
}