原文地址 github.com
安裝
NPM
npm install @vue/composition-api
# or
yarn add @vue/composition-api
在使用 @vue/composition-api
前,必須先通過 Vue.use()
進行安裝。之后才可使用新的 組合式 API 進行組件開發。
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
// 使用 API
import { ref, reactive } from '@vue/composition-api'
💡 當遷移到 Vue 3 時,只需簡單的將
@vue/composition-api
替換成vue
即可。你現有的代碼幾乎無需進行額外的改動。
CDN
在 Vue 之后引入 @vue/composition-api
,插件將會自動完成安裝。
<script src="https://cdn.jsdelivr.net/npm/vue@2.6"></script>
<script src="https://cdn.jsdelivr.net/npm/@vue/composition-api@1.0.0-rc.5"></script>
@vue/composition-api
將會暴露在全局變量 window.VueCompositionAPI
中。
const { ref, reactive } = VueCompositionAPI
TypeScript 支持
本插件要求使用 TypeScript 3.5.1 或以上版本
為了讓 TypeScript 在 Vue 組件選項中正確地進行類型推導,我們必須使用 defineComponent
來定義組件:
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
// 類型推斷啟用
})
JSX/TSX
JSX 現已在 vuejs/jsx 中官方支持。你可以根據這篇文檔開啟支持。你也可以使用由 @luwanquan 維護的社區版本 babel-preset-vca-jsx。
對於 TSX 支持,請在你的項目中創建如下聲明文件:
// file: shim-tsx.d.ts
import Vue, { VNode } from 'vue';
import { ComponentRenderProxy } from '@vue/composition-api';
declare global {
namespace JSX {
interface Element extends VNode {}
interface ElementClass extends ComponentRenderProxy {}
interface ElementAttributesProperty {
$props: any; // specify the property name to use
}
interface IntrinsicElements {
[elem: string]: any;
}
}
}
SSR
盡管 Vue 3 暫時沒有給出確定的 SSR 的 API,這個插件實現了 onServerPrefetch
生命周期鈎子函數。這個鈎子允許你使用傳統 API 中的 serverPrefetch
函數。
import { onServerPrefetch } from '@vue/composition-api'
export default {
setup(props, { ssrContext }) {
const result = ref()
onServerPrefetch(async () => {
result.value = await callApi(ssrContext.someId)
})
return {
result,
}
},
}
限制
✅ 支持 ❌ 不支持
Ref
自動展開 (unwrap)
數組索引屬性無法進行自動展開:
❌ 不要 使用數組直接存取 ref
對象
const state = reactive({
list: [ref(0)],
})
// 不會自動展開, 須使用 `.value`
state.list[0].value === 0 // true
state.list.push(ref(1))
// 不會自動展開, 須使用 `.value`
state.list[1].value === 1 // true
❌ 不要 在數組中使用含有 ref
的普通對象
const a = {
count: ref(0),
}
const b = reactive({
list: [a], // `a.count` 不會自動展開!!
})
// `count` 不會自動展開, 須使用 `.value`
b.list[0].count.value === 0 // true
const b = reactive({
list: [
{
count: ref(0), // 不會自動展開!!
},
],
})
// `count` 不會自動展開, 須使用 `.value`
b.list[0].count.value === 0 // true
✅ 在數組中,應該 總是將 ref
存放到 reactive
對象中
const a = reactive({
count: ref(0),
})
const b = reactive({
list: [a],
})
// 自動展開
b.list[0].count === 0 // true
b.list.push(
reactive({
count: ref(1),
})
)
// 自動展開
b.list[1].count === 1 // true
⚠️ set
和 del
添加與刪除響應式屬性變通方案
⚠️ 警告:
set
和del
並非 Vue 3 的一部分。由於 Vue 2.x 響應式系統的限制,我們在插件中提供該 API 作為添加響應式屬性的一個變通方案。在 Vue 3 中,你只需要直接為屬性賦值即可。
import { reactive, set } from '@vue/composition-api'
const a = reactive({
foo: 1
})
// 添加新的響應式屬性
set(a, 'bar', 1)
// 刪除屬性並觸發響應式更新
del(a, 'bar')
模板 Refs
✅ 字符串 ref && 從 setup()
返回 ref
<template>
<div ref="root"></div>
</template>
<script>
export default {
setup() {
const root = ref(null)
onMounted(() => {
// 在初次渲染后 DOM 元素會被賦值給 ref
console.log(root.value) // <div/>
})
return {
root,
}
},
}
</script>
✅ 字符串 ref && 從 setup()
返回 ref && 渲染函數 / JSX
export default {
setup() {
const root = ref(null)
onMounted(() => {
// 在初次渲染后 DOM 元素會被賦值給 ref
console.log(root.value) // <div/>
})
return {
root,
}
},
render() {
// 使用 JSX
return () => <div ref="root" />
},
}
❌ 函數 ref
<template>
<div :ref="el => root = el"></div>
</template>
<script>
export default {
setup() {
const root = ref(null)
return {
root,
}
},
}
</script>
❌ 在 setup()
中的渲染函數 / JSX
export default {
setup() {
const root = ref(null)
return () =>
h('div', {
ref: root,
})
// 使用 JSX
return () => <div ref={root} />
},
}
⚠️ $refs
訪問的變通方案
⚠️ 警告:
SetupContext.refs
並非Vue 3.0
的一部分,@vue/composition-api
將其暴露在SetupContext
中只是臨時提供一種變通方案。
如果你依然選擇在 setup()
中寫 render
函數,那么你可以使用 SetupContext.refs
來訪問模板引用,它等價於 Vue 2.x 中的 this.$refs
:
export default {
setup(initProps, setupContext) {
const refs = setupContext.refs
onMounted(() => {
// 在初次渲染后 DOM 元素會被賦值給 ref
console.log(refs.root) // <div/>
})
return () =>
h('div', {
ref: 'root',
})
// 使用 JSX
return () => <div ref="root" />
},
}
如果項目使用了 TypeScript,你還需要擴展 SetupContext
類型:
import Vue from 'vue'
declare module '@vue/composition-api' {
interface SetupContext {
readonly refs: { [key: string]: Vue | Element | Vue[] | Element[] }
}
}
Reactive
⚠️ reactive()
會返回一個修改過的原始的對象
此行為與 Vue 2 中的 Vue.observable
一致
💡 在 Vue 3 中,
reactive()
會返回一個新的的代理對象
Watch
❌ 不支持 onTrack
和 onTrigger
選項
watch(
() => {
/* ... */
},
{
immediate: true,
onTrack() {}, // 不可用
onTrigger() {}, // 不可用
}
)
shallowReadonly
⚠️ shallowReadonly()
會返回一個新的淺拷貝對象,在此之后新加的字段將不會獲得只讀或響應式狀態。
💡 在 Vue 3 中,
shallowReadonly()
會返回一個新的的代理對象
readonly
⚠️ readonly()
** 只提供類型層面 ** 的只讀。
readonly()
只在類型層面提供和 Vue 3 的對齊。在其返回值或其屬性上使用 isReadonly()
檢查的結果將無法保證。
props
⚠️ 當使用 toRefs
訪問深層屬性對象 (如 toRefs(props.foo.bar)
時將會得到不正確的警告。 ⚠️ isReactive(props.foo.bar)
將會返回 false。
defineComponent({
setup(props) {
const { bar } = toRefs(props.foo) // it will `warn`
// use this instead
const { foo } = toRefs(props)
const a = foo.value.bar
}
})
缺失的 API
以下在 Vue 3 新引入的 API ,在本插件中暫不適用:
onRenderTracked
onRenderTriggered
isProxy
在 data()
中使用組合式 API
❌ 在 data()
中使用 ref
, reactive
或其他組合式 API 將不會生效
export default {
data() {
return {
// 在模版中會成為 { a: { value: 1 } }
a: ref(1),
}
},
}
emit
選項
❌ emit
僅因在類型定義中對齊 Vue3 的選項而提供,不會有任何效果。
defineComponent({
emit: {
// 無效
submit: (eventOption) => {
if (...) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
}
})
性能影響
由於 Vue 2 的公共 API 的限制,@vue/composition-api
不可避免地引入了額外的性能開銷。除非在極端情況下,否則這並不會對你造成影響。
你可以查看這個 跑分結果 了解更多信息。