componentOptions.ts 27 KB

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