component.ts 31 KB

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