What is @vue/composition-api?
@vue/composition-api is a plugin that provides Vue 2.x users with the Composition API, which is a set of APIs that allows you to use Vue 3's reactivity system and component logic composition in Vue 2.x applications.
What are @vue/composition-api's main functionalities?
Reactive State
The `reactive` function is used to create a reactive state object. This allows you to manage state in a reactive manner, similar to Vue 3.
```javascript
import { reactive } from '@vue/composition-api';
export default {
setup() {
const state = reactive({ count: 0 });
return { state };
}
};
```
Computed Properties
The `computed` function is used to create computed properties that automatically update when their dependencies change.
```javascript
import { computed } from '@vue/composition-api';
export default {
setup() {
const state = reactive({ count: 0 });
const doubleCount = computed(() => state.count * 2);
return { state, doubleCount };
}
};
```
Watchers
The `watch` function is used to perform side effects in response to reactive state changes.
```javascript
import { watch } from '@vue/composition-api';
export default {
setup() {
const state = reactive({ count: 0 });
watch(() => state.count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
});
return { state };
}
};
```
Lifecycle Hooks
The `onMounted` and `onUnmounted` functions are used to register lifecycle hooks within the setup function.
```javascript
import { onMounted, onUnmounted } from '@vue/composition-api';
export default {
setup() {
onMounted(() => {
console.log('Component is mounted');
});
onUnmounted(() => {
console.log('Component is unmounted');
});
}
};
```
Other packages similar to @vue/composition-api
vue-function-api
The `vue-function-api` package provides a similar Composition API for Vue 2.x. It allows you to use Vue 3's Composition API features in Vue 2.x applications. However, `@vue/composition-api` is the official plugin provided by the Vue team, making it more reliable and better supported.
vue-hooks
The `vue-hooks` package offers a way to use hooks in Vue components, similar to React hooks. While it provides some similar functionality to the Composition API, it is not as comprehensive or integrated as `@vue/composition-api`.
@vue/composition-api
Vue 2 plugin for Composition API
English | 中文 ・ Composition API Docs
Installation
NPM
npm install @vue/composition-api
yarn add @vue/composition-api
You must install @vue/composition-api
as a plugin via Vue.use()
before you can use the Composition API to compose your component.
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
import { ref, reactive } from '@vue/composition-api'
:bulb: When you migrate to Vue 3, just replacing @vue/composition-api
to vue
and your code should just work.
CDN
Include @vue/composition-api
after Vue and it will install itself automatically.
<script src="https://cdn.jsdelivr.net/npm/vue@2.6"></script>
<script src="https://cdn.jsdelivr.net/npm/@vue/composition-api@1.0.0-beta.10"></script>
@vue/composition-api
will be exposed to global variable window.VueCompositionAPI
.
const { ref, reactive } = VueCompositionAPI
TypeScript Support
TypeScript version >3.5.1 is required
To let TypeScript properly infer types inside Vue component options, you need to define components with defineComponent
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
})
JSX/TSX
To make JSX/TSX work with @vue/composition-api
, check out babel-preset-vca-jsx by @luwanquan.
SSR
Even if there is no definitive Vue 3 API for SSR yet, this plugin implements the onServerPrefetch
lifecycle hook that allows you to use the serverPrefetch
hook found in the classic API.
import { onServerPrefetch } from '@vue/composition-api'
export default {
setup(props, { ssrContext }) {
const result = ref()
onServerPrefetch(async () => {
result.value = await callApi(ssrContext.someId)
})
return {
result,
}
}
}
Limitations
:white_check_mark: Support :x: Not Supported
Ref
Unwrap
Unwrap
is not working with Array index.
❌ Should NOT store ref
as a direct child of Array
const state = reactive({
list: [ref(0)],
})
state.list[0].value === 0
state.list.push(ref(1))
state.list[1].value === 1
❌ Should NOT use ref
in a plain object when working with Array
const a = {
count: ref(0),
}
const b = reactive({
list: [a],
})
b.list[0].count.value === 0
const b = reactive({
list: [
{
count: ref(0),
},
],
})
b.list[0].count.value === 0
✅ Should always use ref
in a reactive
when working with Array
const a = reactive({
list: [
reactive({
count: ref(0),
}),
]
})
a.list[0].count === 0
a.list.push(
reactive({
count: ref(1),
})
)
a.list[1].count === 1
⚠️ `set` workaround for adding new reactive properties
⚠️ Warning: set
does NOT exist in Vue 3. We provide it as a workaround here, due to the limitation of Vue 2.x reactivity system. In Vue 2, you will need to call set
to track new keys on an object
(similar to Vue.set
but for reactive objects
created by the Composition API). In Vue 3, you can just assign them like normal objects.
import { reactive, set } from '@vue/composition-api'
const a = reactive({
foo: 1
})
set(a, 'bar', 1)
Template Refs
✅ String ref && return it from setup()
<template>
<div ref="root"></div>
</template>
<script>
export default {
setup() {
const root = ref(null)
onMounted(() => {
console.log(root.value)
})
return {
root,
}
},
}
</script>
✅ String ref && return it from setup()
&& Render Function / JSX
export default {
setup() {
const root = ref(null)
onMounted(() => {
console.log(root.value)
})
return {
root,
}
},
render() {
return () => <div ref="root" />
},
}
❌ Function ref
<template>
<div :ref="el => root = el"></div>
</template>
<script>
export default {
setup() {
const root = ref(null)
return {
root,
}
},
}
</script>
❌ Render Function / JSX in setup()
export default {
setup() {
const root = ref(null)
return () =>
h('div', {
ref: root,
})
return () => <div ref={root} />
},
}
⚠️ $refs
accessing workaround
:warning: Warning: The SetupContext.refs
won't exist in Vue 3.0
. @vue/composition-api
provide it as a workaround here.
If you really want to use template refs in this case, you can access vm.$refs
via SetupContext.refs
export default {
setup(initProps, setupContext) {
const refs = setupContext.refs
onMounted(() => {
console.log(refs.root)
})
return () =>
h('div', {
ref: 'root',
})
return () => <div ref="root" />
},
}
You may also need to augment the SetupContext
when working with TypeScript:
import Vue from 'vue'
declare module '@vue/composition-api' {
interface SetupContext {
readonly refs: { [key: string]: Vue | Element | Vue[] | Element[] }
}
}
Reactive
⚠️ reactive()
mutates the original object
reactive
uses Vue.observable
underneath which will mutate the original object.
:bulb: In Vue 3, it will return an new proxy object.
Watch
❌ onTrack
and onTrigger
are not available in WatchOptions
watch(() => {
}, {
immediate: true,
onTrack() {},
onTrigger() {},
})
createApp
⚠️ createApp()
is global
In Vue 3, createApp()
is introduced to provide context(plugin, components, etc.) isolation between app instances. Due the the design of Vue 2, in this plugin, we provide createApp()
as a forward compatible API which is just an alias of the global.
const app1 = createApp(RootComponent1)
app1.component('Foo', Foo)
app1.use(VueRouter)
const app2 = createApp(RootComponent2)
app2.component('Bar', Bar)
shallowReadonly
⚠️ shallowReadonly()
will create a new object and with the same root properties, new properties added will not be readonly or reactive.
:bulb: In Vue 3, it will return an new proxy object.
props
⚠️ toRefs(props.foo.bar)
will incorrectly warn when acessing nested levels of props.
⚠️ isReactive(props.foo.bar)
will return false.
defineComponent({
setup(props) {
const { bar } = toRefs(props.foo)
const { foo } = toRefs(props)
const a = foo.value.bar
}
})
Missing APIs
The following APIs introduced in Vue 3 are not available in this plugin.
readonly
defineAsyncComponent
onRenderTracked
onRenderTriggered
isProxy
Reactive APIs in data()
❌ Passing ref
, reactive
or other reactive apis to data()
would not work.
export default {
data() {
return {
a: ref(1),
}
},
}
Performance Impact
Due the the limitation of Vue2's public API. @vue/composition-api
inevitably introduced some extract costs. It shouldn't bother you unless in extreme environments.
You can check the benchmark results for more details.