component.ts 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. import { VNode, VNodeChild, isVNode } from './vnode'
  2. import {
  3. isRef,
  4. pauseTracking,
  5. resetTracking,
  6. shallowReadonly,
  7. proxyRefs,
  8. EffectScope,
  9. markRaw,
  10. track,
  11. TrackOpTypes,
  12. ReactiveEffect
  13. } from '@vue/reactivity'
  14. import {
  15. ComponentPublicInstance,
  16. PublicInstanceProxyHandlers,
  17. createDevRenderContext,
  18. exposePropsOnRenderContext,
  19. exposeSetupStateOnRenderContext,
  20. ComponentPublicInstanceConstructor,
  21. publicPropertiesMap,
  22. RuntimeCompiledPublicInstanceProxyHandlers
  23. } from './componentPublicInstance'
  24. import {
  25. ComponentPropsOptions,
  26. NormalizedPropsOptions,
  27. initProps,
  28. normalizePropsOptions
  29. } from './componentProps'
  30. import { Slots, initSlots, InternalSlots } from './componentSlots'
  31. import { warn } from './warning'
  32. import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
  33. import { AppContext, createAppContext, AppConfig } from './apiCreateApp'
  34. import { Directive, validateDirectiveName } from './directives'
  35. import {
  36. applyOptions,
  37. ComponentOptions,
  38. ComputedOptions,
  39. MethodOptions,
  40. resolveMergedOptions
  41. } from './componentOptions'
  42. import {
  43. EmitsOptions,
  44. ObjectEmitsOptions,
  45. EmitFn,
  46. emit,
  47. normalizeEmitsOptions
  48. } from './componentEmits'
  49. import {
  50. EMPTY_OBJ,
  51. isArray,
  52. isFunction,
  53. NOOP,
  54. isObject,
  55. NO,
  56. makeMap,
  57. isPromise,
  58. ShapeFlags,
  59. extend
  60. } from '@vue/shared'
  61. import { SuspenseBoundary } from './components/Suspense'
  62. import { CompilerOptions } from '@vue/compiler-core'
  63. import { markAttrsAccessed } from './componentRenderUtils'
  64. import { currentRenderingInstance } from './componentRenderContext'
  65. import { startMeasure, endMeasure } from './profiling'
  66. import { convertLegacyRenderFn } from './compat/renderFn'
  67. import {
  68. CompatConfig,
  69. globalCompatConfig,
  70. validateCompatConfig
  71. } from './compat/compatConfig'
  72. import { SchedulerJob } from './scheduler'
  73. import { LifecycleHooks } from './enums'
  74. export type Data = Record<string, unknown>
  75. /**
  76. * For extending allowed non-declared props on components in TSX
  77. */
  78. export interface ComponentCustomProps {}
  79. /**
  80. * Default allowed non-declared props on component in TSX
  81. */
  82. export interface AllowedComponentProps {
  83. class?: unknown
  84. style?: unknown
  85. }
  86. // Note: can't mark this whole interface internal because some public interfaces
  87. // extend it.
  88. export interface ComponentInternalOptions {
  89. /**
  90. * @internal
  91. */
  92. __scopeId?: string
  93. /**
  94. * @internal
  95. */
  96. __cssModules?: Data
  97. /**
  98. * @internal
  99. */
  100. __hmrId?: string
  101. /**
  102. * Compat build only, for bailing out of certain compatibility behavior
  103. */
  104. __isBuiltIn?: boolean
  105. /**
  106. * This one should be exposed so that devtools can make use of it
  107. */
  108. __file?: string
  109. /**
  110. * name inferred from filename
  111. */
  112. __name?: string
  113. }
  114. export interface FunctionalComponent<P = {}, E extends EmitsOptions = {}>
  115. extends ComponentInternalOptions {
  116. // use of any here is intentional so it can be a valid JSX Element constructor
  117. (props: P, ctx: Omit<SetupContext<E>, 'expose'>): any
  118. props?: ComponentPropsOptions<P>
  119. emits?: E | (keyof E)[]
  120. inheritAttrs?: boolean
  121. displayName?: string
  122. compatConfig?: CompatConfig
  123. }
  124. export interface ClassComponent {
  125. new (...args: any[]): ComponentPublicInstance<any, any, any, any, any>
  126. __vccOpts: ComponentOptions
  127. }
  128. /**
  129. * Concrete component type matches its actual value: it's either an options
  130. * object, or a function. Use this where the code expects to work with actual
  131. * values, e.g. checking if its a function or not. This is mostly for internal
  132. * implementation code.
  133. */
  134. export type ConcreteComponent<
  135. Props = {},
  136. RawBindings = any,
  137. D = any,
  138. C extends ComputedOptions = ComputedOptions,
  139. M extends MethodOptions = MethodOptions
  140. > =
  141. | ComponentOptions<Props, RawBindings, D, C, M>
  142. | FunctionalComponent<Props, any>
  143. /**
  144. * A type used in public APIs where a component type is expected.
  145. * The constructor type is an artificial type returned by defineComponent().
  146. */
  147. export type Component<
  148. Props = any,
  149. RawBindings = any,
  150. D = any,
  151. C extends ComputedOptions = ComputedOptions,
  152. M extends MethodOptions = MethodOptions
  153. > =
  154. | ConcreteComponent<Props, RawBindings, D, C, M>
  155. | ComponentPublicInstanceConstructor<Props>
  156. export { ComponentOptions }
  157. type LifecycleHook<TFn = Function> = TFn[] | null
  158. // use `E extends any` to force evaluating type to fix #2362
  159. export type SetupContext<E = EmitsOptions> = E extends any
  160. ? {
  161. attrs: Data
  162. slots: Slots
  163. emit: EmitFn<E>
  164. expose: (exposed?: Record<string, any>) => void
  165. }
  166. : never
  167. /**
  168. * @internal
  169. */
  170. export type InternalRenderFunction = {
  171. (
  172. ctx: ComponentPublicInstance,
  173. cache: ComponentInternalInstance['renderCache'],
  174. // for compiler-optimized bindings
  175. $props: ComponentInternalInstance['props'],
  176. $setup: ComponentInternalInstance['setupState'],
  177. $data: ComponentInternalInstance['data'],
  178. $options: ComponentInternalInstance['ctx']
  179. ): VNodeChild
  180. _rc?: boolean // isRuntimeCompiled
  181. // __COMPAT__ only
  182. _compatChecked?: boolean // v3 and already checked for v2 compat
  183. _compatWrapped?: boolean // is wrapped for v2 compat
  184. }
  185. /**
  186. * We expose a subset of properties on the internal instance as they are
  187. * useful for advanced external libraries and tools.
  188. */
  189. export interface ComponentInternalInstance {
  190. uid: number
  191. type: ConcreteComponent
  192. parent: ComponentInternalInstance | null
  193. root: ComponentInternalInstance
  194. appContext: AppContext
  195. /**
  196. * Vnode representing this component in its parent's vdom tree
  197. */
  198. vnode: VNode
  199. /**
  200. * The pending new vnode from parent updates
  201. * @internal
  202. */
  203. next: VNode | null
  204. /**
  205. * Root vnode of this component's own vdom tree
  206. */
  207. subTree: VNode
  208. /**
  209. * Render effect instance
  210. */
  211. effect: ReactiveEffect
  212. /**
  213. * Bound effect runner to be passed to schedulers
  214. */
  215. update: SchedulerJob
  216. /**
  217. * The render function that returns vdom tree.
  218. * @internal
  219. */
  220. render: InternalRenderFunction | null
  221. /**
  222. * SSR render function
  223. * @internal
  224. */
  225. ssrRender?: Function | null
  226. /**
  227. * Object containing values this component provides for its descendents
  228. * @internal
  229. */
  230. provides: Data
  231. /**
  232. * Tracking reactive effects (e.g. watchers) associated with this component
  233. * so that they can be automatically stopped on component unmount
  234. * @internal
  235. */
  236. scope: EffectScope
  237. /**
  238. * cache for proxy access type to avoid hasOwnProperty calls
  239. * @internal
  240. */
  241. accessCache: Data | null
  242. /**
  243. * cache for render function values that rely on _ctx but won't need updates
  244. * after initialized (e.g. inline handlers)
  245. * @internal
  246. */
  247. renderCache: (Function | VNode)[]
  248. /**
  249. * Resolved component registry, only for components with mixins or extends
  250. * @internal
  251. */
  252. components: Record<string, ConcreteComponent> | null
  253. /**
  254. * Resolved directive registry, only for components with mixins or extends
  255. * @internal
  256. */
  257. directives: Record<string, Directive> | null
  258. /**
  259. * Resolved filters registry, v2 compat only
  260. * @internal
  261. */
  262. filters?: Record<string, Function>
  263. /**
  264. * resolved props options
  265. * @internal
  266. */
  267. propsOptions: NormalizedPropsOptions
  268. /**
  269. * resolved emits options
  270. * @internal
  271. */
  272. emitsOptions: ObjectEmitsOptions | null
  273. /**
  274. * resolved inheritAttrs options
  275. * @internal
  276. */
  277. inheritAttrs?: boolean
  278. /**
  279. * is custom element?
  280. * @internal
  281. */
  282. isCE?: boolean
  283. /**
  284. * custom element specific HMR method
  285. * @internal
  286. */
  287. ceReload?: (newStyles?: string[]) => void
  288. // the rest are only for stateful components ---------------------------------
  289. // main proxy that serves as the public instance (`this`)
  290. proxy: ComponentPublicInstance | null
  291. // exposed properties via expose()
  292. exposed: Record<string, any> | null
  293. exposeProxy: Record<string, any> | null
  294. /**
  295. * alternative proxy used only for runtime-compiled render functions using
  296. * `with` block
  297. * @internal
  298. */
  299. withProxy: ComponentPublicInstance | null
  300. /**
  301. * This is the target for the public instance proxy. It also holds properties
  302. * injected by user options (computed, methods etc.) and user-attached
  303. * custom properties (via `this.x = ...`)
  304. * @internal
  305. */
  306. ctx: Data
  307. // state
  308. data: Data
  309. props: Data
  310. attrs: Data
  311. slots: InternalSlots
  312. refs: Data
  313. emit: EmitFn
  314. /**
  315. * used for keeping track of .once event handlers on components
  316. * @internal
  317. */
  318. emitted: Record<string, boolean> | null
  319. /**
  320. * used for caching the value returned from props default factory functions to
  321. * avoid unnecessary watcher trigger
  322. * @internal
  323. */
  324. propsDefaults: Data
  325. /**
  326. * setup related
  327. * @internal
  328. */
  329. setupState: Data
  330. /**
  331. * devtools access to additional info
  332. * @internal
  333. */
  334. devtoolsRawSetupState?: any
  335. /**
  336. * @internal
  337. */
  338. setupContext: SetupContext | null
  339. /**
  340. * suspense related
  341. * @internal
  342. */
  343. suspense: SuspenseBoundary | null
  344. /**
  345. * suspense pending batch id
  346. * @internal
  347. */
  348. suspenseId: number
  349. /**
  350. * @internal
  351. */
  352. asyncDep: Promise<any> | null
  353. /**
  354. * @internal
  355. */
  356. asyncResolved: boolean
  357. // lifecycle
  358. isMounted: boolean
  359. isUnmounted: boolean
  360. isDeactivated: boolean
  361. /**
  362. * @internal
  363. */
  364. [LifecycleHooks.BEFORE_CREATE]: LifecycleHook
  365. /**
  366. * @internal
  367. */
  368. [LifecycleHooks.CREATED]: LifecycleHook
  369. /**
  370. * @internal
  371. */
  372. [LifecycleHooks.BEFORE_MOUNT]: LifecycleHook
  373. /**
  374. * @internal
  375. */
  376. [LifecycleHooks.MOUNTED]: LifecycleHook
  377. /**
  378. * @internal
  379. */
  380. [LifecycleHooks.BEFORE_UPDATE]: LifecycleHook
  381. /**
  382. * @internal
  383. */
  384. [LifecycleHooks.UPDATED]: LifecycleHook
  385. /**
  386. * @internal
  387. */
  388. [LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook
  389. /**
  390. * @internal
  391. */
  392. [LifecycleHooks.UNMOUNTED]: LifecycleHook
  393. /**
  394. * @internal
  395. */
  396. [LifecycleHooks.RENDER_TRACKED]: LifecycleHook
  397. /**
  398. * @internal
  399. */
  400. [LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook
  401. /**
  402. * @internal
  403. */
  404. [LifecycleHooks.ACTIVATED]: LifecycleHook
  405. /**
  406. * @internal
  407. */
  408. [LifecycleHooks.DEACTIVATED]: LifecycleHook
  409. /**
  410. * @internal
  411. */
  412. [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
  413. /**
  414. * @internal
  415. */
  416. [LifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
  417. /**
  418. * For caching bound $forceUpdate on public proxy access
  419. * @internal
  420. */
  421. f?: () => void
  422. /**
  423. * For caching bound $nextTick on public proxy access
  424. * @internal
  425. */
  426. n?: () => Promise<void>
  427. /**
  428. * `updateTeleportCssVars`
  429. * For updating css vars on contained teleports
  430. * @internal
  431. */
  432. ut?: (vars?: Record<string, string>) => void
  433. }
  434. const emptyAppContext = createAppContext()
  435. let uid = 0
  436. export function createComponentInstance(
  437. vnode: VNode,
  438. parent: ComponentInternalInstance | null,
  439. suspense: SuspenseBoundary | null
  440. ) {
  441. const type = vnode.type as ConcreteComponent
  442. // inherit parent app context - or - if root, adopt from root vnode
  443. const appContext =
  444. (parent ? parent.appContext : vnode.appContext) || emptyAppContext
  445. const instance: ComponentInternalInstance = {
  446. uid: uid++,
  447. vnode,
  448. type,
  449. parent,
  450. appContext,
  451. root: null!, // to be immediately set
  452. next: null,
  453. subTree: null!, // will be set synchronously right after creation
  454. effect: null!,
  455. update: null!, // will be set synchronously right after creation
  456. scope: new EffectScope(true /* detached */),
  457. render: null,
  458. proxy: null,
  459. exposed: null,
  460. exposeProxy: null,
  461. withProxy: null,
  462. provides: parent ? parent.provides : Object.create(appContext.provides),
  463. accessCache: null!,
  464. renderCache: [],
  465. // local resolved assets
  466. components: null,
  467. directives: null,
  468. // resolved props and emits options
  469. propsOptions: normalizePropsOptions(type, appContext),
  470. emitsOptions: normalizeEmitsOptions(type, appContext),
  471. // emit
  472. emit: null!, // to be set immediately
  473. emitted: null,
  474. // props default value
  475. propsDefaults: EMPTY_OBJ,
  476. // inheritAttrs
  477. inheritAttrs: type.inheritAttrs,
  478. // state
  479. ctx: EMPTY_OBJ,
  480. data: EMPTY_OBJ,
  481. props: EMPTY_OBJ,
  482. attrs: EMPTY_OBJ,
  483. slots: EMPTY_OBJ,
  484. refs: EMPTY_OBJ,
  485. setupState: EMPTY_OBJ,
  486. setupContext: null,
  487. // suspense related
  488. suspense,
  489. suspenseId: suspense ? suspense.pendingId : 0,
  490. asyncDep: null,
  491. asyncResolved: false,
  492. // lifecycle hooks
  493. // not using enums here because it results in computed properties
  494. isMounted: false,
  495. isUnmounted: false,
  496. isDeactivated: false,
  497. bc: null,
  498. c: null,
  499. bm: null,
  500. m: null,
  501. bu: null,
  502. u: null,
  503. um: null,
  504. bum: null,
  505. da: null,
  506. a: null,
  507. rtg: null,
  508. rtc: null,
  509. ec: null,
  510. sp: null
  511. }
  512. if (__DEV__) {
  513. instance.ctx = createDevRenderContext(instance)
  514. } else {
  515. instance.ctx = { _: instance }
  516. }
  517. instance.root = parent ? parent.root : instance
  518. instance.emit = emit.bind(null, instance)
  519. // apply custom element special handling
  520. if (vnode.ce) {
  521. vnode.ce(instance)
  522. }
  523. return instance
  524. }
  525. export let currentInstance: ComponentInternalInstance | null = null
  526. export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
  527. currentInstance || currentRenderingInstance
  528. export const setCurrentInstance = (instance: ComponentInternalInstance) => {
  529. currentInstance = instance
  530. instance.scope.on()
  531. }
  532. export const unsetCurrentInstance = () => {
  533. currentInstance && currentInstance.scope.off()
  534. currentInstance = null
  535. }
  536. const isBuiltInTag = /*#__PURE__*/ makeMap('slot,component')
  537. export function validateComponentName(name: string, config: AppConfig) {
  538. const appIsNativeTag = config.isNativeTag || NO
  539. if (isBuiltInTag(name) || appIsNativeTag(name)) {
  540. warn(
  541. 'Do not use built-in or reserved HTML elements as component id: ' + name
  542. )
  543. }
  544. }
  545. export function isStatefulComponent(instance: ComponentInternalInstance) {
  546. return instance.vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
  547. }
  548. export let isInSSRComponentSetup = false
  549. export function setupComponent(
  550. instance: ComponentInternalInstance,
  551. isSSR = false
  552. ) {
  553. isInSSRComponentSetup = isSSR
  554. const { props, children } = instance.vnode
  555. const isStateful = isStatefulComponent(instance)
  556. initProps(instance, props, isStateful, isSSR)
  557. initSlots(instance, children)
  558. const setupResult = isStateful
  559. ? setupStatefulComponent(instance, isSSR)
  560. : undefined
  561. isInSSRComponentSetup = false
  562. return setupResult
  563. }
  564. function setupStatefulComponent(
  565. instance: ComponentInternalInstance,
  566. isSSR: boolean
  567. ) {
  568. const Component = instance.type as ComponentOptions
  569. if (__DEV__) {
  570. if (Component.name) {
  571. validateComponentName(Component.name, instance.appContext.config)
  572. }
  573. if (Component.components) {
  574. const names = Object.keys(Component.components)
  575. for (let i = 0; i < names.length; i++) {
  576. validateComponentName(names[i], instance.appContext.config)
  577. }
  578. }
  579. if (Component.directives) {
  580. const names = Object.keys(Component.directives)
  581. for (let i = 0; i < names.length; i++) {
  582. validateDirectiveName(names[i])
  583. }
  584. }
  585. if (Component.compilerOptions && isRuntimeOnly()) {
  586. warn(
  587. `"compilerOptions" is only supported when using a build of Vue that ` +
  588. `includes the runtime compiler. Since you are using a runtime-only ` +
  589. `build, the options should be passed via your build tool config instead.`
  590. )
  591. }
  592. }
  593. // 0. create render proxy property access cache
  594. instance.accessCache = Object.create(null)
  595. // 1. create public instance / render proxy
  596. // also mark it raw so it's never observed
  597. instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
  598. if (__DEV__) {
  599. exposePropsOnRenderContext(instance)
  600. }
  601. // 2. call setup()
  602. const { setup } = Component
  603. if (setup) {
  604. const setupContext = (instance.setupContext =
  605. setup.length > 1 ? createSetupContext(instance) : null)
  606. setCurrentInstance(instance)
  607. pauseTracking()
  608. const setupResult = callWithErrorHandling(
  609. setup,
  610. instance,
  611. ErrorCodes.SETUP_FUNCTION,
  612. [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
  613. )
  614. resetTracking()
  615. unsetCurrentInstance()
  616. if (isPromise(setupResult)) {
  617. setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
  618. if (isSSR) {
  619. // return the promise so server-renderer can wait on it
  620. return setupResult
  621. .then((resolvedResult: unknown) => {
  622. handleSetupResult(instance, resolvedResult, isSSR)
  623. })
  624. .catch(e => {
  625. handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
  626. })
  627. } else if (__FEATURE_SUSPENSE__) {
  628. // async setup returned Promise.
  629. // bail here and wait for re-entry.
  630. instance.asyncDep = setupResult
  631. if (__DEV__ && !instance.suspense) {
  632. const name = Component.name ?? 'Anonymous'
  633. warn(
  634. `Component <${name}>: setup function returned a promise, but no ` +
  635. `<Suspense> boundary was found in the parent component tree. ` +
  636. `A component with async setup() must be nested in a <Suspense> ` +
  637. `in order to be rendered.`
  638. )
  639. }
  640. } else if (__DEV__) {
  641. warn(
  642. `setup() returned a Promise, but the version of Vue you are using ` +
  643. `does not support it yet.`
  644. )
  645. }
  646. } else {
  647. handleSetupResult(instance, setupResult, isSSR)
  648. }
  649. } else {
  650. finishComponentSetup(instance, isSSR)
  651. }
  652. }
  653. export function handleSetupResult(
  654. instance: ComponentInternalInstance,
  655. setupResult: unknown,
  656. isSSR: boolean
  657. ) {
  658. if (isFunction(setupResult)) {
  659. // setup returned an inline render function
  660. if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
  661. // when the function's name is `ssrRender` (compiled by SFC inline mode),
  662. // set it as ssrRender instead.
  663. instance.ssrRender = setupResult
  664. } else {
  665. instance.render = setupResult as InternalRenderFunction
  666. }
  667. } else if (isObject(setupResult)) {
  668. if (__DEV__ && isVNode(setupResult)) {
  669. warn(
  670. `setup() should not return VNodes directly - ` +
  671. `return a render function instead.`
  672. )
  673. }
  674. // setup returned bindings.
  675. // assuming a render function compiled from template is present.
  676. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  677. instance.devtoolsRawSetupState = setupResult
  678. }
  679. instance.setupState = proxyRefs(setupResult)
  680. if (__DEV__) {
  681. exposeSetupStateOnRenderContext(instance)
  682. }
  683. } else if (__DEV__ && setupResult !== undefined) {
  684. warn(
  685. `setup() should return an object. Received: ${
  686. setupResult === null ? 'null' : typeof setupResult
  687. }`
  688. )
  689. }
  690. finishComponentSetup(instance, isSSR)
  691. }
  692. type CompileFunction = (
  693. template: string | object,
  694. options?: CompilerOptions
  695. ) => InternalRenderFunction
  696. let compile: CompileFunction | undefined
  697. let installWithProxy: (i: ComponentInternalInstance) => void
  698. /**
  699. * For runtime-dom to register the compiler.
  700. * Note the exported method uses any to avoid d.ts relying on the compiler types.
  701. */
  702. export function registerRuntimeCompiler(_compile: any) {
  703. compile = _compile
  704. installWithProxy = i => {
  705. if (i.render!._rc) {
  706. i.withProxy = new Proxy(i.ctx, RuntimeCompiledPublicInstanceProxyHandlers)
  707. }
  708. }
  709. }
  710. // dev only
  711. export const isRuntimeOnly = () => !compile
  712. export function finishComponentSetup(
  713. instance: ComponentInternalInstance,
  714. isSSR: boolean,
  715. skipOptions?: boolean
  716. ) {
  717. const Component = instance.type as ComponentOptions
  718. if (__COMPAT__) {
  719. convertLegacyRenderFn(instance)
  720. if (__DEV__ && Component.compatConfig) {
  721. validateCompatConfig(Component.compatConfig)
  722. }
  723. }
  724. // template / render function normalization
  725. // could be already set when returned from setup()
  726. if (!instance.render) {
  727. // only do on-the-fly compile if not in SSR - SSR on-the-fly compilation
  728. // is done by server-renderer
  729. if (!isSSR && compile && !Component.render) {
  730. const template =
  731. (__COMPAT__ &&
  732. instance.vnode.props &&
  733. instance.vnode.props['inline-template']) ||
  734. Component.template ||
  735. resolveMergedOptions(instance).template
  736. if (template) {
  737. if (__DEV__) {
  738. startMeasure(instance, `compile`)
  739. }
  740. const { isCustomElement, compilerOptions } = instance.appContext.config
  741. const { delimiters, compilerOptions: componentCompilerOptions } =
  742. Component
  743. const finalCompilerOptions: CompilerOptions = extend(
  744. extend(
  745. {
  746. isCustomElement,
  747. delimiters
  748. },
  749. compilerOptions
  750. ),
  751. componentCompilerOptions
  752. )
  753. if (__COMPAT__) {
  754. // pass runtime compat config into the compiler
  755. finalCompilerOptions.compatConfig = Object.create(globalCompatConfig)
  756. if (Component.compatConfig) {
  757. // @ts-expect-error types are not compatible
  758. extend(finalCompilerOptions.compatConfig, Component.compatConfig)
  759. }
  760. }
  761. Component.render = compile(template, finalCompilerOptions)
  762. if (__DEV__) {
  763. endMeasure(instance, `compile`)
  764. }
  765. }
  766. }
  767. instance.render = (Component.render || NOOP) as InternalRenderFunction
  768. // for runtime-compiled render functions using `with` blocks, the render
  769. // proxy used needs a different `has` handler which is more performant and
  770. // also only allows a whitelist of globals to fallthrough.
  771. if (installWithProxy) {
  772. installWithProxy(instance)
  773. }
  774. }
  775. // support for 2.x options
  776. if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
  777. setCurrentInstance(instance)
  778. pauseTracking()
  779. applyOptions(instance)
  780. resetTracking()
  781. unsetCurrentInstance()
  782. }
  783. // warn missing template/render
  784. // the runtime compilation of template in SSR is done by server-render
  785. if (__DEV__ && !Component.render && instance.render === NOOP && !isSSR) {
  786. /* istanbul ignore if */
  787. if (!compile && Component.template) {
  788. warn(
  789. `Component provided template option but ` +
  790. `runtime compilation is not supported in this build of Vue.` +
  791. (__ESM_BUNDLER__
  792. ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`
  793. : __ESM_BROWSER__
  794. ? ` Use "vue.esm-browser.js" instead.`
  795. : __GLOBAL__
  796. ? ` Use "vue.global.js" instead.`
  797. : ``) /* should not happen */
  798. )
  799. } else {
  800. warn(`Component is missing template or render function.`)
  801. }
  802. }
  803. }
  804. function createAttrsProxy(instance: ComponentInternalInstance): Data {
  805. return new Proxy(
  806. instance.attrs,
  807. __DEV__
  808. ? {
  809. get(target, key: string) {
  810. markAttrsAccessed()
  811. track(instance, TrackOpTypes.GET, '$attrs')
  812. return target[key]
  813. },
  814. set() {
  815. warn(`setupContext.attrs is readonly.`)
  816. return false
  817. },
  818. deleteProperty() {
  819. warn(`setupContext.attrs is readonly.`)
  820. return false
  821. }
  822. }
  823. : {
  824. get(target, key: string) {
  825. track(instance, TrackOpTypes.GET, '$attrs')
  826. return target[key]
  827. }
  828. }
  829. )
  830. }
  831. export function createSetupContext(
  832. instance: ComponentInternalInstance
  833. ): SetupContext {
  834. const expose: SetupContext['expose'] = exposed => {
  835. if (__DEV__) {
  836. if (instance.exposed) {
  837. warn(`expose() should be called only once per setup().`)
  838. }
  839. if (exposed != null) {
  840. let exposedType: string = typeof exposed
  841. if (exposedType === 'object') {
  842. if (isArray(exposed)) {
  843. exposedType = 'array'
  844. } else if (isRef(exposed)) {
  845. exposedType = 'ref'
  846. }
  847. }
  848. if (exposedType !== 'object') {
  849. warn(
  850. `expose() should be passed a plain object, received ${exposedType}.`
  851. )
  852. }
  853. }
  854. }
  855. instance.exposed = exposed || {}
  856. }
  857. let attrs: Data
  858. if (__DEV__) {
  859. // We use getters in dev in case libs like test-utils overwrite instance
  860. // properties (overwrites should not be done in prod)
  861. return Object.freeze({
  862. get attrs() {
  863. return attrs || (attrs = createAttrsProxy(instance))
  864. },
  865. get slots() {
  866. return shallowReadonly(instance.slots)
  867. },
  868. get emit() {
  869. return (event: string, ...args: any[]) => instance.emit(event, ...args)
  870. },
  871. expose
  872. })
  873. } else {
  874. return {
  875. get attrs() {
  876. return attrs || (attrs = createAttrsProxy(instance))
  877. },
  878. slots: instance.slots,
  879. emit: instance.emit,
  880. expose
  881. }
  882. }
  883. }
  884. export function getExposeProxy(instance: ComponentInternalInstance) {
  885. if (instance.exposed) {
  886. return (
  887. instance.exposeProxy ||
  888. (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
  889. get(target, key: string) {
  890. if (key in target) {
  891. return target[key]
  892. } else if (key in publicPropertiesMap) {
  893. return publicPropertiesMap[key](instance)
  894. }
  895. },
  896. has(target, key: string) {
  897. return key in target || key in publicPropertiesMap
  898. }
  899. }))
  900. )
  901. }
  902. }
  903. const classifyRE = /(?:^|[-_])(\w)/g
  904. const classify = (str: string): string =>
  905. str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
  906. export function getComponentName(
  907. Component: ConcreteComponent,
  908. includeInferred = true
  909. ): string | false | undefined {
  910. return isFunction(Component)
  911. ? Component.displayName || Component.name
  912. : Component.name || (includeInferred && Component.__name)
  913. }
  914. /* istanbul ignore next */
  915. export function formatComponentName(
  916. instance: ComponentInternalInstance | null,
  917. Component: ConcreteComponent,
  918. isRoot = false
  919. ): string {
  920. let name = getComponentName(Component)
  921. if (!name && Component.__file) {
  922. const match = Component.__file.match(/([^/\\]+)\.\w+$/)
  923. if (match) {
  924. name = match[1]
  925. }
  926. }
  927. if (!name && instance && instance.parent) {
  928. // try to infer the name based on reverse resolution
  929. const inferFromRegistry = (registry: Record<string, any> | undefined) => {
  930. for (const key in registry) {
  931. if (registry[key] === Component) {
  932. return key
  933. }
  934. }
  935. }
  936. name =
  937. inferFromRegistry(
  938. instance.components ||
  939. (instance.parent.type as ComponentOptions).components
  940. ) || inferFromRegistry(instance.appContext.components)
  941. }
  942. return name ? classify(name) : isRoot ? `App` : `Anonymous`
  943. }
  944. export function isClassComponent(value: unknown): value is ClassComponent {
  945. return isFunction(value) && '__vccOpts' in value
  946. }