2
0

component.ts 29 KB

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