component.ts 32 KB

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