component.ts 25 KB

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