Skip to content

Latest commit

 

History

History
433 lines (317 loc) · 8.22 KB

README.md

File metadata and controls

433 lines (317 loc) · 8.22 KB

@vue/composition-api

Vue 2 plugin for Composition API

npm GitHub Workflow Status

English | 中文Composition API Docs

Installation

NPM

npm install @vue/composition-api
# or
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)
// use the APIs
import { ref, reactive } from '@vue/composition-api'

💡 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@0.6.5"></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({
  // type inference enabled
})

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

✅ Support     ❌ 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)],
})
// 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 // true
const 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

Template Refs

✅ 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: 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(() => {
      // 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

⚠️ 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.

Watch

onTrack and onTrigger are not available in WatchOptions
watch(() => {
  /* ... */
}, {
  immediate: true,
  onTrack() {},  // not available
  onTrigger() {},  // not available
})

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) // 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)

Missing APIs

The following APIs introduced in Vue 3 are not available in this plugin.

  • readonly
  • shallowReadonly
  • defineAsyncComponent
  • onRenderTracked
  • onRenderTriggered
  • customRef
  • isProxy
  • isReadonly
  • isVNode

Reactive APIs in data()

❌ 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) 
    }
  },
}

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.