component.ts 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281
  1. import { type VNode, type VNodeChild, isVNode } from './vnode'
  2. import {
  3. EffectScope,
  4. type ReactiveEffect,
  5. type ShallowRef,
  6. TrackOpTypes,
  7. isRef,
  8. markRaw,
  9. pauseTracking,
  10. proxyRefs,
  11. resetTracking,
  12. shallowReadonly,
  13. track,
  14. } from '@vue/reactivity'
  15. import {
  16. type ComponentPublicInstance,
  17. type ComponentPublicInstanceConstructor,
  18. PublicInstanceProxyHandlers,
  19. RuntimeCompiledPublicInstanceProxyHandlers,
  20. createDevRenderContext,
  21. exposePropsOnRenderContext,
  22. exposeSetupStateOnRenderContext,
  23. publicPropertiesMap,
  24. } from './componentPublicInstance'
  25. import {
  26. type ComponentPropsOptions,
  27. type NormalizedPropsOptions,
  28. initProps,
  29. normalizePropsOptions,
  30. } from './componentProps'
  31. import {
  32. type InternalSlots,
  33. type Slots,
  34. type SlotsType,
  35. type UnwrapSlotsType,
  36. initSlots,
  37. } from './componentSlots'
  38. import { warn } from './warning'
  39. import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
  40. import {
  41. type AppConfig,
  42. type AppContext,
  43. createAppContext,
  44. } from './apiCreateApp'
  45. import { type Directive, validateDirectiveName } from './directives'
  46. import {
  47. type ComponentOptions,
  48. type ComputedOptions,
  49. type MergedComponentOptions,
  50. type MethodOptions,
  51. applyOptions,
  52. resolveMergedOptions,
  53. } from './componentOptions'
  54. import {
  55. type EmitFn,
  56. type EmitsOptions,
  57. type EmitsToProps,
  58. type ObjectEmitsOptions,
  59. type ShortEmitsToObject,
  60. emit,
  61. normalizeEmitsOptions,
  62. } from './componentEmits'
  63. import {
  64. EMPTY_OBJ,
  65. type IfAny,
  66. NOOP,
  67. ShapeFlags,
  68. extend,
  69. getGlobalThis,
  70. isArray,
  71. isFunction,
  72. isObject,
  73. isPromise,
  74. makeMap,
  75. } from '@vue/shared'
  76. import type { SuspenseBoundary } from './components/Suspense'
  77. import type { CompilerOptions } from '@vue/compiler-core'
  78. import { markAttrsAccessed } from './componentRenderUtils'
  79. import { currentRenderingInstance } from './componentRenderContext'
  80. import { endMeasure, startMeasure } from './profiling'
  81. import { convertLegacyRenderFn } from './compat/renderFn'
  82. import {
  83. type CompatConfig,
  84. globalCompatConfig,
  85. validateCompatConfig,
  86. } from './compat/compatConfig'
  87. import type { SchedulerJob } from './scheduler'
  88. import type { LifecycleHooks } from './enums'
  89. // Augment GlobalComponents
  90. import type { TeleportProps } from './components/Teleport'
  91. import type { SuspenseProps } from './components/Suspense'
  92. import type { KeepAliveProps } from './components/KeepAlive'
  93. import type { BaseTransitionProps } from './components/BaseTransition'
  94. import type { DefineComponent } from './apiDefineComponent'
  95. import { markAsyncBoundary } from './helpers/useId'
  96. import { isAsyncWrapper } from './apiAsyncComponent'
  97. import type { RendererElement } from './renderer'
  98. export type Data = Record<string, unknown>
  99. /**
  100. * Public utility type for extracting the instance type of a component.
  101. * Works with all valid component definition types. This is intended to replace
  102. * the usage of `InstanceType<typeof Comp>` which only works for
  103. * constructor-based component definition types.
  104. *
  105. * @example
  106. * ```ts
  107. * const MyComp = { ... }
  108. * declare const instance: ComponentInstance<typeof MyComp>
  109. * ```
  110. */
  111. export type ComponentInstance<T> = T extends { new (): ComponentPublicInstance }
  112. ? InstanceType<T>
  113. : T extends FunctionalComponent<infer Props, infer Emits>
  114. ? ComponentPublicInstance<Props, {}, {}, {}, {}, ShortEmitsToObject<Emits>>
  115. : T extends Component<
  116. infer Props,
  117. infer RawBindings,
  118. infer D,
  119. infer C,
  120. infer M
  121. >
  122. ? // NOTE we override Props/RawBindings/D to make sure is not `unknown`
  123. ComponentPublicInstance<
  124. unknown extends Props ? {} : Props,
  125. unknown extends RawBindings ? {} : RawBindings,
  126. unknown extends D ? {} : D,
  127. C,
  128. M
  129. >
  130. : never // not a vue Component
  131. /**
  132. * For extending allowed non-declared props on components in TSX
  133. */
  134. export interface ComponentCustomProps {}
  135. /**
  136. * For globally defined Directives
  137. * Here is an example of adding a directive `VTooltip` as global directive:
  138. *
  139. * @example
  140. * ```ts
  141. * import VTooltip from 'v-tooltip'
  142. *
  143. * declare module '@vue/runtime-core' {
  144. * interface GlobalDirectives {
  145. * VTooltip
  146. * }
  147. * }
  148. * ```
  149. */
  150. export interface GlobalDirectives {}
  151. /**
  152. * For globally defined Components
  153. * Here is an example of adding a component `RouterView` as global component:
  154. *
  155. * @example
  156. * ```ts
  157. * import { RouterView } from 'vue-router'
  158. *
  159. * declare module '@vue/runtime-core' {
  160. * interface GlobalComponents {
  161. * RouterView
  162. * }
  163. * }
  164. * ```
  165. */
  166. export interface GlobalComponents {
  167. Teleport: DefineComponent<TeleportProps>
  168. Suspense: DefineComponent<SuspenseProps>
  169. KeepAlive: DefineComponent<KeepAliveProps>
  170. BaseTransition: DefineComponent<BaseTransitionProps>
  171. }
  172. /**
  173. * Default allowed non-declared props on component in TSX
  174. */
  175. export interface AllowedComponentProps {
  176. class?: unknown
  177. style?: unknown
  178. }
  179. // Note: can't mark this whole interface internal because some public interfaces
  180. // extend it.
  181. export interface ComponentInternalOptions {
  182. /**
  183. * @internal
  184. */
  185. __scopeId?: string
  186. /**
  187. * @internal
  188. */
  189. __cssModules?: Data
  190. /**
  191. * @internal
  192. */
  193. __hmrId?: string
  194. /**
  195. * Compat build only, for bailing out of certain compatibility behavior
  196. */
  197. __isBuiltIn?: boolean
  198. /**
  199. * This one should be exposed so that devtools can make use of it
  200. */
  201. __file?: string
  202. /**
  203. * name inferred from filename
  204. */
  205. __name?: string
  206. }
  207. export interface FunctionalComponent<
  208. P = {},
  209. E extends EmitsOptions | Record<string, any[]> = {},
  210. S extends Record<string, any> = any,
  211. EE extends EmitsOptions = ShortEmitsToObject<E>,
  212. > extends ComponentInternalOptions {
  213. // use of any here is intentional so it can be a valid JSX Element constructor
  214. (
  215. props: P & EmitsToProps<EE>,
  216. ctx: Omit<SetupContext<EE, IfAny<S, {}, SlotsType<S>>>, 'expose'>,
  217. ): any
  218. props?: ComponentPropsOptions<P>
  219. emits?: EE | (keyof EE)[]
  220. slots?: IfAny<S, Slots, SlotsType<S>>
  221. inheritAttrs?: boolean
  222. displayName?: string
  223. compatConfig?: CompatConfig
  224. }
  225. export interface ClassComponent {
  226. new (...args: any[]): ComponentPublicInstance<any, any, any, any, any>
  227. __vccOpts: ComponentOptions
  228. }
  229. /**
  230. * Concrete component type matches its actual value: it's either an options
  231. * object, or a function. Use this where the code expects to work with actual
  232. * values, e.g. checking if its a function or not. This is mostly for internal
  233. * implementation code.
  234. */
  235. export type ConcreteComponent<
  236. Props = {},
  237. RawBindings = any,
  238. D = any,
  239. C extends ComputedOptions = ComputedOptions,
  240. M extends MethodOptions = MethodOptions,
  241. E extends EmitsOptions | Record<string, any[]> = {},
  242. S extends Record<string, any> = any,
  243. > =
  244. | ComponentOptions<Props, RawBindings, D, C, M>
  245. | FunctionalComponent<Props, E, S>
  246. /**
  247. * A type used in public APIs where a component type is expected.
  248. * The constructor type is an artificial type returned by defineComponent().
  249. */
  250. export type Component<
  251. Props = any,
  252. RawBindings = any,
  253. D = any,
  254. C extends ComputedOptions = ComputedOptions,
  255. M extends MethodOptions = MethodOptions,
  256. E extends EmitsOptions | Record<string, any[]> = {},
  257. S extends Record<string, any> = any,
  258. > =
  259. | ConcreteComponent<Props, RawBindings, D, C, M, E, S>
  260. | ComponentPublicInstanceConstructor<Props>
  261. export type { ComponentOptions }
  262. export type LifecycleHook<TFn = Function> = (TFn & SchedulerJob)[] | null
  263. // use `E extends any` to force evaluating type to fix #2362
  264. export type SetupContext<
  265. E = EmitsOptions,
  266. S extends SlotsType = {},
  267. > = E extends any
  268. ? {
  269. attrs: Data
  270. slots: UnwrapSlotsType<S>
  271. emit: EmitFn<E>
  272. expose: <Exposed extends Record<string, any> = Record<string, any>>(
  273. exposed?: Exposed,
  274. ) => void
  275. }
  276. : never
  277. /**
  278. * @internal
  279. */
  280. export type InternalRenderFunction = {
  281. (
  282. ctx: ComponentPublicInstance,
  283. cache: ComponentInternalInstance['renderCache'],
  284. // for compiler-optimized bindings
  285. $props: ComponentInternalInstance['props'],
  286. $setup: ComponentInternalInstance['setupState'],
  287. $data: ComponentInternalInstance['data'],
  288. $options: ComponentInternalInstance['ctx'],
  289. ): VNodeChild
  290. _rc?: boolean // isRuntimeCompiled
  291. // __COMPAT__ only
  292. _compatChecked?: boolean // v3 and already checked for v2 compat
  293. _compatWrapped?: boolean // is wrapped for v2 compat
  294. }
  295. /**
  296. * We expose a subset of properties on the internal instance as they are
  297. * useful for advanced external libraries and tools.
  298. */
  299. export interface ComponentInternalInstance {
  300. uid: number
  301. type: ConcreteComponent
  302. parent: ComponentInternalInstance | null
  303. root: ComponentInternalInstance
  304. appContext: AppContext
  305. /**
  306. * Vnode representing this component in its parent's vdom tree
  307. */
  308. vnode: VNode
  309. /**
  310. * The pending new vnode from parent updates
  311. * @internal
  312. */
  313. next: VNode | null
  314. /**
  315. * Root vnode of this component's own vdom tree
  316. */
  317. subTree: VNode
  318. /**
  319. * Render effect instance
  320. */
  321. effect: ReactiveEffect
  322. /**
  323. * Force update render effect
  324. */
  325. update: () => void
  326. /**
  327. * Render effect job to be passed to scheduler (checks if dirty)
  328. */
  329. job: SchedulerJob
  330. /**
  331. * The render function that returns vdom tree.
  332. * @internal
  333. */
  334. render: InternalRenderFunction | null
  335. /**
  336. * SSR render function
  337. * @internal
  338. */
  339. ssrRender?: Function | null
  340. /**
  341. * Object containing values this component provides for its descendants
  342. * @internal
  343. */
  344. provides: Data
  345. /**
  346. * for tracking useId()
  347. * first element is the current boundary prefix
  348. * second number is the index of the useId call within that boundary
  349. * @internal
  350. */
  351. ids: [string, number, number]
  352. /**
  353. * Tracking reactive effects (e.g. watchers) associated with this component
  354. * so that they can be automatically stopped on component unmount
  355. * @internal
  356. */
  357. scope: EffectScope
  358. /**
  359. * cache for proxy access type to avoid hasOwnProperty calls
  360. * @internal
  361. */
  362. accessCache: Data | null
  363. /**
  364. * cache for render function values that rely on _ctx but won't need updates
  365. * after initialized (e.g. inline handlers)
  366. * @internal
  367. */
  368. renderCache: (Function | VNode | undefined)[]
  369. /**
  370. * Resolved component registry, only for components with mixins or extends
  371. * @internal
  372. */
  373. components: Record<string, ConcreteComponent> | null
  374. /**
  375. * Resolved directive registry, only for components with mixins or extends
  376. * @internal
  377. */
  378. directives: Record<string, Directive> | null
  379. /**
  380. * Resolved filters registry, v2 compat only
  381. * @internal
  382. */
  383. filters?: Record<string, Function>
  384. /**
  385. * resolved props options
  386. * @internal
  387. */
  388. propsOptions: NormalizedPropsOptions
  389. /**
  390. * resolved emits options
  391. * @internal
  392. */
  393. emitsOptions: ObjectEmitsOptions | null
  394. /**
  395. * resolved inheritAttrs options
  396. * @internal
  397. */
  398. inheritAttrs?: boolean
  399. /**
  400. * Custom Element instance (if component is created by defineCustomElement)
  401. * @internal
  402. */
  403. ce?: ComponentCustomElementInterface
  404. /**
  405. * is custom element? (kept only for compatibility)
  406. * @internal
  407. */
  408. isCE?: boolean
  409. /**
  410. * custom element specific HMR method
  411. * @internal
  412. */
  413. ceReload?: (newStyles?: string[]) => void
  414. // the rest are only for stateful components ---------------------------------
  415. // main proxy that serves as the public instance (`this`)
  416. proxy: ComponentPublicInstance | null
  417. // exposed properties via expose()
  418. exposed: Record<string, any> | null
  419. exposeProxy: Record<string, any> | null
  420. /**
  421. * alternative proxy used only for runtime-compiled render functions using
  422. * `with` block
  423. * @internal
  424. */
  425. withProxy: ComponentPublicInstance | null
  426. /**
  427. * This is the target for the public instance proxy. It also holds properties
  428. * injected by user options (computed, methods etc.) and user-attached
  429. * custom properties (via `this.x = ...`)
  430. * @internal
  431. */
  432. ctx: Data
  433. // state
  434. data: Data
  435. props: Data
  436. attrs: Data
  437. slots: InternalSlots
  438. refs: Data
  439. emit: EmitFn
  440. /**
  441. * used for caching useTemplateRef results
  442. * @internal
  443. */
  444. refsCache?: Map<string, ShallowRef>
  445. /**
  446. * used for keeping track of .once event handlers on components
  447. * @internal
  448. */
  449. emitted: Record<string, boolean> | null
  450. /**
  451. * used for caching the value returned from props default factory functions to
  452. * avoid unnecessary watcher trigger
  453. * @internal
  454. */
  455. propsDefaults: Data
  456. /**
  457. * setup related
  458. * @internal
  459. */
  460. setupState: Data
  461. /**
  462. * devtools access to additional info
  463. * @internal
  464. */
  465. devtoolsRawSetupState?: any
  466. /**
  467. * @internal
  468. */
  469. setupContext: SetupContext | null
  470. /**
  471. * suspense related
  472. * @internal
  473. */
  474. suspense: SuspenseBoundary | null
  475. /**
  476. * suspense pending batch id
  477. * @internal
  478. */
  479. suspenseId: number
  480. /**
  481. * @internal
  482. */
  483. asyncDep: Promise<any> | null
  484. /**
  485. * @internal
  486. */
  487. asyncResolved: boolean
  488. // lifecycle
  489. isMounted: boolean
  490. isUnmounted: boolean
  491. isDeactivated: boolean
  492. /**
  493. * @internal
  494. */
  495. [LifecycleHooks.BEFORE_CREATE]: LifecycleHook
  496. /**
  497. * @internal
  498. */
  499. [LifecycleHooks.CREATED]: LifecycleHook
  500. /**
  501. * @internal
  502. */
  503. [LifecycleHooks.BEFORE_MOUNT]: LifecycleHook
  504. /**
  505. * @internal
  506. */
  507. [LifecycleHooks.MOUNTED]: LifecycleHook
  508. /**
  509. * @internal
  510. */
  511. [LifecycleHooks.BEFORE_UPDATE]: LifecycleHook
  512. /**
  513. * @internal
  514. */
  515. [LifecycleHooks.UPDATED]: LifecycleHook
  516. /**
  517. * @internal
  518. */
  519. [LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook
  520. /**
  521. * @internal
  522. */
  523. [LifecycleHooks.UNMOUNTED]: LifecycleHook
  524. /**
  525. * @internal
  526. */
  527. [LifecycleHooks.RENDER_TRACKED]: LifecycleHook
  528. /**
  529. * @internal
  530. */
  531. [LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook
  532. /**
  533. * @internal
  534. */
  535. [LifecycleHooks.ACTIVATED]: LifecycleHook
  536. /**
  537. * @internal
  538. */
  539. [LifecycleHooks.DEACTIVATED]: LifecycleHook
  540. /**
  541. * @internal
  542. */
  543. [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
  544. /**
  545. * @internal
  546. */
  547. [LifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
  548. /**
  549. * For caching bound $forceUpdate on public proxy access
  550. * @internal
  551. */
  552. f?: () => void
  553. /**
  554. * For caching bound $nextTick on public proxy access
  555. * @internal
  556. */
  557. n?: () => Promise<void>
  558. /**
  559. * `updateTeleportCssVars`
  560. * For updating css vars on contained teleports
  561. * @internal
  562. */
  563. ut?: (vars?: Record<string, string>) => void
  564. /**
  565. * dev only. For style v-bind hydration mismatch checks
  566. * @internal
  567. */
  568. getCssVars?: () => Record<string, string>
  569. /**
  570. * v2 compat only, for caching mutated $options
  571. * @internal
  572. */
  573. resolvedOptions?: MergedComponentOptions
  574. }
  575. const emptyAppContext = createAppContext()
  576. let uid = 0
  577. export function createComponentInstance(
  578. vnode: VNode,
  579. parent: ComponentInternalInstance | null,
  580. suspense: SuspenseBoundary | null,
  581. ): ComponentInternalInstance {
  582. const type = vnode.type as ConcreteComponent
  583. // inherit parent app context - or - if root, adopt from root vnode
  584. const appContext =
  585. (parent ? parent.appContext : vnode.appContext) || emptyAppContext
  586. const instance: ComponentInternalInstance = {
  587. uid: uid++,
  588. vnode,
  589. type,
  590. parent,
  591. appContext,
  592. root: null!, // to be immediately set
  593. next: null,
  594. subTree: null!, // will be set synchronously right after creation
  595. effect: null!,
  596. update: null!, // will be set synchronously right after creation
  597. job: null!,
  598. scope: new EffectScope(true /* detached */),
  599. render: null,
  600. proxy: null,
  601. exposed: null,
  602. exposeProxy: null,
  603. withProxy: null,
  604. provides: parent ? parent.provides : Object.create(appContext.provides),
  605. ids: parent ? parent.ids : ['', 0, 0],
  606. accessCache: null!,
  607. renderCache: [],
  608. // local resolved assets
  609. components: null,
  610. directives: null,
  611. // resolved props and emits options
  612. propsOptions: normalizePropsOptions(type, appContext),
  613. emitsOptions: normalizeEmitsOptions(type, appContext),
  614. // emit
  615. emit: null!, // to be set immediately
  616. emitted: null,
  617. // props default value
  618. propsDefaults: EMPTY_OBJ,
  619. // inheritAttrs
  620. inheritAttrs: type.inheritAttrs,
  621. // state
  622. ctx: EMPTY_OBJ,
  623. data: EMPTY_OBJ,
  624. props: EMPTY_OBJ,
  625. attrs: EMPTY_OBJ,
  626. slots: EMPTY_OBJ,
  627. refs: EMPTY_OBJ,
  628. setupState: EMPTY_OBJ,
  629. setupContext: null,
  630. // suspense related
  631. suspense,
  632. suspenseId: suspense ? suspense.pendingId : 0,
  633. asyncDep: null,
  634. asyncResolved: false,
  635. // lifecycle hooks
  636. // not using enums here because it results in computed properties
  637. isMounted: false,
  638. isUnmounted: false,
  639. isDeactivated: false,
  640. bc: null,
  641. c: null,
  642. bm: null,
  643. m: null,
  644. bu: null,
  645. u: null,
  646. um: null,
  647. bum: null,
  648. da: null,
  649. a: null,
  650. rtg: null,
  651. rtc: null,
  652. ec: null,
  653. sp: null,
  654. }
  655. if (__DEV__) {
  656. instance.ctx = createDevRenderContext(instance)
  657. } else {
  658. instance.ctx = { _: instance }
  659. }
  660. instance.root = parent ? parent.root : instance
  661. instance.emit = emit.bind(null, instance)
  662. // apply custom element special handling
  663. if (vnode.ce) {
  664. vnode.ce(instance)
  665. }
  666. return instance
  667. }
  668. export let currentInstance: ComponentInternalInstance | null = null
  669. export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
  670. currentInstance || currentRenderingInstance
  671. let internalSetCurrentInstance: (
  672. instance: ComponentInternalInstance | null,
  673. ) => void
  674. let setInSSRSetupState: (state: boolean) => void
  675. /**
  676. * The following makes getCurrentInstance() usage across multiple copies of Vue
  677. * work. Some cases of how this can happen are summarized in #7590. In principle
  678. * the duplication should be avoided, but in practice there are often cases
  679. * where the user is unable to resolve on their own, especially in complicated
  680. * SSR setups.
  681. *
  682. * Note this fix is technically incomplete, as we still rely on other singletons
  683. * for effectScope and global reactive dependency maps. However, it does make
  684. * some of the most common cases work. It also warns if the duplication is
  685. * found during browser execution.
  686. */
  687. if (__SSR__) {
  688. type Setter = (v: any) => void
  689. const g = getGlobalThis()
  690. const registerGlobalSetter = (key: string, setter: Setter) => {
  691. let setters: Setter[]
  692. if (!(setters = g[key])) setters = g[key] = []
  693. setters.push(setter)
  694. return (v: any) => {
  695. if (setters.length > 1) setters.forEach(set => set(v))
  696. else setters[0](v)
  697. }
  698. }
  699. internalSetCurrentInstance = registerGlobalSetter(
  700. `__VUE_INSTANCE_SETTERS__`,
  701. v => (currentInstance = v),
  702. )
  703. // also make `isInSSRComponentSetup` sharable across copies of Vue.
  704. // this is needed in the SFC playground when SSRing async components, since
  705. // we have to load both the runtime and the server-renderer from CDNs, they
  706. // contain duplicated copies of Vue runtime code.
  707. setInSSRSetupState = registerGlobalSetter(
  708. `__VUE_SSR_SETTERS__`,
  709. v => (isInSSRComponentSetup = v),
  710. )
  711. } else {
  712. internalSetCurrentInstance = i => {
  713. currentInstance = i
  714. }
  715. setInSSRSetupState = v => {
  716. isInSSRComponentSetup = v
  717. }
  718. }
  719. export const setCurrentInstance = (instance: ComponentInternalInstance) => {
  720. const prev = currentInstance
  721. internalSetCurrentInstance(instance)
  722. instance.scope.on()
  723. return (): void => {
  724. instance.scope.off()
  725. internalSetCurrentInstance(prev)
  726. }
  727. }
  728. export const unsetCurrentInstance = (): void => {
  729. currentInstance && currentInstance.scope.off()
  730. internalSetCurrentInstance(null)
  731. }
  732. const isBuiltInTag = /*@__PURE__*/ makeMap('slot,component')
  733. export function validateComponentName(
  734. name: string,
  735. { isNativeTag }: AppConfig,
  736. ): void {
  737. if (isBuiltInTag(name) || isNativeTag(name)) {
  738. warn(
  739. 'Do not use built-in or reserved HTML elements as component id: ' + name,
  740. )
  741. }
  742. }
  743. export function isStatefulComponent(
  744. instance: ComponentInternalInstance,
  745. ): number {
  746. return instance.vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
  747. }
  748. export let isInSSRComponentSetup = false
  749. export function setupComponent(
  750. instance: ComponentInternalInstance,
  751. isSSR = false,
  752. optimized = false,
  753. ): Promise<void> | undefined {
  754. isSSR && setInSSRSetupState(isSSR)
  755. const { props, children } = instance.vnode
  756. const isStateful = isStatefulComponent(instance)
  757. initProps(instance, props, isStateful, isSSR)
  758. initSlots(instance, children, optimized)
  759. const setupResult = isStateful
  760. ? setupStatefulComponent(instance, isSSR)
  761. : undefined
  762. isSSR && setInSSRSetupState(false)
  763. return setupResult
  764. }
  765. function setupStatefulComponent(
  766. instance: ComponentInternalInstance,
  767. isSSR: boolean,
  768. ) {
  769. const Component = instance.type as ComponentOptions
  770. if (__DEV__) {
  771. if (Component.name) {
  772. validateComponentName(Component.name, instance.appContext.config)
  773. }
  774. if (Component.components) {
  775. const names = Object.keys(Component.components)
  776. for (let i = 0; i < names.length; i++) {
  777. validateComponentName(names[i], instance.appContext.config)
  778. }
  779. }
  780. if (Component.directives) {
  781. const names = Object.keys(Component.directives)
  782. for (let i = 0; i < names.length; i++) {
  783. validateDirectiveName(names[i])
  784. }
  785. }
  786. if (Component.compilerOptions && isRuntimeOnly()) {
  787. warn(
  788. `"compilerOptions" is only supported when using a build of Vue that ` +
  789. `includes the runtime compiler. Since you are using a runtime-only ` +
  790. `build, the options should be passed via your build tool config instead.`,
  791. )
  792. }
  793. }
  794. // 0. create render proxy property access cache
  795. instance.accessCache = Object.create(null)
  796. // 1. create public instance / render proxy
  797. instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
  798. if (__DEV__) {
  799. exposePropsOnRenderContext(instance)
  800. }
  801. // 2. call setup()
  802. const { setup } = Component
  803. if (setup) {
  804. pauseTracking()
  805. const setupContext = (instance.setupContext =
  806. setup.length > 1 ? createSetupContext(instance) : null)
  807. const reset = setCurrentInstance(instance)
  808. const setupResult = callWithErrorHandling(
  809. setup,
  810. instance,
  811. ErrorCodes.SETUP_FUNCTION,
  812. [
  813. __DEV__ ? shallowReadonly(instance.props) : instance.props,
  814. setupContext,
  815. ],
  816. )
  817. const isAsyncSetup = isPromise(setupResult)
  818. resetTracking()
  819. reset()
  820. if ((isAsyncSetup || instance.sp) && !isAsyncWrapper(instance)) {
  821. // async setup / serverPrefetch, mark as async boundary for useId()
  822. markAsyncBoundary(instance)
  823. }
  824. if (isAsyncSetup) {
  825. setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
  826. if (isSSR) {
  827. // return the promise so server-renderer can wait on it
  828. return setupResult
  829. .then((resolvedResult: unknown) => {
  830. handleSetupResult(instance, resolvedResult, isSSR)
  831. })
  832. .catch(e => {
  833. handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
  834. })
  835. } else if (__FEATURE_SUSPENSE__) {
  836. // async setup returned Promise.
  837. // bail here and wait for re-entry.
  838. instance.asyncDep = setupResult
  839. if (__DEV__ && !instance.suspense) {
  840. const name = Component.name ?? 'Anonymous'
  841. warn(
  842. `Component <${name}>: setup function returned a promise, but no ` +
  843. `<Suspense> boundary was found in the parent component tree. ` +
  844. `A component with async setup() must be nested in a <Suspense> ` +
  845. `in order to be rendered.`,
  846. )
  847. }
  848. } else if (__DEV__) {
  849. warn(
  850. `setup() returned a Promise, but the version of Vue you are using ` +
  851. `does not support it yet.`,
  852. )
  853. }
  854. } else {
  855. handleSetupResult(instance, setupResult, isSSR)
  856. }
  857. } else {
  858. finishComponentSetup(instance, isSSR)
  859. }
  860. }
  861. export function handleSetupResult(
  862. instance: ComponentInternalInstance,
  863. setupResult: unknown,
  864. isSSR: boolean,
  865. ): void {
  866. if (isFunction(setupResult)) {
  867. // setup returned an inline render function
  868. if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
  869. // when the function's name is `ssrRender` (compiled by SFC inline mode),
  870. // set it as ssrRender instead.
  871. instance.ssrRender = setupResult
  872. } else {
  873. instance.render = setupResult as InternalRenderFunction
  874. }
  875. } else if (isObject(setupResult)) {
  876. if (__DEV__ && isVNode(setupResult)) {
  877. warn(
  878. `setup() should not return VNodes directly - ` +
  879. `return a render function instead.`,
  880. )
  881. }
  882. // setup returned bindings.
  883. // assuming a render function compiled from template is present.
  884. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  885. instance.devtoolsRawSetupState = setupResult
  886. }
  887. instance.setupState = proxyRefs(setupResult)
  888. if (__DEV__) {
  889. exposeSetupStateOnRenderContext(instance)
  890. }
  891. } else if (__DEV__ && setupResult !== undefined) {
  892. warn(
  893. `setup() should return an object. Received: ${
  894. setupResult === null ? 'null' : typeof setupResult
  895. }`,
  896. )
  897. }
  898. finishComponentSetup(instance, isSSR)
  899. }
  900. type CompileFunction = (
  901. template: string | object,
  902. options?: CompilerOptions,
  903. ) => InternalRenderFunction
  904. let compile: CompileFunction | undefined
  905. let installWithProxy: (i: ComponentInternalInstance) => void
  906. /**
  907. * For runtime-dom to register the compiler.
  908. * Note the exported method uses any to avoid d.ts relying on the compiler types.
  909. */
  910. export function registerRuntimeCompiler(_compile: any): void {
  911. compile = _compile
  912. installWithProxy = i => {
  913. if (i.render!._rc) {
  914. i.withProxy = new Proxy(i.ctx, RuntimeCompiledPublicInstanceProxyHandlers)
  915. }
  916. }
  917. }
  918. // dev only
  919. export const isRuntimeOnly = (): boolean => !compile
  920. export function finishComponentSetup(
  921. instance: ComponentInternalInstance,
  922. isSSR: boolean,
  923. skipOptions?: boolean,
  924. ): void {
  925. const Component = instance.type as ComponentOptions
  926. if (__COMPAT__) {
  927. convertLegacyRenderFn(instance)
  928. if (__DEV__ && Component.compatConfig) {
  929. validateCompatConfig(Component.compatConfig)
  930. }
  931. }
  932. // template / render function normalization
  933. // could be already set when returned from setup()
  934. if (!instance.render) {
  935. // only do on-the-fly compile if not in SSR - SSR on-the-fly compilation
  936. // is done by server-renderer
  937. if (!isSSR && compile && !Component.render) {
  938. const template =
  939. (__COMPAT__ &&
  940. instance.vnode.props &&
  941. instance.vnode.props['inline-template']) ||
  942. Component.template ||
  943. (__FEATURE_OPTIONS_API__ && resolveMergedOptions(instance).template)
  944. if (template) {
  945. if (__DEV__) {
  946. startMeasure(instance, `compile`)
  947. }
  948. const { isCustomElement, compilerOptions } = instance.appContext.config
  949. const { delimiters, compilerOptions: componentCompilerOptions } =
  950. Component
  951. const finalCompilerOptions: CompilerOptions = extend(
  952. extend(
  953. {
  954. isCustomElement,
  955. delimiters,
  956. },
  957. compilerOptions,
  958. ),
  959. componentCompilerOptions,
  960. )
  961. if (__COMPAT__) {
  962. // pass runtime compat config into the compiler
  963. finalCompilerOptions.compatConfig = Object.create(globalCompatConfig)
  964. if (Component.compatConfig) {
  965. // @ts-expect-error types are not compatible
  966. extend(finalCompilerOptions.compatConfig, Component.compatConfig)
  967. }
  968. }
  969. Component.render = compile(template, finalCompilerOptions)
  970. if (__DEV__) {
  971. endMeasure(instance, `compile`)
  972. }
  973. }
  974. }
  975. instance.render = (Component.render || NOOP) as InternalRenderFunction
  976. // for runtime-compiled render functions using `with` blocks, the render
  977. // proxy used needs a different `has` handler which is more performant and
  978. // also only allows a whitelist of globals to fallthrough.
  979. if (installWithProxy) {
  980. installWithProxy(instance)
  981. }
  982. }
  983. // support for 2.x options
  984. if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
  985. const reset = setCurrentInstance(instance)
  986. pauseTracking()
  987. try {
  988. applyOptions(instance)
  989. } finally {
  990. resetTracking()
  991. reset()
  992. }
  993. }
  994. // warn missing template/render
  995. // the runtime compilation of template in SSR is done by server-render
  996. if (__DEV__ && !Component.render && instance.render === NOOP && !isSSR) {
  997. if (!compile && Component.template) {
  998. /* v8 ignore start */
  999. warn(
  1000. `Component provided template option but ` +
  1001. `runtime compilation is not supported in this build of Vue.` +
  1002. (__ESM_BUNDLER__
  1003. ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`
  1004. : __ESM_BROWSER__
  1005. ? ` Use "vue.esm-browser.js" instead.`
  1006. : __GLOBAL__
  1007. ? ` Use "vue.global.js" instead.`
  1008. : ``) /* should not happen */,
  1009. )
  1010. /* v8 ignore stop */
  1011. } else {
  1012. warn(`Component is missing template or render function: `, Component)
  1013. }
  1014. }
  1015. }
  1016. const attrsProxyHandlers = __DEV__
  1017. ? {
  1018. get(target: Data, key: string) {
  1019. markAttrsAccessed()
  1020. track(target, TrackOpTypes.GET, '')
  1021. return target[key]
  1022. },
  1023. set() {
  1024. warn(`setupContext.attrs is readonly.`)
  1025. return false
  1026. },
  1027. deleteProperty() {
  1028. warn(`setupContext.attrs is readonly.`)
  1029. return false
  1030. },
  1031. }
  1032. : {
  1033. get(target: Data, key: string) {
  1034. track(target, TrackOpTypes.GET, '')
  1035. return target[key]
  1036. },
  1037. }
  1038. /**
  1039. * Dev-only
  1040. */
  1041. function getSlotsProxy(instance: ComponentInternalInstance): Slots {
  1042. return new Proxy(instance.slots, {
  1043. get(target, key: string) {
  1044. track(instance, TrackOpTypes.GET, '$slots')
  1045. return target[key]
  1046. },
  1047. })
  1048. }
  1049. export function createSetupContext(
  1050. instance: ComponentInternalInstance,
  1051. ): SetupContext {
  1052. const expose: SetupContext['expose'] = exposed => {
  1053. if (__DEV__) {
  1054. if (instance.exposed) {
  1055. warn(`expose() should be called only once per setup().`)
  1056. }
  1057. if (exposed != null) {
  1058. let exposedType: string = typeof exposed
  1059. if (exposedType === 'object') {
  1060. if (isArray(exposed)) {
  1061. exposedType = 'array'
  1062. } else if (isRef(exposed)) {
  1063. exposedType = 'ref'
  1064. }
  1065. }
  1066. if (exposedType !== 'object') {
  1067. warn(
  1068. `expose() should be passed a plain object, received ${exposedType}.`,
  1069. )
  1070. }
  1071. }
  1072. }
  1073. instance.exposed = exposed || {}
  1074. }
  1075. if (__DEV__) {
  1076. // We use getters in dev in case libs like test-utils overwrite instance
  1077. // properties (overwrites should not be done in prod)
  1078. let attrsProxy: Data
  1079. let slotsProxy: Slots
  1080. return Object.freeze({
  1081. get attrs() {
  1082. return (
  1083. attrsProxy ||
  1084. (attrsProxy = new Proxy(instance.attrs, attrsProxyHandlers))
  1085. )
  1086. },
  1087. get slots() {
  1088. return slotsProxy || (slotsProxy = getSlotsProxy(instance))
  1089. },
  1090. get emit() {
  1091. return (event: string, ...args: any[]) => instance.emit(event, ...args)
  1092. },
  1093. expose,
  1094. })
  1095. } else {
  1096. return {
  1097. attrs: new Proxy(instance.attrs, attrsProxyHandlers),
  1098. slots: instance.slots,
  1099. emit: instance.emit,
  1100. expose,
  1101. }
  1102. }
  1103. }
  1104. export function getComponentPublicInstance(
  1105. instance: ComponentInternalInstance,
  1106. ): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null {
  1107. if (instance.exposed) {
  1108. return (
  1109. instance.exposeProxy ||
  1110. (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
  1111. get(target, key: string) {
  1112. if (key in target) {
  1113. return target[key]
  1114. } else if (key in publicPropertiesMap) {
  1115. return publicPropertiesMap[key](instance)
  1116. }
  1117. },
  1118. has(target, key: string) {
  1119. return key in target || key in publicPropertiesMap
  1120. },
  1121. }))
  1122. )
  1123. } else {
  1124. return instance.proxy
  1125. }
  1126. }
  1127. const classifyRE = /(?:^|[-_])(\w)/g
  1128. const classify = (str: string): string =>
  1129. str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
  1130. export function getComponentName(
  1131. Component: ConcreteComponent,
  1132. includeInferred = true,
  1133. ): string | false | undefined {
  1134. return isFunction(Component)
  1135. ? Component.displayName || Component.name
  1136. : Component.name || (includeInferred && Component.__name)
  1137. }
  1138. export function formatComponentName(
  1139. instance: ComponentInternalInstance | null,
  1140. Component: ConcreteComponent,
  1141. isRoot = false,
  1142. ): string {
  1143. let name = getComponentName(Component)
  1144. if (!name && Component.__file) {
  1145. const match = Component.__file.match(/([^/\\]+)\.\w+$/)
  1146. if (match) {
  1147. name = match[1]
  1148. }
  1149. }
  1150. if (!name && instance && instance.parent) {
  1151. // try to infer the name based on reverse resolution
  1152. const inferFromRegistry = (registry: Record<string, any> | undefined) => {
  1153. for (const key in registry) {
  1154. if (registry[key] === Component) {
  1155. return key
  1156. }
  1157. }
  1158. }
  1159. name =
  1160. inferFromRegistry(
  1161. instance.components ||
  1162. (instance.parent.type as ComponentOptions).components,
  1163. ) || inferFromRegistry(instance.appContext.components)
  1164. }
  1165. return name ? classify(name) : isRoot ? `App` : `Anonymous`
  1166. }
  1167. export function isClassComponent(value: unknown): value is ClassComponent {
  1168. return isFunction(value) && '__vccOpts' in value
  1169. }
  1170. export interface ComponentCustomElementInterface {
  1171. /**
  1172. * @internal
  1173. */
  1174. _injectChildStyle(type: ConcreteComponent): void
  1175. /**
  1176. * @internal
  1177. */
  1178. _removeChildStyle(type: ConcreteComponent): void
  1179. /**
  1180. * @internal
  1181. */
  1182. _setProp(
  1183. key: string,
  1184. val: any,
  1185. shouldReflect?: boolean,
  1186. shouldUpdate?: boolean,
  1187. ): void
  1188. /**
  1189. * @internal attached by the nested Teleport when shadowRoot is false.
  1190. */
  1191. _teleportTarget?: RendererElement
  1192. }