Vue 2 plugin for Composition API
English | 中文 ・ Composition API Docs
npm install @vue/composition-api
# or
yarn add @vue/composition-apiYou 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)// use the APIs
import { ref, reactive } from '@vue/composition-api'💡 When you migrate to Vue 3, just replacing
@vue/composition-apitovueand your code should just work.
Include @vue/composition-api after Vue and it will install itself automatically.
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script src="https://cdn.jsdelivr.net/npm/@vue/[email protected]"></script>@vue/composition-api will be exposed to global variable window.VueCompositionAPI.
const { ref, reactive } = VueCompositionAPITypeScript 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({
// type inference enabled
})To make JSX/TSX work with @vue/composition-api, check out babel-preset-vca-jsx by @luwanquan.
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,
}
},
};✅ Support ❌ Not Supported
Unwrap is not working with Array index.
❌ Should NOT store ref as a direct child of Array
const state = reactive({
list: [ref(0)],
})
// no unwrap, `.value` is required
state.list[0].value === 0 // true
state.list.push(ref(1))
// no unwrap, `.value` is required
state.list[1].value === 1 // true
❌ Should NOT use ref in a plain object when working with Array
const a = {
count: ref(0),
}
const b = reactive({
list: [a], // `a.count` will not unwrap!!
})
// no unwrap for `count`, `.value` is required
b.list[0].count.value === 0 // trueconst b = reactive({
list: [
{
count: ref(0), // no unwrap!!
},
],
})
// no unwrap for `count`, `.value` is required
b.list[0].count.value === 0 // true
✅ Should always use ref in a reactive when working with Array
const a = reactive({
list: [
reactive({
count: ref(0),
})
],
})
// unwrapped
a.list[0].count === 0 // true
a.list.push(
reactive({
count: ref(1),
})
)
// unwrapped
a.list[1].count === 1 // true
✅ String ref && return it from setup()
<template>
<div ref="root"></div>
</template>
<script>
export default {
setup() {
const root = ref(null)
onMounted(() => {
// the DOM element will be assigned to the ref after initial render
console.log(root.value) // <div/>
})
return {
root,
}
},
}
</script>
✅ String ref && return it from setup() && Render Function / JSX
export default {
setup() {
const root = ref(null)
onMounted(() => {
// the DOM element will be assigned to the ref after initial render
console.log(root.value) // <div/>
})
return {
root,
}
},
render() {
// with JSX
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,
})
// with JSX
return () => <div ref={root} />
},
}
⚠️ $refs accessing workaround
⚠️ Warning: TheSetupContext.refswon't exist inVue 3.0.@vue/composition-apiprovide 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(() => {
// the DOM element will be assigned to the ref after initial render
console.log(refs.root) // <div/>
})
return () =>
h('div', {
ref: 'root',
})
// with JSX
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() mutates the original object
reactive uses Vue.observable underneath which will mutate the original object.
💡 In Vue 3, it will return an new proxy object.
❌ onTrack and onTrigger are not available in WatchOptions
watch(() => {
/* ... */
}, {
immediate: true,
onTrack() {}, // not available
onTrigger() {}, // not available
})
⚠️ 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) // equivalent to Vue.component('Foo', Foo)
app1.use(VueRouter) // equivalent to Vue.use(VueRouter)
const app2 = createApp(RootComponent2)
app2.component('Bar', Bar) // equivalent to Vue.use('Bar', Bar)The following APIs introduced in Vue 3 are not available in this plugin.
readonlyshallowReadonlydefineAsyncComponentonRenderTrackedonRenderTriggeredisProxyisReadonlyisVNode
❌ Passing ref, reactive or other reactive apis to data() would not work.
export default {
data() {
return {
// will result { a: { value: 1 } } in template
a: ref(1)
}
},
}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.