, keyof T>
119 |
--------------------------------------------------------------------------------
/src/component/componentProps.ts:
--------------------------------------------------------------------------------
1 | import { Data } from './common'
2 |
3 | export type ComponentPropsOptions =
4 | | ComponentObjectPropsOptions
5 | | string[]
6 |
7 | export type ComponentObjectPropsOptions
= {
8 | [K in keyof P]: Prop
| null
9 | }
10 |
11 | export type Prop = PropOptions | PropType
12 |
13 | type DefaultFactory = () => T | null | undefined
14 |
15 | export interface PropOptions {
16 | type?: PropType | true | null
17 | required?: boolean
18 | default?: D | DefaultFactory | null | undefined | object
19 | validator?(value: unknown): boolean
20 | }
21 |
22 | export type PropType = PropConstructor | PropConstructor[]
23 |
24 | type PropConstructor =
25 | | { new (...args: any[]): T & object }
26 | | { (): T }
27 | | { new (...args: string[]): Function }
28 |
29 | type RequiredKeys = {
30 | [K in keyof T]: T[K] extends
31 | | { required: true }
32 | | { default: any }
33 | | BooleanConstructor
34 | | { type: BooleanConstructor }
35 | ? K
36 | : never
37 | }[keyof T]
38 |
39 | type OptionalKeys = Exclude>
40 |
41 | type ExtractFunctionPropType<
42 | T extends Function,
43 | TArgs extends Array = any[],
44 | TResult = any
45 | > = T extends (...args: TArgs) => TResult ? T : never
46 |
47 | type ExtractCorrectPropType = T extends Function
48 | ? ExtractFunctionPropType
49 | : Exclude
50 |
51 | // prettier-ignore
52 | type InferPropType = T extends null
53 | ? any // null & true would fail to infer
54 | : T extends { type: null | true }
55 | ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
56 | : T extends ObjectConstructor | { type: ObjectConstructor }
57 | ? Record
58 | : T extends BooleanConstructor | { type: BooleanConstructor }
59 | ? boolean
60 | : T extends DateConstructor | { type: DateConstructor}
61 | ? Date
62 | : T extends FunctionConstructor | { type: FunctionConstructor }
63 | ? Function
64 | : T extends Prop
65 | ? unknown extends V
66 | ? D extends null | undefined
67 | ? V
68 | : D
69 | : ExtractCorrectPropType
70 | : T
71 |
72 | export type ExtractPropTypes = {
73 | // use `keyof Pick>` instead of `RequiredKeys` to support IDE features
74 | [K in keyof Pick>]: InferPropType
75 | } & {
76 | // use `keyof Pick>` instead of `OptionalKeys` to support IDE features
77 | [K in keyof Pick>]?: InferPropType
78 | }
79 |
80 | type DefaultKeys = {
81 | [K in keyof T]: T[K] extends
82 | | {
83 | default: any
84 | }
85 | | BooleanConstructor
86 | | { type: BooleanConstructor }
87 | ? T[K] extends {
88 | type: BooleanConstructor
89 | required: true
90 | }
91 | ? never
92 | : K
93 | : never
94 | }[keyof T]
95 |
96 | // extract props which defined with default from prop options
97 | export type ExtractDefaultPropTypes = O extends object
98 | ? // use `keyof Pick>` instead of `DefaultKeys` to support IDE features
99 | { [K in keyof Pick>]: InferPropType }
100 | : {}
101 |
--------------------------------------------------------------------------------
/src/component/componentProxy.ts:
--------------------------------------------------------------------------------
1 | import { ExtractDefaultPropTypes, ExtractPropTypes } from './componentProps'
2 | import {
3 | nextTick,
4 | ShallowUnwrapRef,
5 | UnwrapNestedRefs,
6 | WatchOptions,
7 | WatchStopHandle,
8 | } from '..'
9 | import { Data } from './common'
10 |
11 | import Vue, {
12 | VueConstructor,
13 | ComponentOptions as Vue2ComponentOptions,
14 | } from 'vue'
15 | import {
16 | ComputedOptions,
17 | MethodOptions,
18 | ExtractComputedReturns,
19 | } from './componentOptions'
20 | import {
21 | ComponentInternalInstance,
22 | ComponentRenderEmitFn,
23 | EmitFn,
24 | EmitsOptions,
25 | ObjectEmitsOptions,
26 | Slots,
27 | } from '../runtimeContext'
28 |
29 | type EmitsToProps = T extends string[]
30 | ? {
31 | [K in string & `on${Capitalize}`]?: (...args: any[]) => any
32 | }
33 | : T extends ObjectEmitsOptions
34 | ? {
35 | [K in string &
36 | `on${Capitalize}`]?: K extends `on${infer C}`
37 | ? T[Uncapitalize] extends null
38 | ? (...args: any[]) => any
39 | : (
40 | ...args: T[Uncapitalize] extends (...args: infer P) => any
41 | ? P
42 | : never
43 | ) => any
44 | : never
45 | }
46 | : {}
47 |
48 | export type ComponentInstance = InstanceType
49 |
50 | // public properties exposed on the proxy, which is used as the render context
51 | // in templates (as `this` in the render option)
52 | export type ComponentRenderProxy<
53 | P = {}, // props type extracted from props option
54 | B = {}, // raw bindings returned from setup()
55 | D = {}, // return from data()
56 | C extends ComputedOptions = {},
57 | M extends MethodOptions = {},
58 | Mixin = {},
59 | Extends = {},
60 | Emits extends EmitsOptions = {},
61 | PublicProps = P,
62 | Defaults = {},
63 | MakeDefaultsOptional extends boolean = false
64 | > = {
65 | $data: D
66 | $props: Readonly<
67 | MakeDefaultsOptional extends true
68 | ? Partial & Omit
69 | : P & PublicProps
70 | >
71 | $attrs: Record
72 | $emit: ComponentRenderEmitFn<
73 | Emits,
74 | keyof Emits,
75 | ComponentRenderProxy<
76 | P,
77 | B,
78 | D,
79 | C,
80 | M,
81 | Mixin,
82 | Extends,
83 | Emits,
84 | PublicProps,
85 | Defaults,
86 | MakeDefaultsOptional
87 | >
88 | >
89 | } & Readonly &
90 | ShallowUnwrapRef &
91 | D &
92 | M &
93 | ExtractComputedReturns &
94 | Omit
95 |
96 | // for Vetur and TSX support
97 | type VueConstructorProxy<
98 | PropsOptions,
99 | RawBindings,
100 | Data,
101 | Computed extends ComputedOptions,
102 | Methods extends MethodOptions,
103 | Mixin = {},
104 | Extends = {},
105 | Emits extends EmitsOptions = {},
106 | Props = ExtractPropTypes &
107 | ({} extends Emits ? {} : EmitsToProps)
108 | > = Omit & {
109 | new (...args: any[]): ComponentRenderProxy<
110 | Props,
111 | ShallowUnwrapRef,
112 | Data,
113 | Computed,
114 | Methods,
115 | Mixin,
116 | Extends,
117 | Emits,
118 | Props,
119 | ExtractDefaultPropTypes,
120 | true
121 | >
122 | }
123 |
124 | type DefaultData = object | ((this: V) => object)
125 | type DefaultMethods = { [key: string]: (this: V, ...args: any[]) => any }
126 | type DefaultComputed = { [key: string]: any }
127 |
128 | export type VueProxy<
129 | PropsOptions,
130 | RawBindings,
131 | Data = DefaultData,
132 | Computed extends ComputedOptions = DefaultComputed,
133 | Methods extends MethodOptions = DefaultMethods,
134 | Mixin = {},
135 | Extends = {},
136 | Emits extends EmitsOptions = {}
137 | > = Vue2ComponentOptions<
138 | Vue,
139 | ShallowUnwrapRef & Data,
140 | Methods,
141 | Computed,
142 | PropsOptions,
143 | ExtractPropTypes
144 | > &
145 | VueConstructorProxy<
146 | PropsOptions,
147 | RawBindings,
148 | Data,
149 | Computed,
150 | Methods,
151 | Mixin,
152 | Extends,
153 | Emits
154 | >
155 |
156 | // public properties exposed on the proxy, which is used as the render context
157 | // in templates (as `this` in the render option)
158 | export type ComponentPublicInstance<
159 | P = {}, // props type extracted from props option
160 | B = {}, // raw bindings returned from setup()
161 | D = {}, // return from data()
162 | C extends ComputedOptions = {},
163 | M extends MethodOptions = {},
164 | E extends EmitsOptions = {},
165 | PublicProps = P,
166 | Defaults = {},
167 | MakeDefaultsOptional extends boolean = false
168 | > = {
169 | $: ComponentInternalInstance
170 | $data: D
171 | $props: MakeDefaultsOptional extends true
172 | ? Partial & Omit
173 | : P & PublicProps
174 | $attrs: Data
175 | $refs: Data
176 | $slots: Slots
177 | $root: ComponentPublicInstance | null
178 | $parent: ComponentPublicInstance | null
179 | $emit: EmitFn
180 | $el: any
181 | // $options: Options & MergedComponentOptionsOverride
182 | $forceUpdate: () => void
183 | $nextTick: typeof nextTick
184 | $watch(
185 | source: string | Function,
186 | cb: Function,
187 | options?: WatchOptions
188 | ): WatchStopHandle
189 | } & P &
190 | ShallowUnwrapRef &
191 | UnwrapNestedRefs &
192 | ExtractComputedReturns &
193 | M
194 |
--------------------------------------------------------------------------------
/src/component/defineAsyncComponent.ts:
--------------------------------------------------------------------------------
1 | import { isFunction, isObject, warn } from '../utils'
2 | import { VueProxy } from './componentProxy'
3 | import { AsyncComponent } from 'vue'
4 |
5 | import {
6 | ComponentOptionsWithoutProps,
7 | ComponentOptionsWithArrayProps,
8 | ComponentOptionsWithProps,
9 | } from './componentOptions'
10 |
11 | type Component = VueProxy
12 |
13 | type ComponentOrComponentOptions =
14 | // Component
15 | | Component
16 | // ComponentOptions
17 | | ComponentOptionsWithoutProps
18 | | ComponentOptionsWithArrayProps
19 | | ComponentOptionsWithProps
20 |
21 | export type AsyncComponentResolveResult =
22 | | T
23 | | { default: T } // es modules
24 |
25 | export type AsyncComponentLoader = () => Promise
26 |
27 | export interface AsyncComponentOptions {
28 | loader: AsyncComponentLoader
29 | loadingComponent?: ComponentOrComponentOptions
30 | errorComponent?: ComponentOrComponentOptions
31 | delay?: number
32 | timeout?: number
33 | suspensible?: boolean
34 | onError?: (
35 | error: Error,
36 | retry: () => void,
37 | fail: () => void,
38 | attempts: number
39 | ) => any
40 | }
41 |
42 | export function defineAsyncComponent(
43 | source: AsyncComponentLoader | AsyncComponentOptions
44 | ): AsyncComponent {
45 | if (isFunction(source)) {
46 | source = { loader: source }
47 | }
48 |
49 | const {
50 | loader,
51 | loadingComponent,
52 | errorComponent,
53 | delay = 200,
54 | timeout, // undefined = never times out
55 | suspensible = false, // in Vue 3 default is true
56 | onError: userOnError,
57 | } = source
58 |
59 | if (__DEV__ && suspensible) {
60 | warn(
61 | `The suspensiblbe option for async components is not supported in Vue2. It is ignored.`
62 | )
63 | }
64 |
65 | let pendingRequest: Promise | null = null
66 |
67 | let retries = 0
68 | const retry = () => {
69 | retries++
70 | pendingRequest = null
71 | return load()
72 | }
73 |
74 | const load = (): Promise => {
75 | let thisRequest: Promise
76 | return (
77 | pendingRequest ||
78 | (thisRequest = pendingRequest =
79 | loader()
80 | .catch((err) => {
81 | err = err instanceof Error ? err : new Error(String(err))
82 | if (userOnError) {
83 | return new Promise((resolve, reject) => {
84 | const userRetry = () => resolve(retry())
85 | const userFail = () => reject(err)
86 | userOnError(err, userRetry, userFail, retries + 1)
87 | })
88 | } else {
89 | throw err
90 | }
91 | })
92 | .then((comp: any) => {
93 | if (thisRequest !== pendingRequest && pendingRequest) {
94 | return pendingRequest
95 | }
96 | if (__DEV__ && !comp) {
97 | warn(
98 | `Async component loader resolved to undefined. ` +
99 | `If you are using retry(), make sure to return its return value.`
100 | )
101 | }
102 | // interop module default
103 | if (
104 | comp &&
105 | (comp.__esModule || comp[Symbol.toStringTag] === 'Module')
106 | ) {
107 | comp = comp.default
108 | }
109 | if (__DEV__ && comp && !isObject(comp) && !isFunction(comp)) {
110 | throw new Error(`Invalid async component load result: ${comp}`)
111 | }
112 | return comp
113 | }))
114 | )
115 | }
116 |
117 | return () => {
118 | const component = load()
119 |
120 | return {
121 | component,
122 | delay,
123 | timeout,
124 | error: errorComponent,
125 | loading: loadingComponent,
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/component/defineComponent.ts:
--------------------------------------------------------------------------------
1 | import { ComponentPropsOptions } from './componentProps'
2 | import {
3 | MethodOptions,
4 | ComputedOptions,
5 | ComponentOptionsWithoutProps,
6 | ComponentOptionsWithArrayProps,
7 | ComponentOptionsWithProps,
8 | } from './componentOptions'
9 | import { VueProxy } from './componentProxy'
10 | import { Data } from './common'
11 | import { HasDefined } from '../types/basic'
12 | import { EmitsOptions } from '../runtimeContext'
13 |
14 | /**
15 | * overload 1: object format with no props
16 | */
17 | export function defineComponent<
18 | RawBindings,
19 | D = Data,
20 | C extends ComputedOptions = {},
21 | M extends MethodOptions = {},
22 | Mixin = {},
23 | Extends = {},
24 | Emits extends EmitsOptions = {}
25 | >(
26 | options: ComponentOptionsWithoutProps<
27 | {},
28 | RawBindings,
29 | D,
30 | C,
31 | M,
32 | Mixin,
33 | Extends,
34 | Emits
35 | >
36 | ): VueProxy<{}, RawBindings, D, C, M, Mixin, Extends, Emits>
37 | /**
38 | * overload 2: object format with array props declaration
39 | * props inferred as `{ [key in PropNames]?: any }`
40 | *
41 | * return type is for Vetur and TSX support
42 | */
43 | export function defineComponent<
44 | PropNames extends string,
45 | RawBindings = Data,
46 | D = Data,
47 | C extends ComputedOptions = {},
48 | M extends MethodOptions = {},
49 | Mixin = {},
50 | Extends = {},
51 | Emits extends EmitsOptions = {},
52 | PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
53 | >(
54 | options: ComponentOptionsWithArrayProps<
55 | PropNames,
56 | RawBindings,
57 | D,
58 | C,
59 | M,
60 | Mixin,
61 | Extends,
62 | Emits
63 | >
64 | ): VueProxy<
65 | Readonly<{ [key in PropNames]?: any }>,
66 | RawBindings,
67 | D,
68 | C,
69 | M,
70 | Mixin,
71 | Extends,
72 | Emits
73 | >
74 |
75 | /**
76 | * overload 3: object format with object props declaration
77 | *
78 | * see `ExtractPropTypes` in './componentProps.ts'
79 | */
80 | export function defineComponent<
81 | Props,
82 | RawBindings = Data,
83 | D = Data,
84 | C extends ComputedOptions = {},
85 | M extends MethodOptions = {},
86 | Mixin = {},
87 | Extends = {},
88 | Emits extends EmitsOptions = {},
89 | PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
90 | >(
91 | options: HasDefined extends true
92 | ? ComponentOptionsWithProps<
93 | PropsOptions,
94 | RawBindings,
95 | D,
96 | C,
97 | M,
98 | Mixin,
99 | Extends,
100 | Emits,
101 | Props
102 | >
103 | : ComponentOptionsWithProps<
104 | PropsOptions,
105 | RawBindings,
106 | D,
107 | C,
108 | M,
109 | Mixin,
110 | Extends,
111 | Emits
112 | >
113 | ): VueProxy
114 |
115 | // implementation, close to no-op
116 | export function defineComponent(options: any) {
117 | return options as any
118 | }
119 |
--------------------------------------------------------------------------------
/src/component/directives.ts:
--------------------------------------------------------------------------------
1 | import type { VNodeDirective, VNode } from 'vue'
2 |
3 | export type DirectiveModifiers = Record
4 |
5 | export interface DirectiveBinding extends Readonly {
6 | readonly modifiers: DirectiveModifiers
7 | readonly value: V
8 | readonly oldValue: V | null
9 | }
10 |
11 | export type DirectiveHook = (
12 | el: T,
13 | binding: DirectiveBinding,
14 | vnode: VNode,
15 | prevVNode: Prev
16 | ) => void
17 |
18 | export interface ObjectDirective {
19 | bind?: DirectiveHook
20 | inserted?: DirectiveHook
21 | update?: DirectiveHook
22 | componentUpdated?: DirectiveHook
23 | unbind?: DirectiveHook
24 | }
25 | export type FunctionDirective = DirectiveHook
26 |
27 | export type Directive =
28 | | ObjectDirective
29 | | FunctionDirective
30 |
--------------------------------------------------------------------------------
/src/component/index.ts:
--------------------------------------------------------------------------------
1 | export { defineComponent } from './defineComponent'
2 | export { defineAsyncComponent } from './defineAsyncComponent'
3 | export {
4 | SetupFunction,
5 | ComputedOptions,
6 | MethodOptions,
7 | ComponentPropsOptions,
8 | } from './componentOptions'
9 | export {
10 | ComponentInstance,
11 | ComponentPublicInstance,
12 | ComponentRenderProxy,
13 | } from './componentProxy'
14 | export { Data } from './common'
15 | export {
16 | PropType,
17 | PropOptions,
18 | ExtractPropTypes,
19 | ExtractDefaultPropTypes,
20 | } from './componentProps'
21 |
22 | export {
23 | DirectiveModifiers,
24 | DirectiveBinding,
25 | DirectiveHook,
26 | ObjectDirective,
27 | FunctionDirective,
28 | Directive,
29 | } from './directives'
30 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | import { VueConstructor } from 'vue'
2 | import { VfaState } from './utils/vmStateManager'
3 | import { VueWatcher } from './apis/watch'
4 |
5 | declare global {
6 | interface Window {
7 | Vue: VueConstructor
8 | }
9 | }
10 |
11 | declare module 'vue/types/vue' {
12 | interface Vue {
13 | readonly _uid: number
14 | readonly _data: Record
15 | _watchers: VueWatcher[]
16 | _provided: Record
17 | __composition_api_state__?: VfaState
18 | }
19 |
20 | interface VueConstructor {
21 | observable(x: any): T
22 | util: {
23 | warn(msg: string, vm?: Vue | null)
24 | defineReactive(
25 | obj: Object,
26 | key: string,
27 | val: any,
28 | customSetter?: Function,
29 | shallow?: boolean
30 | )
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/global.d.ts:
--------------------------------------------------------------------------------
1 | // Global compile-time constants
2 | declare const __DEV__: boolean
3 | declare const __VERSION__: string
4 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import type Vue from 'vue'
2 | import { Data, SetupFunction } from './component'
3 | import { Plugin } from './install'
4 |
5 | export const version = __VERSION__
6 |
7 | export * from './apis'
8 | export * from './component'
9 | export {
10 | getCurrentInstance,
11 | ComponentInternalInstance,
12 | SetupContext,
13 | } from './runtimeContext'
14 |
15 | export default Plugin
16 |
17 | declare module 'vue/types/options' {
18 | interface ComponentOptions {
19 | setup?: SetupFunction
20 | }
21 | }
22 |
23 | // auto install when using CDN
24 | if (typeof window !== 'undefined' && window.Vue) {
25 | window.Vue.use(Plugin)
26 | }
27 |
--------------------------------------------------------------------------------
/src/install.ts:
--------------------------------------------------------------------------------
1 | import type { VueConstructor } from 'vue'
2 | import { AnyObject } from './types/basic'
3 | import { isFunction, hasSymbol, hasOwn, isPlainObject, warn } from './utils'
4 | import { isRef } from './reactivity'
5 | import { setVueConstructor, isVueRegistered } from './runtimeContext'
6 | import { mixin } from './mixin'
7 |
8 | /**
9 | * Helper that recursively merges two data objects together.
10 | */
11 | function mergeData(from: AnyObject, to: AnyObject): Object {
12 | if (!from) return to
13 | if (!to) return from
14 |
15 | let key: any
16 | let toVal: any
17 | let fromVal: any
18 |
19 | const keys = hasSymbol ? Reflect.ownKeys(from) : Object.keys(from)
20 |
21 | for (let i = 0; i < keys.length; i++) {
22 | key = keys[i]
23 | // in case the object is already observed...
24 | if (key === '__ob__') continue
25 | toVal = to[key]
26 | fromVal = from[key]
27 | if (!hasOwn(to, key)) {
28 | to[key] = fromVal
29 | } else if (
30 | toVal !== fromVal &&
31 | isPlainObject(toVal) &&
32 | !isRef(toVal) &&
33 | isPlainObject(fromVal) &&
34 | !isRef(fromVal)
35 | ) {
36 | mergeData(fromVal, toVal)
37 | }
38 | }
39 | return to
40 | }
41 |
42 | export function install(Vue: VueConstructor) {
43 | if (isVueRegistered(Vue)) {
44 | if (__DEV__) {
45 | warn(
46 | '[vue-composition-api] already installed. Vue.use(VueCompositionAPI) should be called only once.'
47 | )
48 | }
49 | return
50 | }
51 |
52 | if (__DEV__) {
53 | if (Vue.version) {
54 | if (Vue.version[0] !== '2' || Vue.version[1] !== '.') {
55 | warn(
56 | `[vue-composition-api] only works with Vue 2, v${Vue.version} found.`
57 | )
58 | }
59 | } else {
60 | warn('[vue-composition-api] no Vue version found')
61 | }
62 | }
63 |
64 | Vue.config.optionMergeStrategies.setup = function (
65 | parent: Function,
66 | child: Function
67 | ) {
68 | return function mergedSetupFn(props: any, context: any) {
69 | return mergeData(
70 | isFunction(parent) ? parent(props, context) || {} : undefined,
71 | isFunction(child) ? child(props, context) || {} : undefined
72 | )
73 | }
74 | }
75 |
76 | setVueConstructor(Vue)
77 | mixin(Vue)
78 | }
79 |
80 | export const Plugin = {
81 | install: (Vue: VueConstructor) => install(Vue),
82 | }
83 |
--------------------------------------------------------------------------------
/src/mixin.ts:
--------------------------------------------------------------------------------
1 | import type { VueConstructor } from 'vue'
2 | import { ComponentInstance, SetupFunction, Data } from './component'
3 | import { isRef, isReactive, toRefs, isRaw } from './reactivity'
4 | import {
5 | isPlainObject,
6 | assert,
7 | proxy,
8 | warn,
9 | isFunction,
10 | isObject,
11 | def,
12 | isArray,
13 | } from './utils'
14 | import { ref } from './apis'
15 | import vmStateManager from './utils/vmStateManager'
16 | import {
17 | afterRender,
18 | activateCurrentInstance,
19 | resolveScopedSlots,
20 | asVmProperty,
21 | updateVmAttrs,
22 | } from './utils/instance'
23 | import {
24 | getVueConstructor,
25 | SetupContext,
26 | toVue3ComponentInstance,
27 | } from './runtimeContext'
28 | import { createObserver } from './reactivity/reactive'
29 |
30 | export function mixin(Vue: VueConstructor) {
31 | Vue.mixin({
32 | beforeCreate: functionApiInit,
33 | mounted(this: ComponentInstance) {
34 | afterRender(this)
35 | },
36 | beforeUpdate() {
37 | updateVmAttrs(this as ComponentInstance)
38 | },
39 | updated(this: ComponentInstance) {
40 | afterRender(this)
41 | },
42 | })
43 |
44 | /**
45 | * Vuex init hook, injected into each instances init hooks list.
46 | */
47 |
48 | function functionApiInit(this: ComponentInstance) {
49 | const vm = this
50 | const $options = vm.$options
51 | const { setup, render } = $options
52 |
53 | if (render) {
54 | // keep currentInstance accessible for createElement
55 | $options.render = function (...args: any): any {
56 | return activateCurrentInstance(toVue3ComponentInstance(vm), () =>
57 | render.apply(this, args)
58 | )
59 | }
60 | }
61 |
62 | if (!setup) {
63 | return
64 | }
65 | if (!isFunction(setup)) {
66 | if (__DEV__) {
67 | warn(
68 | 'The "setup" option should be a function that returns a object in component definitions.',
69 | vm
70 | )
71 | }
72 | return
73 | }
74 |
75 | const { data } = $options
76 | // wrapper the data option, so we can invoke setup before data get resolved
77 | $options.data = function wrappedData() {
78 | initSetup(vm, vm.$props)
79 | return isFunction(data)
80 | ? (
81 | data as (this: ComponentInstance, x: ComponentInstance) => object
82 | ).call(vm, vm)
83 | : data || {}
84 | }
85 | }
86 |
87 | function initSetup(vm: ComponentInstance, props: Record = {}) {
88 | const setup = vm.$options.setup!
89 | const ctx = createSetupContext(vm)
90 | const instance = toVue3ComponentInstance(vm)
91 | instance.setupContext = ctx
92 |
93 | // fake reactive for `toRefs(props)`
94 | def(props, '__ob__', createObserver())
95 |
96 | // resolve scopedSlots and slots to functions
97 | resolveScopedSlots(vm, ctx.slots)
98 |
99 | let binding: ReturnType> | undefined | null
100 | activateCurrentInstance(instance, () => {
101 | // make props to be fake reactive, this is for `toRefs(props)`
102 | binding = setup(props, ctx)
103 | })
104 |
105 | if (!binding) return
106 | if (isFunction(binding)) {
107 | // keep typescript happy with the binding type.
108 | const bindingFunc = binding
109 | // keep currentInstance accessible for createElement
110 | vm.$options.render = () => {
111 | resolveScopedSlots(vm, ctx.slots)
112 | return activateCurrentInstance(instance, () => bindingFunc())
113 | }
114 | return
115 | } else if (isObject(binding)) {
116 | if (isReactive(binding)) {
117 | binding = toRefs(binding) as Data
118 | }
119 |
120 | vmStateManager.set(vm, 'rawBindings', binding)
121 | const bindingObj = binding
122 |
123 | Object.keys(bindingObj).forEach((name) => {
124 | let bindingValue: any = bindingObj[name]
125 |
126 | if (!isRef(bindingValue)) {
127 | if (!isReactive(bindingValue)) {
128 | if (isFunction(bindingValue)) {
129 | const copy = bindingValue
130 | bindingValue = bindingValue.bind(vm)
131 | Object.keys(copy).forEach(function (ele) {
132 | bindingValue[ele] = copy[ele]
133 | })
134 | } else if (!isObject(bindingValue)) {
135 | bindingValue = ref(bindingValue)
136 | } else if (hasReactiveArrayChild(bindingValue)) {
137 | // creates a custom reactive properties without make the object explicitly reactive
138 | // NOTE we should try to avoid this, better implementation needed
139 | customReactive(bindingValue)
140 | }
141 | } else if (isArray(bindingValue)) {
142 | bindingValue = ref(bindingValue)
143 | }
144 | }
145 | asVmProperty(vm, name, bindingValue)
146 | })
147 |
148 | return
149 | }
150 |
151 | if (__DEV__) {
152 | assert(
153 | false,
154 | `"setup" must return a "Object" or a "Function", got "${Object.prototype.toString
155 | .call(binding)
156 | .slice(8, -1)}"`
157 | )
158 | }
159 | }
160 |
161 | function customReactive(target: object, seen = new Set()) {
162 | if (seen.has(target)) return
163 | if (
164 | !isPlainObject(target) ||
165 | isRef(target) ||
166 | isReactive(target) ||
167 | isRaw(target)
168 | )
169 | return
170 | const Vue = getVueConstructor()
171 | // @ts-expect-error https://github.com/vuejs/vue/pull/12132
172 | const defineReactive = Vue.util.defineReactive
173 |
174 | Object.keys(target).forEach((k) => {
175 | const val = target[k]
176 | defineReactive(target, k, val)
177 | if (val) {
178 | seen.add(val)
179 | customReactive(val, seen)
180 | }
181 | return
182 | })
183 | }
184 |
185 | function hasReactiveArrayChild(target: object, visited = new Map()): boolean {
186 | if (visited.has(target)) {
187 | return visited.get(target)
188 | }
189 | visited.set(target, false)
190 | if (isArray(target) && isReactive(target)) {
191 | visited.set(target, true)
192 | return true
193 | }
194 |
195 | if (!isPlainObject(target) || isRaw(target) || isRef(target)) {
196 | return false
197 | }
198 | return Object.keys(target).some((x) =>
199 | hasReactiveArrayChild(target[x], visited)
200 | )
201 | }
202 |
203 | function createSetupContext(
204 | vm: ComponentInstance & { [x: string]: any }
205 | ): SetupContext {
206 | const ctx = { slots: {} } as SetupContext
207 |
208 | const propsPlain = [
209 | 'root',
210 | 'parent',
211 | 'refs',
212 | 'listeners',
213 | 'isServer',
214 | 'ssrContext',
215 | ]
216 | const methodReturnVoid = ['emit']
217 |
218 | propsPlain.forEach((key) => {
219 | let srcKey = `$${key}`
220 | proxy(ctx, key, {
221 | get: () => vm[srcKey],
222 | set() {
223 | __DEV__ &&
224 | warn(
225 | `Cannot assign to '${key}' because it is a read-only property`,
226 | vm
227 | )
228 | },
229 | })
230 | })
231 |
232 | updateVmAttrs(vm, ctx)
233 |
234 | methodReturnVoid.forEach((key) => {
235 | const srcKey = `$${key}`
236 | proxy(ctx, key, {
237 | get() {
238 | return (...args: any[]) => {
239 | const fn: Function = vm[srcKey]
240 | fn.apply(vm, args)
241 | }
242 | },
243 | })
244 | })
245 | if (process.env.NODE_ENV === 'test') {
246 | ;(ctx as any)._vm = vm
247 | }
248 | return ctx
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/src/reactivity/del.ts:
--------------------------------------------------------------------------------
1 | import { AnyObject } from '../types/basic'
2 | import { getVueConstructor } from '../runtimeContext'
3 | import {
4 | hasOwn,
5 | isPrimitive,
6 | isUndef,
7 | isArray,
8 | isValidArrayIndex,
9 | } from '../utils'
10 |
11 | /**
12 | * Delete a property and trigger change if necessary.
13 | */
14 | export function del(target: AnyObject, key: any) {
15 | const Vue = getVueConstructor()
16 | const { warn } = Vue.util
17 |
18 | if (__DEV__ && (isUndef(target) || isPrimitive(target))) {
19 | warn(
20 | `Cannot delete reactive property on undefined, null, or primitive value: ${target}`
21 | )
22 | }
23 | if (isArray(target) && isValidArrayIndex(key)) {
24 | target.splice(key, 1)
25 | return
26 | }
27 | const ob = target.__ob__
28 | if (target._isVue || (ob && ob.vmCount)) {
29 | __DEV__ &&
30 | warn(
31 | 'Avoid deleting properties on a Vue instance or its root $data ' +
32 | '- just set it to null.'
33 | )
34 | return
35 | }
36 | if (!hasOwn(target, key)) {
37 | return
38 | }
39 | delete target[key]
40 | if (!ob) {
41 | return
42 | }
43 | ob.dep.notify()
44 | }
45 |
--------------------------------------------------------------------------------
/src/reactivity/force.ts:
--------------------------------------------------------------------------------
1 | let _isForceTrigger = false
2 |
3 | export function isForceTrigger() {
4 | return _isForceTrigger
5 | }
6 |
7 | export function setForceTrigger(v: boolean) {
8 | _isForceTrigger = v
9 | }
10 |
--------------------------------------------------------------------------------
/src/reactivity/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | reactive,
3 | isReactive,
4 | markRaw,
5 | shallowReactive,
6 | toRaw,
7 | isRaw,
8 | } from './reactive'
9 | export {
10 | ref,
11 | customRef,
12 | isRef,
13 | createRef,
14 | toRefs,
15 | toRef,
16 | unref,
17 | shallowRef,
18 | triggerRef,
19 | proxyRefs,
20 | } from './ref'
21 | export { readonly, isReadonly, shallowReadonly } from './readonly'
22 | export { set } from './set'
23 | export { del } from './del'
24 |
25 | export type {
26 | Ref,
27 | ComputedRef,
28 | WritableComputedRef,
29 | ToRefs,
30 | UnwrapRef,
31 | UnwrapRefSimple,
32 | ShallowUnwrapRef,
33 | } from './ref'
34 | export type { DeepReadonly, UnwrapNestedRefs } from './readonly'
35 |
--------------------------------------------------------------------------------
/src/reactivity/reactive.ts:
--------------------------------------------------------------------------------
1 | import { AnyObject } from '../types/basic'
2 | import { getRegisteredVueOrDefault } from '../runtimeContext'
3 | import {
4 | isPlainObject,
5 | def,
6 | warn,
7 | isArray,
8 | hasOwn,
9 | noopFn,
10 | isObject,
11 | proxy,
12 | } from '../utils'
13 | import { isComponentInstance, defineComponentInstance } from '../utils/helper'
14 | import { RefKey } from '../utils/symbols'
15 | import { isRef, UnwrapRef } from './ref'
16 | import { rawSet, accessModifiedSet } from '../utils/sets'
17 | import { isForceTrigger } from './force'
18 |
19 | const SKIPFLAG = '__v_skip'
20 |
21 | export function isRaw(obj: any): boolean {
22 | return Boolean(
23 | obj &&
24 | hasOwn(obj, '__ob__') &&
25 | typeof obj.__ob__ === 'object' &&
26 | obj.__ob__?.[SKIPFLAG]
27 | )
28 | }
29 |
30 | export function isReactive(obj: any): boolean {
31 | return Boolean(
32 | obj &&
33 | hasOwn(obj, '__ob__') &&
34 | typeof obj.__ob__ === 'object' &&
35 | !obj.__ob__?.[SKIPFLAG]
36 | )
37 | }
38 |
39 | /**
40 | * Proxing property access of target.
41 | * We can do unwrapping and other things here.
42 | */
43 | function setupAccessControl(target: AnyObject): void {
44 | if (
45 | !isPlainObject(target) ||
46 | isRaw(target) ||
47 | isArray(target) ||
48 | isRef(target) ||
49 | isComponentInstance(target) ||
50 | accessModifiedSet.has(target)
51 | )
52 | return
53 |
54 | accessModifiedSet.set(target, true)
55 |
56 | const keys = Object.keys(target)
57 | for (let i = 0; i < keys.length; i++) {
58 | defineAccessControl(target, keys[i])
59 | }
60 | }
61 |
62 | /**
63 | * Auto unwrapping when access property
64 | */
65 | export function defineAccessControl(target: AnyObject, key: any, val?: any) {
66 | if (key === '__ob__') return
67 | if (isRaw(target[key])) return
68 |
69 | let getter: (() => any) | undefined
70 | let setter: ((x: any) => void) | undefined
71 | const property = Object.getOwnPropertyDescriptor(target, key)
72 | if (property) {
73 | if (property.configurable === false) {
74 | return
75 | }
76 | getter = property.get
77 | setter = property.set
78 | if (
79 | (!getter || setter) /* not only have getter */ &&
80 | arguments.length === 2
81 | ) {
82 | val = target[key]
83 | }
84 | }
85 |
86 | setupAccessControl(val)
87 | proxy(target, key, {
88 | get: function getterHandler() {
89 | const value = getter ? getter.call(target) : val
90 | // if the key is equal to RefKey, skip the unwrap logic
91 | if (key !== RefKey && isRef(value)) {
92 | return value.value
93 | } else {
94 | return value
95 | }
96 | },
97 | set: function setterHandler(newVal: any) {
98 | if (getter && !setter) return
99 |
100 | // If the key is equal to RefKey, skip the unwrap logic
101 | // If and only if "value" is ref and "newVal" is not a ref,
102 | // the assignment should be proxied to "value" ref.
103 | if (key !== RefKey && isRef(val) && !isRef(newVal)) {
104 | val.value = newVal
105 | } else if (setter) {
106 | setter.call(target, newVal)
107 | val = newVal
108 | } else {
109 | val = newVal
110 | }
111 | setupAccessControl(newVal)
112 | },
113 | })
114 | }
115 |
116 | export function observe(obj: T): T {
117 | const Vue = getRegisteredVueOrDefault()
118 | let observed: T
119 | if (Vue.observable) {
120 | observed = Vue.observable(obj)
121 | } else {
122 | const vm = defineComponentInstance(Vue, {
123 | data: {
124 | $$state: obj,
125 | },
126 | })
127 | observed = vm._data.$$state
128 | }
129 |
130 | // in SSR, there is no __ob__. Mock for reactivity check
131 | if (!hasOwn(observed, '__ob__')) {
132 | mockReactivityDeep(observed)
133 | }
134 |
135 | return observed
136 | }
137 |
138 | /**
139 | * Mock __ob__ for object recursively
140 | */
141 | export function mockReactivityDeep(obj: any, seen = new Set()) {
142 | if (seen.has(obj) || hasOwn(obj, '__ob__') || !Object.isExtensible(obj))
143 | return
144 |
145 | def(obj, '__ob__', mockObserver(obj))
146 | seen.add(obj)
147 |
148 | for (const key of Object.keys(obj)) {
149 | const value = obj[key]
150 | if (
151 | !(isPlainObject(value) || isArray(value)) ||
152 | isRaw(value) ||
153 | !Object.isExtensible(value)
154 | ) {
155 | continue
156 | }
157 | mockReactivityDeep(value, seen)
158 | }
159 | }
160 |
161 | function mockObserver(value: any = {}): any {
162 | return {
163 | value,
164 | dep: {
165 | notify: noopFn,
166 | depend: noopFn,
167 | addSub: noopFn,
168 | removeSub: noopFn,
169 | },
170 | }
171 | }
172 |
173 | export function createObserver() {
174 | return observe({}).__ob__
175 | }
176 |
177 | export function shallowReactive(obj: T): T
178 | export function shallowReactive(obj: any) {
179 | if (!isObject(obj)) {
180 | if (__DEV__) {
181 | warn('"shallowReactive()" must be called on an object.')
182 | }
183 | return obj
184 | }
185 |
186 | if (
187 | !(isPlainObject(obj) || isArray(obj)) ||
188 | isRaw(obj) ||
189 | !Object.isExtensible(obj)
190 | ) {
191 | return obj
192 | }
193 |
194 | const observed = observe(isArray(obj) ? [] : {})
195 |
196 | const ob = (observed as any).__ob__
197 |
198 | for (const key of Object.keys(obj)) {
199 | let val = obj[key]
200 | let getter: (() => any) | undefined
201 | let setter: ((x: any) => void) | undefined
202 | const property = Object.getOwnPropertyDescriptor(obj, key)
203 | if (property) {
204 | if (property.configurable === false) {
205 | continue
206 | }
207 | getter = property.get
208 | setter = property.set
209 | }
210 |
211 | proxy(observed, key, {
212 | get: function getterHandler() {
213 | ob.dep?.depend()
214 | return val
215 | },
216 | set: function setterHandler(newVal: any) {
217 | if (getter && !setter) return
218 |
219 | if (!isForceTrigger() && val === newVal) return
220 | if (setter) {
221 | setter.call(obj, newVal)
222 | } else {
223 | val = newVal
224 | }
225 | ob.dep?.notify()
226 | },
227 | })
228 | }
229 | return observed
230 | }
231 |
232 | /**
233 | * Make obj reactivity
234 | */
235 | export function reactive(obj: T): UnwrapRef {
236 | if (!isObject(obj)) {
237 | if (__DEV__) {
238 | warn('"reactive()" must be called on an object.')
239 | }
240 | return obj
241 | }
242 |
243 | if (
244 | !(isPlainObject(obj) || isArray(obj)) ||
245 | isRaw(obj) ||
246 | !Object.isExtensible(obj)
247 | ) {
248 | return obj as any
249 | }
250 |
251 | const observed = observe(obj)
252 | setupAccessControl(observed)
253 | return observed as UnwrapRef
254 | }
255 |
256 | /**
257 | * Make sure obj can't be a reactive
258 | */
259 | export function markRaw(obj: T): T {
260 | if (!(isPlainObject(obj) || isArray(obj)) || !Object.isExtensible(obj)) {
261 | return obj
262 | }
263 |
264 | // set the vue observable flag at obj
265 | const ob = createObserver()
266 | ob[SKIPFLAG] = true
267 | def(obj, '__ob__', ob)
268 |
269 | // mark as Raw
270 | rawSet.set(obj, true)
271 |
272 | return obj
273 | }
274 |
275 | export function toRaw(observed: T): T {
276 | if (isRaw(observed) || !Object.isExtensible(observed)) {
277 | return observed
278 | }
279 |
280 | return (observed as any)?.__ob__?.value || observed
281 | }
282 |
--------------------------------------------------------------------------------
/src/reactivity/readonly.ts:
--------------------------------------------------------------------------------
1 | import { reactive, Ref, UnwrapRefSimple } from '.'
2 | import { isArray, isPlainObject, isObject, warn, proxy } from '../utils'
3 | import { readonlySet } from '../utils/sets'
4 | import { isReactive, observe } from './reactive'
5 | import { isRef, RefImpl } from './ref'
6 |
7 | export function isReadonly(obj: any): boolean {
8 | return readonlySet.has(obj)
9 | }
10 |
11 | type Primitive = string | number | boolean | bigint | symbol | undefined | null
12 | type Builtin = Primitive | Function | Date | Error | RegExp
13 |
14 | // prettier-ignore
15 | export type DeepReadonly = T extends Builtin
16 | ? T
17 | : T extends Map
18 | ? ReadonlyMap, DeepReadonly>
19 | : T extends ReadonlyMap
20 | ? ReadonlyMap, DeepReadonly>
21 | : T extends WeakMap
22 | ? WeakMap, DeepReadonly>
23 | : T extends Set
24 | ? ReadonlySet>
25 | : T extends ReadonlySet
26 | ? ReadonlySet>
27 | : T extends WeakSet
28 | ? WeakSet>
29 | : T extends Promise
30 | ? Promise>
31 | : T extends {}
32 | ? { readonly [K in keyof T]: DeepReadonly }
33 | : Readonly
34 |
35 | // only unwrap nested ref
36 | export type UnwrapNestedRefs = T extends Ref ? T : UnwrapRefSimple
37 |
38 | /**
39 | * **In @vue/composition-api, `reactive` only provides type-level readonly check**
40 | *
41 | * Creates a readonly copy of the original object. Note the returned copy is not
42 | * made reactive, but `readonly` can be called on an already reactive object.
43 | */
44 | export function readonly(
45 | target: T
46 | ): DeepReadonly> {
47 | if (__DEV__ && !isObject(target)) {
48 | warn(`value cannot be made reactive: ${String(target)}`)
49 | } else {
50 | readonlySet.set(target, true)
51 | }
52 | return target as any
53 | }
54 |
55 | export function shallowReadonly(obj: T): Readonly
56 | export function shallowReadonly(obj: any): any {
57 | if (!isObject(obj)) {
58 | if (__DEV__) {
59 | warn(`value cannot be made reactive: ${String(obj)}`)
60 | }
61 | return obj
62 | }
63 |
64 | if (
65 | !(isPlainObject(obj) || isArray(obj)) ||
66 | (!Object.isExtensible(obj) && !isRef(obj))
67 | ) {
68 | return obj
69 | }
70 |
71 | const readonlyObj = isRef(obj)
72 | ? new RefImpl({} as any)
73 | : isReactive(obj)
74 | ? observe({})
75 | : {}
76 | const source = reactive({})
77 | const ob = (source as any).__ob__
78 |
79 | for (const key of Object.keys(obj)) {
80 | let val = obj[key]
81 | let getter: (() => any) | undefined
82 | const property = Object.getOwnPropertyDescriptor(obj, key)
83 | if (property) {
84 | if (property.configurable === false && !isRef(obj)) {
85 | continue
86 | }
87 | getter = property.get
88 | }
89 |
90 | proxy(readonlyObj, key, {
91 | get: function getterHandler() {
92 | const value = getter ? getter.call(obj) : val
93 | ob.dep.depend()
94 | return value
95 | },
96 | set(v: any) {
97 | if (__DEV__) {
98 | warn(`Set operation on key "${key}" failed: target is readonly.`)
99 | }
100 | },
101 | })
102 | }
103 |
104 | readonlySet.set(readonlyObj, true)
105 |
106 | return readonlyObj
107 | }
108 |
--------------------------------------------------------------------------------
/src/reactivity/ref.ts:
--------------------------------------------------------------------------------
1 | import { RefKey } from '../utils/symbols'
2 | import { proxy, isPlainObject, warn, def } from '../utils'
3 | import { reactive, isReactive, shallowReactive } from './reactive'
4 | import { readonlySet } from '../utils/sets'
5 | import { set } from './set'
6 | import { setForceTrigger } from './force'
7 |
8 | declare const _refBrand: unique symbol
9 | export interface Ref {
10 | readonly [_refBrand]: true
11 | value: T
12 | }
13 |
14 | export interface WritableComputedRef extends Ref {
15 | /**
16 | * `effect` is added to be able to differentiate refs from computed properties.
17 | * **Differently from Vue 3, it's just `true`**. This is because there is no equivalent
18 | * of `ReactiveEffect` in `@vue/composition-api`.
19 | */
20 | effect: true
21 | }
22 |
23 | export interface ComputedRef extends WritableComputedRef {
24 | readonly value: T
25 | }
26 |
27 | export type ToRefs = { [K in keyof T]: Ref }
28 |
29 | export type CollectionTypes = IterableCollections | WeakCollections
30 |
31 | type IterableCollections = Map | Set
32 | type WeakCollections = WeakMap | WeakSet
33 |
34 | // corner case when use narrows type
35 | // Ex. type RelativePath = string & { __brand: unknown }
36 | // RelativePath extends object -> true
37 | type BaseTypes = string | number | boolean | Node | Window | Date
38 |
39 | export type ShallowUnwrapRef = {
40 | [K in keyof T]: T[K] extends Ref ? V : T[K]
41 | }
42 |
43 | export type UnwrapRef = T extends Ref
44 | ? UnwrapRefSimple
45 | : UnwrapRefSimple
46 |
47 | export type UnwrapRefSimple = T extends
48 | | Function
49 | | CollectionTypes
50 | | BaseTypes
51 | | Ref
52 | ? T
53 | : T extends Array
54 | ? { [K in keyof T]: UnwrapRefSimple }
55 | : T extends object
56 | ? {
57 | [P in keyof T]: P extends symbol ? T[P] : UnwrapRef
58 | }
59 | : T
60 |
61 | interface RefOption {
62 | get(): T
63 | set?(x: T): void
64 | }
65 | export class RefImpl implements Ref {
66 | readonly [_refBrand]!: true
67 | public value!: T
68 | constructor({ get, set }: RefOption) {
69 | proxy(this, 'value', {
70 | get,
71 | set,
72 | })
73 | }
74 | }
75 |
76 | export function createRef(
77 | options: RefOption,
78 | isReadonly = false,
79 | isComputed = false
80 | ): RefImpl {
81 | const r = new RefImpl(options)
82 |
83 | // add effect to differentiate refs from computed
84 | if (isComputed) (r as ComputedRef).effect = true
85 |
86 | // seal the ref, this could prevent ref from being observed
87 | // It's safe to seal the ref, since we really shouldn't extend it.
88 | // related issues: #79
89 | const sealed = Object.seal(r)
90 |
91 | if (isReadonly) readonlySet.set(sealed, true)
92 |
93 | return sealed
94 | }
95 |
96 | export function ref(
97 | raw: T
98 | ): T extends Ref ? T : Ref>
99 | export function ref(raw: T): Ref>
100 | export function ref(): Ref
101 | export function ref(raw?: unknown) {
102 | if (isRef(raw)) {
103 | return raw
104 | }
105 |
106 | const value = reactive({ [RefKey]: raw })
107 | return createRef({
108 | get: () => value[RefKey] as any,
109 | set: (v) => ((value[RefKey] as any) = v),
110 | })
111 | }
112 |
113 | export function isRef(value: any): value is Ref {
114 | return value instanceof RefImpl
115 | }
116 |
117 | export function unref(ref: T | Ref): T {
118 | return isRef(ref) ? (ref.value as any) : ref
119 | }
120 |
121 | export function toRefs(obj: T): ToRefs {
122 | if (__DEV__ && !isReactive(obj)) {
123 | warn(`toRefs() expects a reactive object but received a plain one.`)
124 | }
125 | if (!isPlainObject(obj)) return obj
126 |
127 | const ret: any = {}
128 | for (const key in obj) {
129 | ret[key] = toRef(obj, key)
130 | }
131 |
132 | return ret
133 | }
134 |
135 | export type CustomRefFactory = (
136 | track: () => void,
137 | trigger: () => void
138 | ) => {
139 | get: () => T
140 | set: (value: T) => void
141 | }
142 |
143 | export function customRef(factory: CustomRefFactory): Ref {
144 | const version = ref(0)
145 | return createRef(
146 | factory(
147 | () => void version.value,
148 | () => {
149 | ++version.value
150 | }
151 | )
152 | )
153 | }
154 |
155 | export function toRef