wip: optimize w/ shapeFlag

This commit is contained in:
Evan You 2019-06-02 16:35:19 +08:00
parent b77709286f
commit 2f1f6b4355
5 changed files with 49 additions and 26 deletions

View File

@ -70,10 +70,13 @@ function run(effect: ReactiveEffect, fn: Function, args: any[]): any {
} }
export function cleanup(effect: ReactiveEffect) { export function cleanup(effect: ReactiveEffect) {
for (let i = 0; i < effect.deps.length; i++) { const { deps } = effect
effect.deps[i].delete(effect) if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect)
}
deps.length = 0
} }
effect.deps.length = 0
} }
export function track( export function track(

View File

@ -5,11 +5,12 @@ import {
observable, observable,
immutable immutable
} from '@vue/observer' } from '@vue/observer'
import { isFunction, EMPTY_OBJ } from '@vue/shared' import { EMPTY_OBJ } from '@vue/shared'
import { RenderProxyHandlers } from './componentProxy' import { RenderProxyHandlers } from './componentProxy'
import { ComponentPropsOptions, ExtractPropTypes } from './componentProps' import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags' import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags'
import { Slots } from './componentSlots' import { Slots } from './componentSlots'
import { STATEFUL_COMPONENT } from './shapeFlags'
export type Data = { [key: string]: any } export type Data = { [key: string]: any }
@ -148,16 +149,17 @@ export function setupStatefulComponent(instance: ComponentInstance) {
} }
export function renderComponentRoot(instance: ComponentInstance): VNode { export function renderComponentRoot(instance: ComponentInstance): VNode {
const { type: Component, renderProxy } = instance const { type: Component, vnode } = instance
if (isFunction(Component)) { if (vnode.shapeFlag & STATEFUL_COMPONENT) {
return normalizeVNode(Component(instance)) if (__DEV__ && !(Component as any).render) {
} else {
if (__DEV__ && !Component.render) {
// TODO warn missing render // TODO warn missing render
} }
return normalizeVNode( return normalizeVNode(
(Component.render as Function).call(renderProxy, instance) (Component as any).render.call(instance.renderProxy, instance)
) )
} else {
// functional
return normalizeVNode((Component as FunctionalComponent)(instance))
} }
} }

View File

