componentOptions.ts 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164
  1. import {
  2. ComponentInternalInstance,
  3. Data,
  4. SetupContext,
  5. ComponentInternalOptions,
  6. Component,
  7. ConcreteComponent,
  8. InternalRenderFunction
  9. } from './component'
  10. import {
  11. isFunction,
  12. extend,
  13. isString,
  14. isObject,
  15. isArray,
  16. NOOP,
  17. isPromise,
  18. LooseRequired,
  19. Prettify
  20. } from '@vue/shared'
  21. import { isRef, Ref } from '@vue/reactivity'
  22. import { computed } from './apiComputed'
  23. import {
  24. watch,
  25. WatchOptions,
  26. WatchCallback,
  27. createPathGetter
  28. } from './apiWatch'
  29. import { provide, inject } from './apiInject'
  30. import {
  31. onBeforeMount,
  32. onMounted,
  33. onBeforeUpdate,
  34. onUpdated,
  35. onErrorCaptured,
  36. onRenderTracked,
  37. onBeforeUnmount,
  38. onUnmounted,
  39. onActivated,
  40. onDeactivated,
  41. onRenderTriggered,
  42. DebuggerHook,
  43. ErrorCapturedHook,
  44. onServerPrefetch
  45. } from './apiLifecycle'
  46. import {
  47. reactive,
  48. ComputedGetter,
  49. WritableComputedOptions
  50. } from '@vue/reactivity'
  51. import {
  52. ComponentObjectPropsOptions,
  53. ExtractPropTypes,
  54. ExtractDefaultPropTypes
  55. } from './componentProps'
  56. import { EmitsOptions, EmitsToProps } from './componentEmits'
  57. import { Directive } from './directives'
  58. import {
  59. CreateComponentPublicInstance,
  60. ComponentPublicInstance,
  61. isReservedPrefix,
  62. IntersectionMixin,
  63. UnwrapMixinsType
  64. } from './componentPublicInstance'
  65. import { warn } from './warning'
  66. import { VNodeChild } from './vnode'
  67. import { callWithAsyncErrorHandling } from './errorHandling'
  68. import { deepMergeData } from './compat/data'
  69. import { DeprecationTypes } from './compat/compatConfig'
  70. import {
  71. CompatConfig,
  72. isCompatEnabled,
  73. softAssertCompatEnabled
  74. } from './compat/compatConfig'
  75. import { OptionMergeFunction } from './apiCreateApp'
  76. import { LifecycleHooks } from './enums'
  77. import { SlotsType } from './componentSlots'
  78. /**
  79. * Interface for declaring custom options.
  80. *
  81. * @example
  82. * ```ts
  83. * declare module '@vue/runtime-core' {
  84. * interface ComponentCustomOptions {
  85. * beforeRouteUpdate?(
  86. * to: Route,
  87. * from: Route,
  88. * next: () => void
  89. * ): void
  90. * }
  91. * }
  92. * ```
  93. */
  94. export interface ComponentCustomOptions {}
  95. export type RenderFunction = () => VNodeChild
  96. export interface ComponentOptionsBase<
  97. Props,
  98. RawBindings,
  99. D,
  100. C extends ComputedOptions,
  101. M extends MethodOptions,
  102. Mixin extends ComponentOptionsMixin,
  103. Extends extends ComponentOptionsMixin,
  104. E extends EmitsOptions,
  105. EE extends string = string,
  106. Defaults = {},
  107. I extends ComponentInjectOptions = {},
  108. II extends string = string,
  109. S extends SlotsType = {}
  110. > extends LegacyOptions<Props, D, C, M, Mixin, Extends, I, II>,
  111. ComponentInternalOptions,
  112. ComponentCustomOptions {
  113. setup?: (
  114. this: void,
  115. props: LooseRequired<
  116. Props &
  117. Prettify<
  118. UnwrapMixinsType<
  119. IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
  120. 'P'
  121. >
  122. >
  123. >,
  124. ctx: SetupContext<E, S>
  125. ) => Promise<RawBindings> | RawBindings | RenderFunction | void
  126. name?: string
  127. template?: string | object // can be a direct DOM node
  128. // Note: we are intentionally using the signature-less `Function` type here
  129. // since any type with signature will cause the whole inference to fail when
  130. // the return expression contains reference to `this`.
  131. // Luckily `render()` doesn't need any arguments nor does it care about return
  132. // type.
  133. render?: Function
  134. components?: Record<string, Component>
  135. directives?: Record<string, Directive>
  136. inheritAttrs?: boolean
  137. emits?: (E | EE[]) & ThisType<void>
  138. slots?: S
  139. // TODO infer public instance type based on exposed keys
  140. expose?: string[]
  141. serverPrefetch?(): void | Promise<any>
  142. // Runtime compiler only -----------------------------------------------------
  143. compilerOptions?: RuntimeCompilerOptions
  144. // Internal ------------------------------------------------------------------
  145. /**
  146. * SSR only. This is produced by compiler-ssr and attached in compiler-sfc
  147. * not user facing, so the typing is lax and for test only.
  148. * @internal
  149. */
  150. ssrRender?: (
  151. ctx: any,
  152. push: (item: any) => void,
  153. parentInstance: ComponentInternalInstance,
  154. attrs: Data | undefined,
  155. // for compiler-optimized bindings
  156. $props: ComponentInternalInstance['props'],
  157. $setup: ComponentInternalInstance['setupState'],
  158. $data: ComponentInternalInstance['data'],
  159. $options: ComponentInternalInstance['ctx']
  160. ) => void
  161. /**
  162. * Only generated by compiler-sfc to mark a ssr render function inlined and
  163. * returned from setup()
  164. * @internal
  165. */
  166. __ssrInlineRender?: boolean
  167. /**
  168. * marker for AsyncComponentWrapper
  169. * @internal
  170. */
  171. __asyncLoader?: () => Promise<ConcreteComponent>
  172. /**
  173. * the inner component resolved by the AsyncComponentWrapper
  174. * @internal
  175. */
  176. __asyncResolved?: ConcreteComponent
  177. // Type differentiators ------------------------------------------------------
  178. // Note these are internal but need to be exposed in d.ts for type inference
  179. // to work!
  180. // type-only differentiator to separate OptionWithoutProps from a constructor
  181. // type returned by defineComponent() or FunctionalComponent
  182. call?: (this: unknown, ...args: unknown[]) => never
  183. // type-only differentiators for built-in Vnode types
  184. __isFragment?: never
  185. __isTeleport?: never
  186. __isSuspense?: never
  187. __defaults?: Defaults
  188. }
  189. /**
  190. * Subset of compiler options that makes sense for the runtime.
  191. */
  192. export interface RuntimeCompilerOptions {
  193. isCustomElement?: (tag: string) => boolean
  194. whitespace?: 'preserve' | 'condense'
  195. comments?: boolean
  196. delimiters?: [string, string]
  197. }
  198. export type ComponentOptionsWithoutProps<
  199. Props = {},
  200. RawBindings = {},
  201. D = {},
  202. C extends ComputedOptions = {},
  203. M extends MethodOptions = {},
  204. Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  205. Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  206. E extends EmitsOptions = EmitsOptions,
  207. EE extends string = string,
  208. I extends ComponentInjectOptions = {},
  209. II extends string = string,
  210. S extends SlotsType = {},
  211. PE = Props & EmitsToProps<E>
  212. > = ComponentOptionsBase<
  213. PE,
  214. RawBindings,
  215. D,
  216. C,
  217. M,
  218. Mixin,
  219. Extends,
  220. E,
  221. EE,
  222. {},
  223. I,
  224. II,
  225. S
  226. > & {
  227. props?: undefined
  228. } & ThisType<
  229. CreateComponentPublicInstance<
  230. PE,
  231. RawBindings,
  232. D,
  233. C,
  234. M,
  235. Mixin,
  236. Extends,
  237. E,
  238. PE,
  239. {},
  240. false,
  241. I,
  242. S
  243. >
  244. >
  245. export type ComponentOptionsWithArrayProps<
  246. PropNames extends string = string,
  247. RawBindings = {},
  248. D = {},
  249. C extends ComputedOptions = {},
  250. M extends MethodOptions = {},
  251. Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  252. Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  253. E extends EmitsOptions = EmitsOptions,
  254. EE extends string = string,
  255. I extends ComponentInjectOptions = {},
  256. II extends string = string,
  257. S extends SlotsType = {},
  258. Props = Prettify<Readonly<{ [key in PropNames]?: any } & EmitsToProps<E>>>
  259. > = ComponentOptionsBase<
  260. Props,
  261. RawBindings,
  262. D,
  263. C,
  264. M,
  265. Mixin,
  266. Extends,
  267. E,
  268. EE,
  269. {},
  270. I,
  271. II,
  272. S
  273. > & {
  274. props: PropNames[]
  275. } & ThisType<
  276. CreateComponentPublicInstance<
  277. Props,
  278. RawBindings,
  279. D,
  280. C,
  281. M,
  282. Mixin,
  283. Extends,
  284. E,
  285. Props,
  286. {},
  287. false,
  288. I,
  289. S
  290. >
  291. >
  292. export type ComponentOptionsWithObjectProps<
  293. PropsOptions = ComponentObjectPropsOptions,
  294. RawBindings = {},
  295. D = {},
  296. C extends ComputedOptions = {},
  297. M extends MethodOptions = {},
  298. Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  299. Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  300. E extends EmitsOptions = EmitsOptions,
  301. EE extends string = string,
  302. I extends ComponentInjectOptions = {},
  303. II extends string = string,
  304. S extends SlotsType = {},
  305. Props = Prettify<Readonly<ExtractPropTypes<PropsOptions> & EmitsToProps<E>>>,
  306. Defaults = ExtractDefaultPropTypes<PropsOptions>
  307. > = ComponentOptionsBase<
  308. Props,
  309. RawBindings,
  310. D,
  311. C,
  312. M,
  313. Mixin,
  314. Extends,
  315. E,
  316. EE,
  317. Defaults,
  318. I,
  319. II,
  320. S
  321. > & {
  322. props: PropsOptions & ThisType<void>
  323. } & ThisType<
  324. CreateComponentPublicInstance<
  325. Props,
  326. RawBindings,
  327. D,
  328. C,
  329. M,
  330. Mixin,
  331. Extends,
  332. E,
  333. Props,
  334. Defaults,
  335. false,
  336. I,
  337. S
  338. >
  339. >
  340. export type ComponentOptions<
  341. Props = {},
  342. RawBindings = any,
  343. D = any,
  344. C extends ComputedOptions = any,
  345. M extends MethodOptions = any,
  346. Mixin extends ComponentOptionsMixin = any,
  347. Extends extends ComponentOptionsMixin = any,
  348. E extends EmitsOptions = any,
  349. S extends SlotsType = any
  350. > = ComponentOptionsBase<
  351. Props,
  352. RawBindings,
  353. D,
  354. C,
  355. M,
  356. Mixin,
  357. Extends,
  358. E,
  359. string,
  360. S
  361. > &
  362. ThisType<
  363. CreateComponentPublicInstance<
  364. {},
  365. RawBindings,
  366. D,
  367. C,
  368. M,
  369. Mixin,
  370. Extends,
  371. E,
  372. Readonly<Props>
  373. >
  374. >
  375. export type ComponentOptionsMixin = ComponentOptionsBase<
  376. any,
  377. any,
  378. any,
  379. any,
  380. any,
  381. any,
  382. any,
  383. any,
  384. any,
  385. any,
  386. any
  387. >
  388. export type ComputedOptions = Record<
  389. string,
  390. ComputedGetter<any> | WritableComputedOptions<any>
  391. >
  392. export interface MethodOptions {
  393. [key: string]: Function
  394. }
  395. export type ExtractComputedReturns<T extends any> = {
  396. [key in keyof T]: T[key] extends { get: (...args: any[]) => infer TReturn }
  397. ? TReturn
  398. : T[key] extends (...args: any[]) => infer TReturn
  399. ? TReturn
  400. : never
  401. }
  402. export type ObjectWatchOptionItem = {
  403. handler: WatchCallback | string
  404. } & WatchOptions
  405. type WatchOptionItem = string | WatchCallback | ObjectWatchOptionItem
  406. type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[]
  407. type ComponentWatchOptions = Record<string, ComponentWatchOptionItem>
  408. export type ComponentProvideOptions = ObjectProvideOptions | Function
  409. type ObjectProvideOptions = Record<string | symbol, unknown>
  410. export type ComponentInjectOptions = string[] | ObjectInjectOptions
  411. type ObjectInjectOptions = Record<
  412. string | symbol,
  413. string | symbol | { from?: string | symbol; default?: unknown }
  414. >
  415. export type InjectToObject<T extends ComponentInjectOptions> =
  416. T extends string[]
  417. ? {
  418. [K in T[number]]?: unknown
  419. }
  420. : T extends ObjectInjectOptions
  421. ? {
  422. [K in keyof T]?: unknown
  423. }
  424. : never
  425. interface LegacyOptions<
  426. Props,
  427. D,
  428. C extends ComputedOptions,
  429. M extends MethodOptions,
  430. Mixin extends ComponentOptionsMixin,
  431. Extends extends ComponentOptionsMixin,
  432. I extends ComponentInjectOptions,
  433. II extends string
  434. > {
  435. compatConfig?: CompatConfig
  436. // allow any custom options
  437. [key: string]: any
  438. // state
  439. // Limitation: we cannot expose RawBindings on the `this` context for data
  440. // since that leads to some sort of circular inference and breaks ThisType
  441. // for the entire component.
  442. data?: (
  443. this: CreateComponentPublicInstance<
  444. Props,
  445. {},
  446. {},
  447. {},
  448. MethodOptions,
  449. Mixin,
  450. Extends
  451. >,
  452. vm: CreateComponentPublicInstance<
  453. Props,
  454. {},
  455. {},
  456. {},
  457. MethodOptions,
  458. Mixin,
  459. Extends
  460. >
  461. ) => D
  462. computed?: C
  463. methods?: M
  464. watch?: ComponentWatchOptions
  465. provide?: ComponentProvideOptions
  466. inject?: I | II[]
  467. // assets
  468. filters?: Record<string, Function>
  469. // composition
  470. mixins?: Mixin[]
  471. extends?: Extends
  472. // lifecycle
  473. beforeCreate?(): void
  474. created?(): void
  475. beforeMount?(): void
  476. mounted?(): void
  477. beforeUpdate?(): void
  478. updated?(): void
  479. activated?(): void
  480. deactivated?(): void
  481. /** @deprecated use `beforeUnmount` instead */
  482. beforeDestroy?(): void
  483. beforeUnmount?(): void
  484. /** @deprecated use `unmounted` instead */
  485. destroyed?(): void
  486. unmounted?(): void
  487. renderTracked?: DebuggerHook
  488. renderTriggered?: DebuggerHook
  489. errorCaptured?: ErrorCapturedHook
  490. /**
  491. * runtime compile only
  492. * @deprecated use `compilerOptions.delimiters` instead.
  493. */
  494. delimiters?: [string, string]
  495. /**
  496. * #3468
  497. *
  498. * type-only, used to assist Mixin's type inference,
  499. * typescript will try to simplify the inferred `Mixin` type,
  500. * with the `__differentiator`, typescript won't be able to combine different mixins,
  501. * because the `__differentiator` will be different
  502. */
  503. __differentiator?: keyof D | keyof C | keyof M
  504. }
  505. type MergedHook<T = () => void> = T | T[]
  506. export type MergedComponentOptions = ComponentOptions &
  507. MergedComponentOptionsOverride
  508. export type MergedComponentOptionsOverride = {
  509. beforeCreate?: MergedHook
  510. created?: MergedHook
  511. beforeMount?: MergedHook
  512. mounted?: MergedHook
  513. beforeUpdate?: MergedHook
  514. updated?: MergedHook
  515. activated?: MergedHook
  516. deactivated?: MergedHook
  517. /** @deprecated use `beforeUnmount` instead */
  518. beforeDestroy?: MergedHook
  519. beforeUnmount?: MergedHook
  520. /** @deprecated use `unmounted` instead */
  521. destroyed?: MergedHook
  522. unmounted?: MergedHook
  523. renderTracked?: MergedHook<DebuggerHook>
  524. renderTriggered?: MergedHook<DebuggerHook>
  525. errorCaptured?: MergedHook<ErrorCapturedHook>
  526. }
  527. export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'Defaults'
  528. export type OptionTypesType<
  529. P = {},
  530. B = {},
  531. D = {},
  532. C extends ComputedOptions = {},
  533. M extends MethodOptions = {},
  534. Defaults = {}
  535. > = {
  536. P: P
  537. B: B
  538. D: D
  539. C: C
  540. M: M
  541. Defaults: Defaults
  542. }
  543. const enum OptionTypes {
  544. PROPS = 'Props',
  545. DATA = 'Data',
  546. COMPUTED = 'Computed',
  547. METHODS = 'Methods',
  548. INJECT = 'Inject'
  549. }
  550. function createDuplicateChecker() {
  551. const cache = Object.create(null)
  552. return (type: OptionTypes, key: string) => {
  553. if (cache[key]) {
  554. warn(`${type} property "${key}" is already defined in ${cache[key]}.`)
  555. } else {
  556. cache[key] = type
  557. }
  558. }
  559. }
  560. export let shouldCacheAccess = true
  561. export function applyOptions(instance: ComponentInternalInstance) {
  562. const options = resolveMergedOptions(instance)
  563. const publicThis = instance.proxy! as any
  564. const ctx = instance.ctx
  565. // do not cache property access on public proxy during state initialization
  566. shouldCacheAccess = false
  567. // call beforeCreate first before accessing other options since
  568. // the hook may mutate resolved options (#2791)
  569. if (options.beforeCreate) {
  570. callHook(options.beforeCreate, instance, LifecycleHooks.BEFORE_CREATE)
  571. }
  572. const {
  573. // state
  574. data: dataOptions,
  575. computed: computedOptions,
  576. methods,
  577. watch: watchOptions,
  578. provide: provideOptions,
  579. inject: injectOptions,
  580. // lifecycle
  581. created,
  582. beforeMount,
  583. mounted,
  584. beforeUpdate,
  585. updated,
  586. activated,
  587. deactivated,
  588. beforeDestroy,
  589. beforeUnmount,
  590. destroyed,
  591. unmounted,
  592. render,
  593. renderTracked,
  594. renderTriggered,
  595. errorCaptured,
  596. serverPrefetch,
  597. // public API
  598. expose,
  599. inheritAttrs,
  600. // assets
  601. components,
  602. directives,
  603. filters
  604. } = options
  605. const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null
  606. if (__DEV__) {
  607. const [propsOptions] = instance.propsOptions
  608. if (propsOptions) {
  609. for (const key in propsOptions) {
  610. checkDuplicateProperties!(OptionTypes.PROPS, key)
  611. }
  612. }
  613. }
  614. // options initialization order (to be consistent with Vue 2):
  615. // - props (already done outside of this function)
  616. // - inject
  617. // - methods
  618. // - data (deferred since it relies on `this` access)
  619. // - computed
  620. // - watch (deferred since it relies on `this` access)
  621. if (injectOptions) {
  622. resolveInjections(
  623. injectOptions,
  624. ctx,
  625. checkDuplicateProperties,
  626. instance.appContext.config.unwrapInjectedRef
  627. )
  628. }
  629. if (methods) {
  630. for (const key in methods) {
  631. const methodHandler = (methods as MethodOptions)[key]
  632. if (isFunction(methodHandler)) {
  633. // In dev mode, we use the `createRenderContext` function to define
  634. // methods to the proxy target, and those are read-only but
  635. // reconfigurable, so it needs to be redefined here
  636. if (__DEV__) {
  637. Object.defineProperty(ctx, key, {
  638. value: methodHandler.bind(publicThis),
  639. configurable: true,
  640. enumerable: true,
  641. writable: true
  642. })
  643. } else {
  644. ctx[key] = methodHandler.bind(publicThis)
  645. }
  646. if (__DEV__) {
  647. checkDuplicateProperties!(OptionTypes.METHODS, key)
  648. }
  649. } else if (__DEV__) {
  650. warn(
  651. `Method "${key}" has type "${typeof methodHandler}" in the component definition. ` +
  652. `Did you reference the function correctly?`
  653. )
  654. }
  655. }
  656. }
  657. if (dataOptions) {
  658. if (__DEV__ && !isFunction(dataOptions)) {
  659. warn(
  660. `The data option must be a function. ` +
  661. `Plain object usage is no longer supported.`
  662. )
  663. }
  664. const data = dataOptions.call(publicThis, publicThis)
  665. if (__DEV__ && isPromise(data)) {
  666. warn(
  667. `data() returned a Promise - note data() cannot be async; If you ` +
  668. `intend to perform data fetching before component renders, use ` +
  669. `async setup() + <Suspense>.`
  670. )
  671. }
  672. if (!isObject(data)) {
  673. __DEV__ && warn(`data() should return an object.`)
  674. } else {
  675. instance.data = reactive(data)
  676. if (__DEV__) {
  677. for (const key in data) {
  678. checkDuplicateProperties!(OptionTypes.DATA, key)
  679. // expose data on ctx during dev
  680. if (!isReservedPrefix(key[0])) {
  681. Object.defineProperty(ctx, key, {
  682. configurable: true,
  683. enumerable: true,
  684. get: () => data[key],
  685. set: NOOP
  686. })
  687. }
  688. }
  689. }
  690. }
  691. }
  692. // state initialization complete at this point - start caching access
  693. shouldCacheAccess = true
  694. if (computedOptions) {
  695. for (const key in computedOptions) {
  696. const opt = (computedOptions as ComputedOptions)[key]
  697. const get = isFunction(opt)
  698. ? opt.bind(publicThis, publicThis)
  699. : isFunction(opt.get)
  700. ? opt.get.bind(publicThis, publicThis)
  701. : NOOP
  702. if (__DEV__ && get === NOOP) {
  703. warn(`Computed property "${key}" has no getter.`)
  704. }
  705. const set =
  706. !isFunction(opt) && isFunction(opt.set)
  707. ? opt.set.bind(publicThis)
  708. : __DEV__
  709. ? () => {
  710. warn(
  711. `Write operation failed: computed property "${key}" is readonly.`
  712. )
  713. }
  714. : NOOP
  715. const c = computed({
  716. get,
  717. set
  718. })
  719. Object.defineProperty(ctx, key, {
  720. enumerable: true,
  721. configurable: true,
  722. get: () => c.value,
  723. set: v => (c.value = v)
  724. })
  725. if (__DEV__) {
  726. checkDuplicateProperties!(OptionTypes.COMPUTED, key)
  727. }
  728. }
  729. }
  730. if (watchOptions) {
  731. for (const key in watchOptions) {
  732. createWatcher(watchOptions[key], ctx, publicThis, key)
  733. }
  734. }
  735. if (provideOptions) {
  736. const provides = isFunction(provideOptions)
  737. ? provideOptions.call(publicThis)
  738. : provideOptions
  739. Reflect.ownKeys(provides).forEach(key => {
  740. provide(key, provides[key])
  741. })
  742. }
  743. if (created) {
  744. callHook(created, instance, LifecycleHooks.CREATED)
  745. }
  746. function registerLifecycleHook(
  747. register: Function,
  748. hook?: Function | Function[]
  749. ) {
  750. if (isArray(hook)) {
  751. hook.forEach(_hook => register(_hook.bind(publicThis)))
  752. } else if (hook) {
  753. register((hook as Function).bind(publicThis))
  754. }
  755. }
  756. registerLifecycleHook(onBeforeMount, beforeMount)
  757. registerLifecycleHook(onMounted, mounted)
  758. registerLifecycleHook(onBeforeUpdate, beforeUpdate)
  759. registerLifecycleHook(onUpdated, updated)
  760. registerLifecycleHook(onActivated, activated)
  761. registerLifecycleHook(onDeactivated, deactivated)
  762. registerLifecycleHook(onErrorCaptured, errorCaptured)
  763. registerLifecycleHook(onRenderTracked, renderTracked)
  764. registerLifecycleHook(onRenderTriggered, renderTriggered)
  765. registerLifecycleHook(onBeforeUnmount, beforeUnmount)
  766. registerLifecycleHook(onUnmounted, unmounted)
  767. registerLifecycleHook(onServerPrefetch, serverPrefetch)
  768. if (__COMPAT__) {
  769. if (
  770. beforeDestroy &&
  771. softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY, instance)
  772. ) {
  773. registerLifecycleHook(onBeforeUnmount, beforeDestroy)
  774. }
  775. if (
  776. destroyed &&
  777. softAssertCompatEnabled(DeprecationTypes.OPTIONS_DESTROYED, instance)
  778. ) {
  779. registerLifecycleHook(onUnmounted, destroyed)
  780. }
  781. }
  782. if (isArray(expose)) {
  783. if (expose.length) {
  784. const exposed = instance.exposed || (instance.exposed = {})
  785. expose.forEach(key => {
  786. Object.defineProperty(exposed, key, {
  787. get: () => publicThis[key],
  788. set: val => (publicThis[key] = val)
  789. })
  790. })
  791. } else if (!instance.exposed) {
  792. instance.exposed = {}
  793. }
  794. }
  795. // options that are handled when creating the instance but also need to be
  796. // applied from mixins
  797. if (render && instance.render === NOOP) {
  798. instance.render = render as InternalRenderFunction
  799. }
  800. if (inheritAttrs != null) {
  801. instance.inheritAttrs = inheritAttrs
  802. }
  803. // asset options.
  804. if (components) instance.components = components as any
  805. if (directives) instance.directives = directives
  806. if (
  807. __COMPAT__ &&
  808. filters &&
  809. isCompatEnabled(DeprecationTypes.FILTERS, instance)
  810. ) {
  811. instance.filters = filters
  812. }
  813. }
  814. export function resolveInjections(
  815. injectOptions: ComponentInjectOptions,
  816. ctx: any,
  817. checkDuplicateProperties = NOOP as any,
  818. unwrapRef = false
  819. ) {
  820. if (isArray(injectOptions)) {
  821. injectOptions = normalizeInject(injectOptions)!
  822. }
  823. for (const key in injectOptions) {
  824. const opt = (injectOptions as ObjectInjectOptions)[key]
  825. let injected: unknown
  826. if (isObject(opt)) {
  827. if ('default' in opt) {
  828. injected = inject(
  829. opt.from || key,
  830. opt.default,
  831. true /* treat default function as factory */
  832. )
  833. } else {
  834. injected = inject(opt.from || key)
  835. }
  836. } else {
  837. injected = inject(opt)
  838. }
  839. if (isRef(injected)) {
  840. // TODO remove the check in 3.3
  841. if (unwrapRef) {
  842. Object.defineProperty(ctx, key, {
  843. enumerable: true,
  844. configurable: true,
  845. get: () => (injected as Ref).value,
  846. set: v => ((injected as Ref).value = v)
  847. })
  848. } else {
  849. if (__DEV__) {
  850. warn(
  851. `injected property "${key}" is a ref and will be auto-unwrapped ` +
  852. `and no longer needs \`.value\` in the next minor release. ` +
  853. `To opt-in to the new behavior now, ` +
  854. `set \`app.config.unwrapInjectedRef = true\` (this config is ` +
  855. `temporary and will not be needed in the future.)`
  856. )
  857. }
  858. ctx[key] = injected
  859. }
  860. } else {
  861. ctx[key] = injected
  862. }
  863. if (__DEV__) {
  864. checkDuplicateProperties!(OptionTypes.INJECT, key)
  865. }
  866. }
  867. }
  868. function callHook(
  869. hook: Function,
  870. instance: ComponentInternalInstance,
  871. type: LifecycleHooks
  872. ) {
  873. callWithAsyncErrorHandling(
  874. isArray(hook)
  875. ? hook.map(h => h.bind(instance.proxy!))
  876. : hook.bind(instance.proxy!),
  877. instance,
  878. type
  879. )
  880. }
  881. export function createWatcher(
  882. raw: ComponentWatchOptionItem,
  883. ctx: Data,
  884. publicThis: ComponentPublicInstance,
  885. key: string
  886. ) {
  887. const getter = key.includes('.')
  888. ? createPathGetter(publicThis, key)
  889. : () => (publicThis as any)[key]
  890. if (isString(raw)) {
  891. const handler = ctx[raw]
  892. if (isFunction(handler)) {
  893. watch(getter, handler as WatchCallback)
  894. } else if (__DEV__) {
  895. warn(`Invalid watch handler specified by key "${raw}"`, handler)
  896. }
  897. } else if (isFunction(raw)) {
  898. watch(getter, raw.bind(publicThis))
  899. } else if (isObject(raw)) {
  900. if (isArray(raw)) {
  901. raw.forEach(r => createWatcher(r, ctx, publicThis, key))
  902. } else {
  903. const handler = isFunction(raw.handler)
  904. ? raw.handler.bind(publicThis)
  905. : (ctx[raw.handler] as WatchCallback)
  906. if (isFunction(handler)) {
  907. watch(getter, handler, raw)
  908. } else if (__DEV__) {
  909. warn(`Invalid watch handler specified by key "${raw.handler}"`, handler)
  910. }
  911. }
  912. } else if (__DEV__) {
  913. warn(`Invalid watch option: "${key}"`, raw)
  914. }
  915. }
  916. /**
  917. * Resolve merged options and cache it on the component.
  918. * This is done only once per-component since the merging does not involve
  919. * instances.
  920. */
  921. export function resolveMergedOptions(
  922. instance: ComponentInternalInstance
  923. ): MergedComponentOptions {
  924. const base = instance.type as ComponentOptions
  925. const { mixins, extends: extendsOptions } = base
  926. const {
  927. mixins: globalMixins,
  928. optionsCache: cache,
  929. config: { optionMergeStrategies }
  930. } = instance.appContext
  931. const cached = cache.get(base)
  932. let resolved: MergedComponentOptions
  933. if (cached) {
  934. resolved = cached
  935. } else if (!globalMixins.length && !mixins && !extendsOptions) {
  936. if (
  937. __COMPAT__ &&
  938. isCompatEnabled(DeprecationTypes.PRIVATE_APIS, instance)
  939. ) {
  940. resolved = extend({}, base) as MergedComponentOptions
  941. resolved.parent = instance.parent && instance.parent.proxy
  942. resolved.propsData = instance.vnode.props
  943. } else {
  944. resolved = base as MergedComponentOptions
  945. }
  946. } else {
  947. resolved = {}
  948. if (globalMixins.length) {
  949. globalMixins.forEach(m =>
  950. mergeOptions(resolved, m, optionMergeStrategies, true)
  951. )
  952. }
  953. mergeOptions(resolved, base, optionMergeStrategies)
  954. }
  955. if (isObject(base)) {
  956. cache.set(base, resolved)
  957. }
  958. return resolved
  959. }
  960. export function mergeOptions(
  961. to: any,
  962. from: any,
  963. strats: Record<string, OptionMergeFunction>,
  964. asMixin = false
  965. ) {
  966. if (__COMPAT__ && isFunction(from)) {
  967. from = from.options
  968. }
  969. const { mixins, extends: extendsOptions } = from
  970. if (extendsOptions) {
  971. mergeOptions(to, extendsOptions, strats, true)
  972. }
  973. if (mixins) {
  974. mixins.forEach((m: ComponentOptionsMixin) =>
  975. mergeOptions(to, m, strats, true)
  976. )
  977. }
  978. for (const key in from) {
  979. if (asMixin && key === 'expose') {
  980. __DEV__ &&
  981. warn(
  982. `"expose" option is ignored when declared in mixins or extends. ` +
  983. `It should only be declared in the base component itself.`
  984. )
  985. } else {
  986. const strat = internalOptionMergeStrats[key] || (strats && strats[key])
  987. to[key] = strat ? strat(to[key], from[key]) : from[key]
  988. }
  989. }
  990. return to
  991. }
  992. export const internalOptionMergeStrats: Record<string, Function> = {
  993. data: mergeDataFn,
  994. props: mergeObjectOptions, // TODO
  995. emits: mergeObjectOptions, // TODO
  996. // objects
  997. methods: mergeObjectOptions,
  998. computed: mergeObjectOptions,
  999. // lifecycle
  1000. beforeCreate: mergeAsArray,
  1001. created: mergeAsArray,
  1002. beforeMount: mergeAsArray,
  1003. mounted: mergeAsArray,
  1004. beforeUpdate: mergeAsArray,
  1005. updated: mergeAsArray,
  1006. beforeDestroy: mergeAsArray,
  1007. beforeUnmount: mergeAsArray,
  1008. destroyed: mergeAsArray,
  1009. unmounted: mergeAsArray,
  1010. activated: mergeAsArray,
  1011. deactivated: mergeAsArray,
  1012. errorCaptured: mergeAsArray,
  1013. serverPrefetch: mergeAsArray,
  1014. // assets
  1015. components: mergeObjectOptions,
  1016. directives: mergeObjectOptions,
  1017. // watch
  1018. watch: mergeWatchOptions,
  1019. // provide / inject
  1020. provide: mergeDataFn,
  1021. inject: mergeInject
  1022. }
  1023. if (__COMPAT__) {
  1024. internalOptionMergeStrats.filters = mergeObjectOptions
  1025. }
  1026. function mergeDataFn(to: any, from: any) {
  1027. if (!from) {
  1028. return to
  1029. }
  1030. if (!to) {
  1031. return from
  1032. }
  1033. return function mergedDataFn(this: ComponentPublicInstance) {
  1034. return (
  1035. __COMPAT__ && isCompatEnabled(DeprecationTypes.OPTIONS_DATA_MERGE, null)
  1036. ? deepMergeData
  1037. : extend
  1038. )(
  1039. isFunction(to) ? to.call(this, this) : to,
  1040. isFunction(from) ? from.call(this, this) : from
  1041. )
  1042. }
  1043. }
  1044. function mergeInject(
  1045. to: ComponentInjectOptions | undefined,
  1046. from: ComponentInjectOptions
  1047. ) {
  1048. return mergeObjectOptions(normalizeInject(to), normalizeInject(from))
  1049. }
  1050. function normalizeInject(
  1051. raw: ComponentInjectOptions | undefined
  1052. ): ObjectInjectOptions | undefined {
  1053. if (isArray(raw)) {
  1054. const res: ObjectInjectOptions = {}
  1055. for (let i = 0; i < raw.length; i++) {
  1056. res[raw[i]] = raw[i]
  1057. }
  1058. return res
  1059. }
  1060. return raw
  1061. }
  1062. function mergeAsArray<T = Function>(to: T[] | T | undefined, from: T | T[]) {
  1063. return to ? [...new Set([].concat(to as any, from as any))] : from
  1064. }
  1065. function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
  1066. return to ? extend(extend(Object.create(null), to), from) : from
  1067. }
  1068. function mergeWatchOptions(
  1069. to: ComponentWatchOptions | undefined,
  1070. from: ComponentWatchOptions | undefined
  1071. ) {
  1072. if (!to) return from
  1073. if (!from) return to
  1074. const merged = extend(Object.create(null), to)
  1075. for (const key in from) {
  1076. merged[key] = mergeAsArray(to[key], from[key])
  1077. }
  1078. return merged
  1079. }