component.ts 25 KB

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