Skip to content

Commit

Permalink
feat(runtime-vapor): extract apiSetup & init setup ctx
Browse files Browse the repository at this point in the history
  • Loading branch information
Doctor-wu committed Mar 17, 2024
1 parent 3f155f2 commit a0b5653
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 46 deletions.
2 changes: 1 addition & 1 deletion packages/runtime-vapor/src/apiCreateComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
createComponentInstance,
currentInstance,
} from './component'
import { setupComponent } from './apiRender'
import { setupComponent } from './apiSetup'
import type { RawProps } from './componentProps'

export function createComponent(comp: Component, rawProps: RawProps = null) {
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-vapor/src/apiCreateVaporApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import {
} from './component'
import { warn } from './warning'
import { version } from '.'
import { render, setupComponent, unmountComponent } from './apiRender'
import { render, unmountComponent } from './apiRender'
import type { RawProps } from './componentProps'
import { setupComponent } from './apiSetup'

export function createVaporApp(
rootComponent: Component,
Expand Down
41 changes: 1 addition & 40 deletions packages/runtime-vapor/src/apiRender.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { isArray, isFunction, isObject } from '@vue/shared'
import { type ComponentInternalInstance, setCurrentInstance } from './component'
import type { ComponentInternalInstance } from './component'
import { insert, querySelector, remove } from './dom/element'
import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler'
import { proxyRefs } from '@vue/reactivity'
import { invokeLifecycle } from './componentLifecycle'
import { VaporLifecycleHooks } from './apiLifecycle'

Expand All @@ -15,43 +13,6 @@ export type Fragment = {
[fragmentKey]: true
}

export function setupComponent(instance: ComponentInternalInstance): void {
const reset = setCurrentInstance(instance)
instance.scope.run(() => {
const { component, props, emit, attrs } = instance
const ctx = { expose: () => {}, emit, attrs }

const setupFn = isFunction(component) ? component : component.setup
const stateOrNode = setupFn && setupFn(props, ctx)

let block: Block | undefined

if (
stateOrNode &&
(stateOrNode instanceof Node ||
isArray(stateOrNode) ||
(stateOrNode as any)[fragmentKey])
) {
block = stateOrNode as Block
} else if (isObject(stateOrNode)) {
instance.setupState = proxyRefs(stateOrNode)
}
if (!block && component.render) {
block = component.render(instance.setupState)
}

if (block instanceof DocumentFragment) {
block = Array.from(block.childNodes)
}
if (!block) {
// TODO: warn no template
block = []
}
return (instance.block = block)
})
reset()
}

export function render(
instance: ComponentInternalInstance,
container: string | ParentNode,
Expand Down
66 changes: 66 additions & 0 deletions packages/runtime-vapor/src/apiSetup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { proxyRefs } from '@vue/reactivity'
import { isArray, isFunction, isObject } from '@vue/shared'
import { type ComponentInternalInstance, setCurrentInstance } from './component'
import { getAttrsProxy } from './componentAttrs'
// import { SetupContext } from 'vue'
import { type Block, fragmentKey } from './apiRender'

export function setupComponent(instance: ComponentInternalInstance): void {
const reset = setCurrentInstance(instance)
instance.scope.run(() => {
const { component, props } = instance
const ctx = createSetupContext(instance)

const setupFn = isFunction(component) ? component : component.setup
const stateOrNode = setupFn && setupFn(props, ctx)

let block: Block | undefined

if (
stateOrNode &&
(stateOrNode instanceof Node ||
isArray(stateOrNode) ||
(stateOrNode as any)[fragmentKey])
) {
block = stateOrNode as Block
} else if (isObject(stateOrNode)) {
instance.setupState = proxyRefs(stateOrNode)
}
if (!block && component.render) {
block = component.render(instance.setupState)
}

if (block instanceof DocumentFragment) {
block = Array.from(block.childNodes)
}
if (!block) {
// TODO: warn no template
block = []
}
return (instance.block = block)
})
reset()
}

export function createSetupContext(instance: ComponentInternalInstance): any {
if (__DEV__) {
// We use getters in dev in case libs like test-utils overwrite instance
// properties (overwrites should not be done in prod)
return Object.freeze({
expose: () => {},
get attrs() {
return getAttrsProxy(instance)
},
get emit() {
return (event: string, ...args: any[]) => instance.emit(event, ...args)
},
})
} else {
return {
get attrs() {
return getAttrsProxy(instance)
},
emit: instance.emit,
}
}
}
8 changes: 5 additions & 3 deletions packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export interface ComponentInternalInstance {
*/
inheritAttrs?: boolean

attrsProxy: Data | null

// lifecycle
isMounted: boolean
isUnmounted: boolean
Expand Down Expand Up @@ -176,6 +178,9 @@ export function createComponentInstance(
attrs: EMPTY_OBJ,
refs: EMPTY_OBJ,

inheritAttrs: component.inheritAttrs,
attrsProxy: null,

// lifecycle
isMounted: false,
isUnmounted: false,
Expand Down Expand Up @@ -230,9 +235,6 @@ export function createComponentInstance(
*/
// [VaporLifecycleHooks.SERVER_PREFETCH]: null,
}
if (component.inheritAttrs != null) {
instance.inheritAttrs = component.inheritAttrs
}
initProps(instance, rawProps, !isFunction(component))
instance.emit = emit.bind(null, instance)

Expand Down
46 changes: 45 additions & 1 deletion packages/runtime-vapor/src/componentAttrs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { camelize, isFunction } from '@vue/shared'
import { type Data, camelize, isFunction } from '@vue/shared'
import type { ComponentInternalInstance } from './component'
import { isEmitListener } from './componentEmits'
import { TrackOpTypes, track } from '@vue/reactivity'
import { warn } from './warning'

export function patchAttrs(instance: ComponentInternalInstance) {
const attrs = instance.attrs
Expand Down Expand Up @@ -42,3 +44,45 @@ export function patchAttrs(instance: ComponentInternalInstance) {
}
}
}

/**
* dev only flag to track whether $attrs was used during render.
* If $attrs was used during render then the warning for failed attrs
* fallthrough can be suppressed.
*/
let accessedAttrs: boolean = false

function markAttrsAccessed() {
accessedAttrs = true
}

export function getAttrsProxy(instance: ComponentInternalInstance): Data {
return (
instance.attrsProxy ||
(instance.attrsProxy = new Proxy(
instance.attrs,
__DEV__
? {
get(target, key: string) {
markAttrsAccessed()
track(instance, TrackOpTypes.GET, '$attrs')
return target[key]
},
set() {
warn(`setupContext.attrs is readonly.`)
return false
},
deleteProperty() {
warn(`setupContext.attrs is readonly.`)
return false
},
}
: {
get(target, key: string) {
track(instance, TrackOpTypes.GET, '$attrs')
return target[key]
},
},
))
)
}

0 comments on commit a0b5653

Please sign in to comment.