Vue 2 plugin for Composition API
English | 中文 ・ Composition API Docs
⚠️ With the release of Vue 2.7, which has Composition API built-in, you no longer need this plugin. Thereby this plugin has entered maintenance mode and will only support Vue 2.6 or earlier. This project will reach End of Life by the end of 2022.
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.
importVuefrom'vue'importVueCompositionAPIfrom'@vue/composition-api'Vue.use(VueCompositionAPI)// use the APIsimport{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.
<scriptsrc="https://cdn.jsdelivr.net/npm/vue@2.6"></script><scriptsrc="https://cdn.jsdelivr.net/npm/@vue/composition-api@1.7.2"></script>@vue/composition-api will be exposed to global variable window.VueCompositionAPI.
const{ ref, reactive }=VueCompositionAPITypeScript version >4.2 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'exportdefaultdefineComponent({// type inference enabled})JSX is now officially supported on vuejs/jsx. You can enable it by following this document. A community maintained version can be found at babel-preset-vca-jsx by @luwanquan.
To support TSX, create a declaration file with the following content in your project.
// file: shim-tsx.d.tsimportVue,{VNode}from'vue';import{ComponentRenderProxy}from'@vue/composition-api';declare global {namespaceJSX{interfaceElementextendsVNode{}interfaceElementClassextendsComponentRenderProxy{}interfaceElementAttributesProperty{$props: any;// specify the property name to use}interfaceIntrinsicElements{[elem: string]: any;}}}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'exportdefault{setup(props,{ ssrContext }){constresult=ref()onServerPrefetch(async()=>{result.value=awaitcallApi(ssrContext.someId)})return{
result,}}}@vue/composition-api supports all modern browsers and IE11+. For lower versions IE you should install WeakMap polyfill (for example from core-js package).
✅ Support ❌ Not Supported
❌ Should NOT use ref in a plain object when working with Array
consta={count: ref(0),}constb=reactive({list: [a],// `a.count` will not unwrap!!})// no unwrap for `count`, `.value` is requiredb.list[0].count.value===0// trueconstb=reactive({list: [{count: ref(0),// no unwrap!!},],})// no unwrap for `count`, `.value` is requiredb.list[0].count.value===0// true
✅ Should always use ref in a reactive when working with Array
consta=reactive({list: [reactive({count: ref(0),}),]})// unwrappeda.list[0].count===0// truea.list.push(reactive({count: ref(1),}))// unwrappeda.list[1].count===1// true
✅ String ref && return it from setup()
<template><divref="root"></div></template><script>exportdefault{setup(){constroot=ref(null)onMounted(()=>{// the DOM element will be assigned to the ref after initial renderconsole.log(root.value)// <div/>})return{
root,}},}</script>
✅ String ref && return it from setup() && Render Function / JSX
exportdefault{setup(){constroot=ref(null)onMounted(()=>{// the DOM element will be assigned to the ref after initial renderconsole.log(root.value)// <div/>})return{
root,}},render(){// with JSXreturn()=><divref="root"/>},}❌ Function ref
<template><div:ref="el => root = el"></div></template><script>exportdefault{setup(){constroot=ref(null)return{
root,}},}</script>
❌ Render Function / JSX in setup()
exportdefault{setup(){constroot=ref(null)return()=>h('div',{ref: root,})// with JSXreturn()=><divref={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
exportdefault{setup(initProps,setupContext){constrefs=setupContext.refsonMounted(()=>{// the DOM element will be assigned to the ref after initial renderconsole.log(refs.root)// <div/>})return()=>h('div',{ref: 'root',})// with JSXreturn()=><divref="root"/>},}⚠️ reactive()mutates the original object
reactive uses Vue.observable underneath which will mutate the original object.
💡 In Vue 3, it will return a new proxy object.
⚠️ set and del workaround for adding and deleting reactive properties
⚠️ Warning:setanddeldo NOT exist in Vue 3. We provide them as a workaround here, due to the limitation of Vue 2.x reactivity system.In Vue 2, you will need to call
setto track new keys on anobject(similar toVue.setbut forreactive objectscreated by the Composition API). In Vue 3, you can just assign them like normal objects.Similarly, in Vue 2 you will need to call
delto ensure a key deletion triggers view updates in reactive objects (similar toVue.deletebut forreactive objectscreated by the Composition API). In Vue 3 you can just delete them by callingdelete foo.bar.
import{reactive,set,del}from'@vue/composition-api'consta=reactive({foo: 1})// add new reactive keyset(a,'bar',1)// remove a key and trigger reactivitydel(a,'bar')
❌ onTrack and onTrigger are not available in WatchOptions
watch(()=>{/* ... */},{immediate: true,onTrack(){},// not availableonTrigger(){},// not available})⚠️ createApp() is global
In Vue 3, createApp() is introduced to provide context(plugin, components, etc.) isolation between app instances. Due the design of Vue 2, in this plugin, we provide createApp() as a forward compatible API which is just an alias of the global.
constapp1=createApp(RootComponent1)app1.component('Foo',Foo)// equivalent to Vue.component('Foo', Foo)app1.use(VueRouter)// equivalent to Vue.use(VueRouter)constapp2=createApp(RootComponent2)app2.component('Bar',Bar)// equivalent to Vue.component('Bar', Bar)⚠️ createElement / h workaround
createElement / h in Vue 2 is only accessable in render() function. To use it outside of render(), you can explicitly bind a component instance to it.
⚠️ Warning: This ability is provided as a workaround Vue 2, it's not part of the Vue 3 API.
import{has_h}from'@vue/composition-api'exportdefault{setup(){constvm=getCurrentInstance()consth=_h.bind(vm)return()=>h('div',{ref: 'root',})},}⚠️ shallowReadonly() will create a new object and with the same root properties, new properties added will not be readonly or reactive.
💡 In Vue 3, it will return a new proxy object.
⚠️ readonly() provides only type-level readonly check.
readonly() is provided as API alignment with Vue 3 on type-level only. Use isReadonly() on it or it's properties can not be guaranteed.
⚠️ toRefs(props.foo) will incorrectly warn when accessing nested levels of props.
⚠️ isReactive(props.foo) will return false.
defineComponent({setup(props){const{ bar }=toRefs(props.foo)// it will `warn`// use this insteadconst{ foo }=toRefs(props)consta=foo.value.bar}})⚠️ computed() has a property effect set to true instead of a ReactiveEffect.
Due to the difference in implementation, there is no such concept as a ReactiveEffect in @vue/composition-api. Therefore, effect is merely true to enable differentiating computed from refs:
functionisComputed<T>(o: ComputedRef<T>|unknown): o is ComputedRef<T>functionisComputed(o: any): o is ComputedRef{return!!(isRef(o)&&o.effect)}The following APIs introduced in Vue 3 are not available in this plugin.
onRenderTrackedonRenderTriggeredisProxy
❌ Passing ref, reactive or other reactive apis to data() would not work.
exportdefault{data(){return{// will result { a: { value: 1 }} in templatea: ref(1),}},}
❌ emits option is provided in type-level only, in order to align with Vue 3's type interface. Does NOT have actual effects on the code.
defineComponent({emits: {// has no effectssubmit: (eventOption)=>{if(...){
return true}else{console.warn('Invalid submit event payload!')returnfalse}}}})Due the limitation of Vue2's public API. @vue/composition-api inevitably introduces some performance overhead. Note that in most scenarios, this shouldn't be the source of performance issues.
You can check the benchmark results for more details.