componentOptions.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  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. EMPTY_OBJ,
  17. NOOP,
  18. hasOwn,
  19. isPromise
  20. } from '@vue/shared'
  21. import { computed } from './apiComputed'
  22. import { watch, WatchOptions, WatchCallback } from './apiWatch'
  23. import { provide, inject } from './apiInject'
  24. import {
  25. onBeforeMount,
  26. onMounted,
  27. onBeforeUpdate,
  28. onUpdated,
  29. onErrorCaptured,
  30. onRenderTracked,
  31. onBeforeUnmount,
  32. onUnmounted,
  33. onActivated,
  34. onDeactivated,
  35. onRenderTriggered,
  36. DebuggerHook,
  37. ErrorCapturedHook
  38. } from './apiLifecycle'
  39. import {
  40. reactive,
  41. ComputedGetter,
  42. WritableComputedOptions,
  43. toRaw
  44. } from '@vue/reactivity'
  45. import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
  46. import { EmitsOptions } from './componentEmits'
  47. import { Directive } from './directives'
  48. import {
  49. CreateComponentPublicInstance,
  50. ComponentPublicInstance
  51. } from './componentPublicInstance'
  52. import { warn } from './warning'
  53. import { VNodeChild } from './vnode'
  54. /**
  55. * Interface for declaring custom options.
  56. *
  57. * @example
  58. * ```ts
  59. * declare module '@vue/runtime-core' {
  60. * interface ComponentCustomOptions {
  61. * beforeRouteUpdate?(
  62. * to: Route,
  63. * from: Route,
  64. * next: () => void
  65. * ): void
  66. * }
  67. * }
  68. * ```
  69. */
  70. export interface ComponentCustomOptions {}
  71. export type RenderFunction = () => VNodeChild
  72. export interface ComponentOptionsBase<
  73. Props,
  74. RawBindings,
  75. D,
  76. C extends ComputedOptions,
  77. M extends MethodOptions,
  78. Mixin extends ComponentOptionsMixin,
  79. Extends extends ComponentOptionsMixin,
  80. E extends EmitsOptions,
  81. EE extends string = string
  82. >
  83. extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
  84. ComponentInternalOptions,
  85. ComponentCustomOptions {
  86. setup?: (
  87. this: void,
  88. props: Props,
  89. ctx: SetupContext<E>
  90. ) => Promise<RawBindings> | RawBindings | RenderFunction | void
  91. name?: string
  92. template?: string | object // can be a direct DOM node
  93. // Note: we are intentionally using the signature-less `Function` type here
  94. // since any type with signature will cause the whole inference to fail when
  95. // the return expression contains reference to `this`.
  96. // Luckily `render()` doesn't need any arguments nor does it care about return
  97. // type.
  98. render?: Function
  99. components?: Record<string, Component>
  100. directives?: Record<string, Directive>
  101. inheritAttrs?: boolean
  102. emits?: (E | EE[]) & ThisType<void>
  103. serverPrefetch?(): Promise<any>
  104. // Internal ------------------------------------------------------------------
  105. /**
  106. * SSR only. This is produced by compiler-ssr and attached in compiler-sfc
  107. * not user facing, so the typing is lax and for test only.
  108. *
  109. * @internal
  110. */
  111. ssrRender?: (
  112. ctx: any,
  113. push: (item: any) => void,
  114. parentInstance: ComponentInternalInstance,
  115. attrs: Data | undefined,
  116. // for compiler-optimized bindings
  117. $props: ComponentInternalInstance['props'],
  118. $setup: ComponentInternalInstance['setupState'],
  119. $data: ComponentInternalInstance['data'],
  120. $options: ComponentInternalInstance['ctx']
  121. ) => void
  122. /**
  123. * marker for AsyncComponentWrapper
  124. * @internal
  125. */
  126. __asyncLoader?: () => Promise<ConcreteComponent>
  127. /**
  128. * cache for merged $options
  129. * @internal
  130. */
  131. __merged?: ComponentOptions
  132. // Type differentiators ------------------------------------------------------
  133. // Note these are internal but need to be exposed in d.ts for type inference
  134. // to work!
  135. // type-only differentiator to separate OptionWithoutProps from a constructor
  136. // type returned by defineComponent() or FunctionalComponent
  137. call?: (this: unknown, ...args: unknown[]) => never
  138. // type-only differentiators for built-in Vnode types
  139. __isFragment?: never
  140. __isTeleport?: never
  141. __isSuspense?: never
  142. }
  143. export type ComponentOptionsWithoutProps<
  144. Props = {},
  145. RawBindings = {},
  146. D = {},
  147. C extends ComputedOptions = {},
  148. M extends MethodOptions = {},
  149. Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  150. Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  151. E extends EmitsOptions = EmitsOptions,
  152. EE extends string = string
  153. > = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
  154. props?: undefined
  155. } & ThisType<
  156. CreateComponentPublicInstance<
  157. {},
  158. RawBindings,
  159. D,
  160. C,
  161. M,
  162. Mixin,
  163. Extends,
  164. E,
  165. Readonly<Props>
  166. >
  167. >
  168. export type ComponentOptionsWithArrayProps<
  169. PropNames extends string = string,
  170. RawBindings = {},
  171. D = {},
  172. C extends ComputedOptions = {},
  173. M extends MethodOptions = {},
  174. Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  175. Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  176. E extends EmitsOptions = EmitsOptions,
  177. EE extends string = string,
  178. Props = Readonly<{ [key in PropNames]?: any }>
  179. > = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
  180. props: PropNames[]
  181. } & ThisType<
  182. CreateComponentPublicInstance<
  183. Props,
  184. RawBindings,
  185. D,
  186. C,
  187. M,
  188. Mixin,
  189. Extends,
  190. E
  191. >
  192. >
  193. export type ComponentOptionsWithObjectProps<
  194. PropsOptions = ComponentObjectPropsOptions,
  195. RawBindings = {},
  196. D = {},
  197. C extends ComputedOptions = {},
  198. M extends MethodOptions = {},
  199. Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  200. Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  201. E extends EmitsOptions = EmitsOptions,
  202. EE extends string = string,
  203. Props = Readonly<ExtractPropTypes<PropsOptions>>
  204. > = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
  205. props: PropsOptions & ThisType<void>
  206. } & ThisType<
  207. CreateComponentPublicInstance<
  208. Props,
  209. RawBindings,
  210. D,
  211. C,
  212. M,
  213. Mixin,
  214. Extends,
  215. E
  216. >
  217. >
  218. export type ComponentOptions<
  219. Props = {},
  220. RawBindings = any,
  221. D = any,
  222. C extends ComputedOptions = any,
  223. M extends MethodOptions = any,
  224. Mixin extends ComponentOptionsMixin = any,
  225. Extends extends ComponentOptionsMixin = any,
  226. E extends EmitsOptions = any
  227. > = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E> &
  228. ThisType<
  229. CreateComponentPublicInstance<
  230. {},
  231. RawBindings,
  232. D,
  233. C,
  234. M,
  235. Mixin,
  236. Extends,
  237. E,
  238. Readonly<Props>
  239. >
  240. >
  241. export type ComponentOptionsMixin = ComponentOptionsBase<
  242. any,
  243. any,
  244. any,
  245. any,
  246. any,
  247. any,
  248. any,
  249. any,
  250. any
  251. >
  252. export type ComputedOptions = Record<
  253. string,
  254. ComputedGetter<any> | WritableComputedOptions<any>
  255. >
  256. export interface MethodOptions {
  257. [key: string]: Function
  258. }
  259. export type ExtractComputedReturns<T extends any> = {
  260. [key in keyof T]: T[key] extends { get: (...args: any[]) => infer TReturn }
  261. ? TReturn
  262. : T[key] extends (...args: any[]) => infer TReturn ? TReturn : never
  263. }
  264. type WatchOptionItem =
  265. | string
  266. | WatchCallback
  267. | { handler: WatchCallback | string } & WatchOptions
  268. type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[]
  269. type ComponentWatchOptions = Record<string, ComponentWatchOptionItem>
  270. type ComponentInjectOptions =
  271. | string[]
  272. | Record<
  273. string | symbol,
  274. string | symbol | { from?: string | symbol; default?: unknown }
  275. >
  276. interface LegacyOptions<
  277. Props,
  278. D,
  279. C extends ComputedOptions,
  280. M extends MethodOptions,
  281. Mixin extends ComponentOptionsMixin,
  282. Extends extends ComponentOptionsMixin
  283. > {
  284. // allow any custom options
  285. [key: string]: any
  286. // state
  287. // Limitation: we cannot expose RawBindings on the `this` context for data
  288. // since that leads to some sort of circular inference and breaks ThisType
  289. // for the entire component.
  290. data?: (
  291. this: CreateComponentPublicInstance<Props>,
  292. vm: CreateComponentPublicInstance<Props>
  293. ) => D
  294. computed?: C
  295. methods?: M
  296. watch?: ComponentWatchOptions
  297. provide?: Data | Function
  298. inject?: ComponentInjectOptions
  299. // composition
  300. mixins?: Mixin[]
  301. extends?: Extends
  302. // lifecycle
  303. beforeCreate?(): void
  304. created?(): void
  305. beforeMount?(): void
  306. mounted?(): void
  307. beforeUpdate?(): void
  308. updated?(): void
  309. activated?(): void
  310. deactivated?(): void
  311. /** @deprecated use `beforeUnmount` instead */
  312. beforeDestroy?(): void
  313. beforeUnmount?(): void
  314. /** @deprecated use `unmounted` instead */
  315. destroyed?(): void
  316. unmounted?(): void
  317. renderTracked?: DebuggerHook
  318. renderTriggered?: DebuggerHook
  319. errorCaptured?: ErrorCapturedHook
  320. // runtime compile only
  321. delimiters?: [string, string]
  322. }
  323. export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M'
  324. export type OptionTypesType<
  325. P = {},
  326. B = {},
  327. D = {},
  328. C extends ComputedOptions = {},
  329. M extends MethodOptions = {}
  330. > = {
  331. P: P
  332. B: B
  333. D: D
  334. C: C
  335. M: M
  336. }
  337. const enum OptionTypes {
  338. PROPS = 'Props',
  339. DATA = 'Data',
  340. COMPUTED = 'Computed',
  341. METHODS = 'Methods',
  342. INJECT = 'Inject'
  343. }
  344. function createDuplicateChecker() {
  345. const cache = Object.create(null)
  346. return (type: OptionTypes, key: string) => {
  347. if (cache[key]) {
  348. warn(`${type} property "${key}" is already defined in ${cache[key]}.`)
  349. } else {
  350. cache[key] = type
  351. }
  352. }
  353. }
  354. type DataFn = (vm: ComponentPublicInstance) => any
  355. export let isInBeforeCreate = false
  356. export function applyOptions(
  357. instance: ComponentInternalInstance,
  358. options: ComponentOptions,
  359. deferredData: DataFn[] = [],
  360. deferredWatch: ComponentWatchOptions[] = [],
  361. asMixin: boolean = false
  362. ) {
  363. const {
  364. // composition
  365. mixins,
  366. extends: extendsOptions,
  367. // state
  368. data: dataOptions,
  369. computed: computedOptions,
  370. methods,
  371. watch: watchOptions,
  372. provide: provideOptions,
  373. inject: injectOptions,
  374. // assets
  375. components,
  376. directives,
  377. // lifecycle
  378. beforeMount,
  379. mounted,
  380. beforeUpdate,
  381. updated,
  382. activated,
  383. deactivated,
  384. beforeDestroy,
  385. beforeUnmount,
  386. destroyed,
  387. unmounted,
  388. render,
  389. renderTracked,
  390. renderTriggered,
  391. errorCaptured
  392. } = options
  393. const publicThis = instance.proxy!
  394. const ctx = instance.ctx
  395. const globalMixins = instance.appContext.mixins
  396. if (asMixin && render && instance.render === NOOP) {
  397. instance.render = render as InternalRenderFunction
  398. }
  399. // applyOptions is called non-as-mixin once per instance
  400. if (!asMixin) {
  401. isInBeforeCreate = true
  402. callSyncHook('beforeCreate', options, publicThis, globalMixins)
  403. isInBeforeCreate = false
  404. // global mixins are applied first
  405. applyMixins(instance, globalMixins, deferredData, deferredWatch)
  406. }
  407. // extending a base component...
  408. if (extendsOptions) {
  409. applyOptions(instance, extendsOptions, deferredData, deferredWatch, true)
  410. }
  411. // local mixins
  412. if (mixins) {
  413. applyMixins(instance, mixins, deferredData, deferredWatch)
  414. }
  415. const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null
  416. if (__DEV__) {
  417. const [propsOptions] = instance.propsOptions
  418. if (propsOptions) {
  419. for (const key in propsOptions) {
  420. checkDuplicateProperties!(OptionTypes.PROPS, key)
  421. }
  422. }
  423. }
  424. // options initialization order (to be consistent with Vue 2):
  425. // - props (already done outside of this function)
  426. // - inject
  427. // - methods
  428. // - data (deferred since it relies on `this` access)
  429. // - computed
  430. // - watch (deferred since it relies on `this` access)
  431. if (injectOptions) {
  432. if (isArray(injectOptions)) {
  433. for (let i = 0; i < injectOptions.length; i++) {
  434. const key = injectOptions[i]
  435. ctx[key] = inject(key)
  436. if (__DEV__) {
  437. checkDuplicateProperties!(OptionTypes.INJECT, key)
  438. }
  439. }
  440. } else {
  441. for (const key in injectOptions) {
  442. const opt = injectOptions[key]
  443. if (isObject(opt)) {
  444. ctx[key] = inject(
  445. opt.from || key,
  446. opt.default,
  447. true /* treat default function as factory */
  448. )
  449. } else {
  450. ctx[key] = inject(opt)
  451. }
  452. if (__DEV__) {
  453. checkDuplicateProperties!(OptionTypes.INJECT, key)
  454. }
  455. }
  456. }
  457. }
  458. if (methods) {
  459. for (const key in methods) {
  460. const methodHandler = (methods as MethodOptions)[key]
  461. if (isFunction(methodHandler)) {
  462. ctx[key] = methodHandler.bind(publicThis)
  463. if (__DEV__) {
  464. checkDuplicateProperties!(OptionTypes.METHODS, key)
  465. }
  466. } else if (__DEV__) {
  467. warn(
  468. `Method "${key}" has type "${typeof methodHandler}" in the component definition. ` +
  469. `Did you reference the function correctly?`
  470. )
  471. }
  472. }
  473. }
  474. if (!asMixin) {
  475. if (deferredData.length) {
  476. deferredData.forEach(dataFn => resolveData(instance, dataFn, publicThis))
  477. }
  478. if (dataOptions) {
  479. resolveData(instance, dataOptions, publicThis)
  480. }
  481. if (__DEV__) {
  482. const rawData = toRaw(instance.data)
  483. for (const key in rawData) {
  484. checkDuplicateProperties!(OptionTypes.DATA, key)
  485. // expose data on ctx during dev
  486. if (key[0] !== '$' && key[0] !== '_') {
  487. Object.defineProperty(ctx, key, {
  488. configurable: true,
  489. enumerable: true,
  490. get: () => rawData[key],
  491. set: NOOP
  492. })
  493. }
  494. }
  495. }
  496. } else if (dataOptions) {
  497. deferredData.push(dataOptions as DataFn)
  498. }
  499. if (computedOptions) {
  500. for (const key in computedOptions) {
  501. const opt = (computedOptions as ComputedOptions)[key]
  502. const get = isFunction(opt)
  503. ? opt.bind(publicThis, publicThis)
  504. : isFunction(opt.get)
  505. ? opt.get.bind(publicThis, publicThis)
  506. : NOOP
  507. if (__DEV__ && get === NOOP) {
  508. warn(`Computed property "${key}" has no getter.`)
  509. }
  510. const set =
  511. !isFunction(opt) && isFunction(opt.set)
  512. ? opt.set.bind(publicThis)
  513. : __DEV__
  514. ? () => {
  515. warn(
  516. `Write operation failed: computed property "${key}" is readonly.`
  517. )
  518. }
  519. : NOOP
  520. const c = computed({
  521. get,
  522. set
  523. })
  524. Object.defineProperty(ctx, key, {
  525. enumerable: true,
  526. configurable: true,
  527. get: () => c.value,
  528. set: v => (c.value = v)
  529. })
  530. if (__DEV__) {
  531. checkDuplicateProperties!(OptionTypes.COMPUTED, key)
  532. }
  533. }
  534. }
  535. if (watchOptions) {
  536. deferredWatch.push(watchOptions)
  537. }
  538. if (!asMixin && deferredWatch.length) {
  539. deferredWatch.forEach(watchOptions => {
  540. for (const key in watchOptions) {
  541. createWatcher(watchOptions[key], ctx, publicThis, key)
  542. }
  543. })
  544. }
  545. if (provideOptions) {
  546. const provides = isFunction(provideOptions)
  547. ? provideOptions.call(publicThis)
  548. : provideOptions
  549. for (const key in provides) {
  550. provide(key, provides[key])
  551. }
  552. }
  553. // asset options.
  554. // To reduce memory usage, only components with mixins or extends will have
  555. // resolved asset registry attached to instance.
  556. if (asMixin) {
  557. if (components) {
  558. extend(
  559. instance.components ||
  560. (instance.components = extend(
  561. {},
  562. (instance.type as ComponentOptions).components
  563. ) as Record<string, ConcreteComponent>),
  564. components
  565. )
  566. }
  567. if (directives) {
  568. extend(
  569. instance.directives ||
  570. (instance.directives = extend(
  571. {},
  572. (instance.type as ComponentOptions).directives
  573. )),
  574. directives
  575. )
  576. }
  577. }
  578. // lifecycle options
  579. if (!asMixin) {
  580. callSyncHook('created', options, publicThis, globalMixins)
  581. }
  582. if (beforeMount) {
  583. onBeforeMount(beforeMount.bind(publicThis))
  584. }
  585. if (mounted) {
  586. onMounted(mounted.bind(publicThis))
  587. }
  588. if (beforeUpdate) {
  589. onBeforeUpdate(beforeUpdate.bind(publicThis))
  590. }
  591. if (updated) {
  592. onUpdated(updated.bind(publicThis))
  593. }
  594. if (activated) {
  595. onActivated(activated.bind(publicThis))
  596. }
  597. if (deactivated) {
  598. onDeactivated(deactivated.bind(publicThis))
  599. }
  600. if (errorCaptured) {
  601. onErrorCaptured(errorCaptured.bind(publicThis))
  602. }
  603. if (renderTracked) {
  604. onRenderTracked(renderTracked.bind(publicThis))
  605. }
  606. if (renderTriggered) {
  607. onRenderTriggered(renderTriggered.bind(publicThis))
  608. }
  609. if (__DEV__ && beforeDestroy) {
  610. warn(`\`beforeDestroy\` has been renamed to \`beforeUnmount\`.`)
  611. }
  612. if (beforeUnmount) {
  613. onBeforeUnmount(beforeUnmount.bind(publicThis))
  614. }
  615. if (__DEV__ && destroyed) {
  616. warn(`\`destroyed\` has been renamed to \`unmounted\`.`)
  617. }
  618. if (unmounted) {
  619. onUnmounted(unmounted.bind(publicThis))
  620. }
  621. }
  622. function callSyncHook(
  623. name: 'beforeCreate' | 'created',
  624. options: ComponentOptions,
  625. ctx: ComponentPublicInstance,
  626. globalMixins: ComponentOptions[]
  627. ) {
  628. callHookFromMixins(name, globalMixins, ctx)
  629. const { extends: base, mixins } = options
  630. if (base) {
  631. callHookFromExtends(name, base, ctx)
  632. }
  633. if (mixins) {
  634. callHookFromMixins(name, mixins, ctx)
  635. }
  636. const selfHook = options[name]
  637. if (selfHook) {
  638. selfHook.call(ctx)
  639. }
  640. }
  641. function callHookFromExtends(
  642. name: 'beforeCreate' | 'created',
  643. base: ComponentOptions,
  644. ctx: ComponentPublicInstance
  645. ) {
  646. if (base.extends) {
  647. callHookFromExtends(name, base.extends, ctx)
  648. }
  649. const baseHook = base[name]
  650. if (baseHook) {
  651. baseHook.call(ctx)
  652. }
  653. }
  654. function callHookFromMixins(
  655. name: 'beforeCreate' | 'created',
  656. mixins: ComponentOptions[],
  657. ctx: ComponentPublicInstance
  658. ) {
  659. for (let i = 0; i < mixins.length; i++) {
  660. const chainedMixins = mixins[i].mixins
  661. if (chainedMixins) {
  662. callHookFromMixins(name, chainedMixins, ctx)
  663. }
  664. const fn = mixins[i][name]
  665. if (fn) {
  666. fn.call(ctx)
  667. }
  668. }
  669. }
  670. function applyMixins(
  671. instance: ComponentInternalInstance,
  672. mixins: ComponentOptions[],
  673. deferredData: DataFn[],
  674. deferredWatch: ComponentWatchOptions[]
  675. ) {
  676. for (let i = 0; i < mixins.length; i++) {
  677. applyOptions(instance, mixins[i], deferredData, deferredWatch, true)
  678. }
  679. }
  680. function resolveData(
  681. instance: ComponentInternalInstance,
  682. dataFn: DataFn,
  683. publicThis: ComponentPublicInstance
  684. ) {
  685. if (__DEV__ && !isFunction(dataFn)) {
  686. warn(
  687. `The data option must be a function. ` +
  688. `Plain object usage is no longer supported.`
  689. )
  690. }
  691. const data = dataFn.call(publicThis, publicThis)
  692. if (__DEV__ && isPromise(data)) {
  693. warn(
  694. `data() returned a Promise - note data() cannot be async; If you ` +
  695. `intend to perform data fetching before component renders, use ` +
  696. `async setup() + <Suspense>.`
  697. )
  698. }
  699. if (!isObject(data)) {
  700. __DEV__ && warn(`data() should return an object.`)
  701. } else if (instance.data === EMPTY_OBJ) {
  702. instance.data = reactive(data)
  703. } else {
  704. // existing data: this is a mixin or extends.
  705. extend(instance.data, data)
  706. }
  707. }
  708. function createWatcher(
  709. raw: ComponentWatchOptionItem,
  710. ctx: Data,
  711. publicThis: ComponentPublicInstance,
  712. key: string
  713. ) {
  714. const getter = () => (publicThis as any)[key]
  715. if (isString(raw)) {
  716. const handler = ctx[raw]
  717. if (isFunction(handler)) {
  718. watch(getter, handler as WatchCallback)
  719. } else if (__DEV__) {
  720. warn(`Invalid watch handler specified by key "${raw}"`, handler)
  721. }
  722. } else if (isFunction(raw)) {
  723. watch(getter, raw.bind(publicThis))
  724. } else if (isObject(raw)) {
  725. if (isArray(raw)) {
  726. raw.forEach(r => createWatcher(r, ctx, publicThis, key))
  727. } else {
  728. const handler = isFunction(raw.handler)
  729. ? raw.handler.bind(publicThis)
  730. : (ctx[raw.handler] as WatchCallback)
  731. if (isFunction(handler)) {
  732. watch(getter, handler, raw)
  733. } else if (__DEV__) {
  734. warn(`Invalid watch handler specified by key "${raw.handler}"`, handler)
  735. }
  736. }
  737. } else if (__DEV__) {
  738. warn(`Invalid watch option: "${key}"`)
  739. }
  740. }
  741. export function resolveMergedOptions(
  742. instance: ComponentInternalInstance
  743. ): ComponentOptions {
  744. const raw = instance.type as ComponentOptions
  745. const { __merged, mixins, extends: extendsOptions } = raw
  746. if (__merged) return __merged
  747. const globalMixins = instance.appContext.mixins
  748. if (!globalMixins.length && !mixins && !extendsOptions) return raw
  749. const options = {}
  750. globalMixins.forEach(m => mergeOptions(options, m, instance))
  751. mergeOptions(options, raw, instance)
  752. return (raw.__merged = options)
  753. }
  754. function mergeOptions(to: any, from: any, instance: ComponentInternalInstance) {
  755. const strats = instance.appContext.config.optionMergeStrategies
  756. const { mixins, extends: extendsOptions } = from
  757. extendsOptions && mergeOptions(to, extendsOptions, instance)
  758. mixins &&
  759. mixins.forEach((m: ComponentOptionsMixin) => mergeOptions(to, m, instance))
  760. for (const key in from) {
  761. if (strats && hasOwn(strats, key)) {
  762. to[key] = strats[key](to[key], from[key], instance.proxy, key)
  763. } else {
  764. to[key] = from[key]
  765. }
  766. }
  767. }