vue2 + typescript + composition-api 常见问题
WARNING
环境
- nuxt@2.15.3
- typescript@4.2.4
- vue@2.6.12
安装
安装 @nuxtjs/composition-api
:
yarn add @nuxtjs/composition-api
配置 nuxt.config.js
开启模块
{
buildModules: ['@nuxtjs/composition-api/module']
}
setup
import {
defineComponent,
reactive,
onMounted,
useRoute,
} from '@nuxtjs/composition-api'
export default defineComponent({
setup() {
// ...
},
})
router & route
import { defineComponent, useRoute, useRouter } from '@nuxtjs/composition-api'
export default defineComponent({
setup() {
const { query } = useRoute().value
const id = query.id as string
const router = useRouter()
router.push('/')
},
})
$axios
配置 tsconfig.json
{
"compilerOptions": {
"types": ["@nuxt/types", "@nuxtjs/axios"]
}
}
import { defineComponent, useContext } from '@nuxtjs/composition-api'
export default defineComponent({
setup() {
const { $axios } = useContext()
// ...
},
})
$loading
import {
defineComponent,
useRoute,
wrapProperty,
useContext,
} from '@nuxtjs/composition-api'
import type { ElLoadingComponent } from 'element-ui/types/loading'
export default defineComponent({
setup() {
const { $axios } = useContext()
const { query } = useRoute().value
const id = query.id as string
const data = []
const fetchApi = async () => {
const $loading = wrapProperty('$loading', false)()
let loading!: ElLoadingComponent
try {
loading = $loading({})
data = await $axios.$get(API.apiDetail(id))
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
fetchApi()
return {
data,
}
},
})
TIP
wrapProperty 第二个参数是一个布尔值,指示辅助函数是否应该返回一个计算属性,它默认为 true
vuex
import { defineComponent, useStore } from '@nuxtjs/composition-api'
export default defineComponent({
setup() {
const store = useStore()
},
})
mapState / mapMutations / mapActions / mapGetters
方式一:vuex#accessing-mutations-and-actions
import { defineComponent, useStore } from '@nuxtjs/composition-api'
export default defineComponent({
setup() {
const store = useStore()
const fooMutation = (...args: any[]) =>
store.commit('namespace/fooMutation', ...args)
// call fooMutation with args
// fooMutation('foo', 'bar')
return {
// access a mutation
increment: () => store.commit('increment'),
// access an action
asyncIncrement: () => store.dispatch('asyncIncrement'),
}
},
})
方式二:vuex-composition-helpers (封装了 store 的 state/commit/dispath)
import {
useNamespacedMutations,
useNamespacedState,
} from 'vuex-composition-helpers'
const namespace = 'interface-setting'
export default defineComponent({
setup() {
const { code } = useNamespacedState(namespace, ['code'])
const { updateCode } = useNamespacedMutations(namespace, ['updateCode'])
return {
code,
updateCode,
}
},
})
refs
<template>
<el-form ref="dialogFormRef" :model="dialogForm">
<!-- ... -->
</el-form>
<el-button type="primary" @click="onDialogFormSubmit">确 定</el-button>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from '@nuxtjs/composition-api'
import type { ElForm } from 'element-ui/types/form'
export default defineComponent({
setup() {
const dialogForm = reactive({...})
const dialogFormRef = ref({} as ElForm)
const onDialogFormSubmit = () => {
dialogFormRef.value.validate(async valid => {
if (valid) {
// do with dialogForm
} else {
return false
}
})
}
return {
dialogForm,
dialogFormRef,
onDialogFormSubmit,
}
}
})
</script>
jsx in typescript
way 1: replace jsx with vnode
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column
v-for="col in columns"
:key="col.prop"
v-bind="{ ...col }"
/>
</el-table>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from '@nuxtjs/composition-api'
import { h } from '@vue/composition-api'
export default defineComponent({
setup() {
return {
tableData: [
{
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄',
},
],
columns: [
{
prop: 'date',
label: '日期',
},
{
prop: 'name',
label: '姓名',
formatter(row, column, value) {
// avoid writing jsx in typescript. instead of return a vdom
return [
h(
ElementPlus.ElButton,
{
type: 'success',
icon: 'el-icon-search',
},
'搜索'
),
]
},
},
{
prop: 'address',
label: '地址',
},
],
}
},
})
</script>
way2: (推荐)
- 创建 jsx.d.ts
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
interface Element extends VNode {}
interface ElementClass extends Vue {}
interface ElementAttributesProperty {
$props: {}
}
interface IntrinsicElements {
[elemName: string]: any
}
}
}
- 更新
tsconfig.json
{
"compilerOptions": {
"jsx": "preserve"
}
}
- code
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column v-for="col in columns" :key="col.prop" v-bind="{...col}" />
</el-table>
</template>
<script lang="tsx">
import { defineComponent, ref, reactive } from '@nuxtjs/composition-api'
export default defineComponent({
setup() {
return {
tableData: [
{
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄',
},
],
columns: [
{
prop: 'date',
label: '日期',
},
{
prop: 'name',
label: '姓名',
formatter(row, column, value) {
return <ElButton type={row.type} icon={row.icon}></ElButton>
},
},
{
prop: 'address',
label: '地址',
},
],
}
},
})
</script>
vuex types
vuex@3.6.2 支持 vue2。如果使用 vue3,请使用 vuex@4
// Getter, Plugin 等参见 vuex/types
import { MutationTree, ActionTree } from 'vuex'
const export const state = () => ({
stateA: '',
stateB: {
foo: 0
},
stateC: false
})
type State = ReturnType<typeof state>
export const mutations: MutationTree<State> = {
updateA(state, payload: string) {
state.stateA = payload
},
updateB(state, payload: {foo: number}) {
state.stateB = payload
},
}
export const actions: ActionTree<State, {}> = {
async login({ commit }, payload: any) {
const json = await $axios.post(API.login, payload)
commit('updateB', json)
}
}
provide/inject(响应性)
父组件中提供响应式的注入
import { defineComponent, provide, toRef } from '@nuxtjs/composition-api'
export default defineComponent({
setup(props) {
//
provide('height', toRef(props, 'height'))
}
}
子组件中监听响应式的注入
import { defineComponent, inject, watch } from '@nuxtjs/composition-api'
export default defineComponent({
setup(props) {
watch(
inject('height') as string,
(height) => {
// do something with height
},
{
immediate: true,
}
)
}
}