@ -14,14 +14,7 @@ import {
createComponentInstance, createComponentInstance,
setupStatefulComponent setupStatefulComponent
} from './component' } from './component'
import { import { isString, isArray, EMPTY_OBJ, EMPTY_ARR } from '@vue/shared'
isString,
isArray,
isFunction,
isObject,
EMPTY_OBJ,
EMPTY_ARR
} from '@vue/shared'
import { import {
TEXT, TEXT,
CLASS, CLASS,
@ -35,6 +28,7 @@ import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
import { effect, stop, ReactiveEffectOptions } from '@vue/observer' import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
import { resolveProps } from './componentProps' import { resolveProps } from './componentProps'
import { resolveSlots } from './componentSlots' import { resolveSlots } from './componentSlots'
import { ELEMENT, STATEFUL_COMPONENT, FUNCTIONAL_COMPONENT } from './shapeFlags'
const prodEffectOptions = { const prodEffectOptions = {
scheduler: queueJob scheduler: queueJob
@ -108,7 +102,7 @@ export function createRenderer(options: RendererOptions) {
n2: VNode, n2: VNode,
container: HostNode, container: HostNode,
anchor?: HostNode, anchor?: HostNode,
optimized?: boolean optimized: boolean = false
) { ) {
// patching & not same type, unmount old tree // patching & not same type, unmount old tree
if (n1 != null && !isSameType(n1, n2)) { if (n1 != null && !isSameType(n1, n2)) {
@ -117,7 +111,7 @@ export function createRenderer(options: RendererOptions) {
n1 = null n1 = null
} }
const { type } = n2 const { type, shapeFlag } = n2
switch (type) { switch (type) {
case Text: case Text:
processText(n1, n2, container, anchor) processText(n1, n2, container, anchor)
@ -132,10 +126,14 @@ export function createRenderer(options: RendererOptions) {
processPortal(n1, n2, container, anchor, optimized) processPortal(n1, n2, container, anchor, optimized)
break break
default: default:
if (isString(type)) { if (shapeFlag & ELEMENT) {
processElement(n1, n2, container, anchor, optimized) processElement(n1, n2, container, anchor, optimized)
} else { } else {
if (__DEV__ && !isFunction(type) && !isObject(type)) { if (
__DEV__ &&
!(shapeFlag & STATEFUL_COMPONENT) &&
!(shapeFlag & FUNCTIONAL_COMPONENT)
) {
// TODO warn invalid node type // TODO warn invalid node type
debugger debugger
} }
@ -453,14 +451,14 @@ export function createRenderer(options: RendererOptions) {
const instance: ComponentInstance = (vnode.component = createComponentInstance( const instance: ComponentInstance = (vnode.component = createComponentInstance(
Component Component
)) ))
instance.update = effect(() => { instance.update = effect(function updateComponent() {
if (!instance.vnode) { if (instance.vnode === null) {
// initial mount // initial mount
instance.vnode = vnode instance.vnode = vnode
resolveProps(instance, vnode.props, Component.props) resolveProps(instance, vnode.props, Component.props)
resolveSlots(instance, vnode.children) resolveSlots(instance, vnode.children)
// setup stateful // setup stateful
if (typeof Component === 'object') { if (vnode.shapeFlag & STATEFUL_COMPONENT) {
setupStatefulComponent(instance) setupStatefulComponent(instance)
} }
const subTree = (instance.subTree = renderComponentRoot(instance)) const subTree = (instance.subTree = renderComponentRoot(instance))

View File

@ -0,0 +1,3 @@
export const ELEMENT = 1
export const FUNCTIONAL_COMPONENT = 1 << 1
export const STATEFUL_COMPONENT = 1 << 2

View File

@ -3,6 +3,7 @@ import { ComponentInstance } from './component'
import { HostNode } from './createRenderer' import { HostNode } from './createRenderer'
import { RawSlots } from './componentSlots' import { RawSlots } from './componentSlots'
import { CLASS } from './patchFlags' import { CLASS } from './patchFlags'
import { ELEMENT, FUNCTIONAL_COMPONENT, STATEFUL_COMPONENT } from './shapeFlags'
export const Fragment = Symbol('Fragment') export const Fragment = Symbol('Fragment')
export const Text = Symbol('Text') export const Text = Symbol('Text')
@ -36,6 +37,7 @@ export interface VNode {
target: HostNode | null // portal target target: HostNode | null // portal target
// optimization only // optimization only
shapeFlag: number
patchFlag: number patchFlag: number
dynamicProps: string[] | null dynamicProps: string[] | null
dynamicChildren: VNode[] | null dynamicChildren: VNode[] | null
@ -93,6 +95,15 @@ export function createVNode(
): VNode { ): VNode {
// Allow passing 0 for props, this can save bytes on generated code. // Allow passing 0 for props, this can save bytes on generated code.
props = props || null props = props || null
const shapeFlag = isString(type)
? ELEMENT
: isFunction(type)
? FUNCTIONAL_COMPONENT
: isObject(type)
? STATEFUL_COMPONENT
: 0
const vnode: VNode = { const vnode: VNode = {
type, type,
props, props,
@ -102,6 +113,7 @@ export function createVNode(
el: null, el: null,
anchor: null, anchor: null,
target: null, target: null,
shapeFlag,
patchFlag, patchFlag,
dynamicProps, dynamicProps,
dynamicChildren: null dynamicChildren: null
@ -123,7 +135,12 @@ export function createVNode(
// component nodes also should always be tracked, because even if the // component nodes also should always be tracked, because even if the
// component doesn't need to update, it needs to persist the instance on to // component doesn't need to update, it needs to persist the instance on to
// the next vnode so that it can be properly unmounted later. // the next vnode so that it can be properly unmounted later.
if (shouldTrack && (patchFlag || isObject(type) || isFunction(type))) { if (
shouldTrack &&
(patchFlag ||
shapeFlag & STATEFUL_COMPONENT ||
shapeFlag & FUNCTIONAL_COMPONENT)
) {
trackDynamicNode(vnode) trackDynamicNode(vnode)
} }