共计 4736 个字符,预计需要花费 12 分钟才能阅读完成。
简略的治理后盾基本上就是数据的增删改查。次要就是 列表 + form 表单。每个页面的逻辑基本上都雷同。不同的中央就是每个页面须要调用的具体 API 及参数。
以前 vue2 的时候最简略的做法是写进去一个页面的逻辑,而后间接 copy 到各个页面中,批改 API 及参数即可。高级一点的是利用 mixin
函数,将可复用逻辑抽离,每个页面引入 mixin
。
vue3 之后新增了 composition API
。本文就是利用composition API
,将可复用的逻辑抽离到composition API
中,并引入 ts,实现一个简略的治理后盾性能。
利用 @vue/cli 创立我的项目
首先须要将 @vue/cli 降级到最新版。本文用的是 4.5.6 版本。
vue create admin | |
cd admin | |
npm run serve |
create 抉择手动抉择 Manually select features
,会有一些交互性的抉择,是否要装置router、vuex
等选项,空格能够切换是否选中。咱们选中TypeScript、Router、Vuex、CSS Pre-processors
。
咱们利用 axios + axios-mock-adapter + mockjs
来进行接口申请、接口模仿及假数据生成,接下来再装置这几个包。
npm install axios | |
npm install -D axios-mock-adapter mockjs |
我的项目整体框架
假如咱们的我的项目蕴含一个 Header,Header 的作用是切换页面。两个页面,别离为 List 和 About,这两个页面都是简略的列表 + 增删改查的操作。
路由
须要在 router 中减少一个 list 的路由信息。
const routes: Array<RouteRecordRaw> = [ | |
{ | |
path: '/', | |
name: 'Home', | |
component: Home, | |
}, | |
{ | |
path: '/about', | |
name: 'About', | |
component: () => { return import(/* webpackChunkName: "about" */ '../views/About.vue'); }, | |
}, | |
{ | |
path: '/list', | |
name: 'List', | |
component: () => { return import(/* webpackChunkName: "list" */ '../views/List.vue'); }, | |
}, | |
]; |
列表页
首先把列表页的构造写进去,List 和 About 的构造大体类似。
<template> | |
<div class='content_page'> | |
<div class='content_body'> | |
<div class='content_button'> | |
<button class='add primary' @click='addItem' title='增加'> 增加 </button> | |
</div> | |
<div class='content_table'> | |
<table> | |
<thead> | |
<tr> | |
<th v-for='item in thead' :key='item'>{{item}}</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr v-for='(item, index) in list' :key='item.id'> | |
<td> | |
<span :title='item.id'>{{item.id}}</span> | |
</td> | |
<td> | |
<div v-if='index === currentIndex'> | |
<input | |
v-model='item.name' | |
title='name' | |
/> | |
</div> | |
<span :title='item.name' v-else>{{item.name}}</span> | |
</td> | |
<td :title='item.sex'> | |
<div v-if='index === currentIndex'> | |
<input | |
v-model='item.sex' | |
title='sex' | |
/> | |
</div> | |
<span :title='item.sex' v-else>{{item.sex ? '男' : '女'}}</span> | |
</td> | |
<td :title='item.birth'> | |
<div v-if='index === currentIndex'> | |
<input | |
v-model='item.birth' | |
title='birth' | |
/> | |
</div> | |
<span :title='item.birth' v-else>{{item.birth}}</span></td> | |
<td :title='item.address'> | |
<div v-if='index === currentIndex'> | |
<input | |
v-model='item.address' | |
title='birth' | |
/> | |
</div> | |
<span :title='item.address' v-else>{{item.address}}</span> | |
</td> | |
<td> | |
<div v-if='index === currentIndex'> | |
<button | |
class='primary confirm' | |
@click='confirm(item)' | |
> 确定 </button> | |
<button | |
@click='cancel(item)' | |
> 勾销 </button> | |
</div> | |
<span v-else> | |
<span @click='editItem(index)'> | |
edit | |
</span> | |
<span @click='deleteItem(index, item)'>delete</span> | |
</span> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
</template> |
其中用到了 addItem、editItem、deleteItem、confirm、cancel
这几个办法,每个列表页的这几个办法性能都是雷同的,惟一的不同就是申请的 API,咱们能够将这几个 API 做为参数,将增删改查的办法提取到 setup
函数中,做到复用。接下来就来到重点的composition API
。
composition API 具体实现
import {ref, onMounted} from 'vue'; | |
import {ItemType, FetchType, DeleteType, AddType, EditType} from '../../types/index'; | |
export const compositionApi = ( | |
fetchApi: FetchType, | |
deleteApi: DeleteType, | |
confirmAddApi: AddType, | |
confirmEditApi: EditType, | |
itemData: ItemType, | |
) => {const currentIndex = ref<number | null>(null); | |
const list = ref([{}]); | |
const getList = () => {fetchApi().then((res: any) => {list.value = res.data.list;}); | |
}; | |
const addItem = () => {list.value.unshift(itemData); | |
currentIndex.value = 0; | |
}; | |
const editItem = (index: number) => {currentIndex.value = index;}; | |
const deleteItem = (index: number, item: ItemType) => {deleteApi(item).then(() => {list.value.splice(index, 1); | |
// getList();}); | |
}; | |
const cancel = (item: ItemType) => { | |
currentIndex.value = null; | |
if (!item.id) {list.value.splice(0, 1); | |
} | |
}; | |
const confirm = (item: ItemType) => { | |
const api = item.id ? confirmEditApi : confirmAddApi; | |
api(item).then(() => {getList(); | |
cancel(item); | |
}); | |
}; | |
onMounted(() => {getList(); | |
}); | |
return { | |
list, | |
currentIndex, | |
getList, | |
addItem, | |
editItem, | |
deleteItem, | |
cancel, | |
confirm, | |
}; | |
}; | |
export default compositionApi; |
接下来就是在 List 和 About 页面中的 setup 办法中引入即可。
<script lang='ts'> | |
import axios from 'axios'; | |
import {defineComponent, reactive} from 'vue'; | |
import {compositionApi} from '../components/composables/index'; | |
import {ItemType} from '../types/index'; | |
const ListComponent = defineComponent({ | |
name: 'List', | |
setup() { | |
const state = reactive({ | |
itemData: { | |
id: '', | |
name: '', | |
sex: 0, | |
birth: '', | |
address: '', | |
}, | |
}); | |
const fetchApi = () => {return axios.get('/users'); | |
}; | |
const deleteApi = (item: ItemType) => {return axios.post('/users/delete', { id: item.id}); | |
}; | |
const confirmAddApi = (item: ItemType) => { | |
return axios.post('/users/add', { | |
name: item.name, | |
birth: item.birth, | |
address: item.address, | |
}); | |
}; | |
const confirmEditApi = (item: ItemType) => { | |
return axios.post('/users/edit', { | |
id: item.id, | |
name: item.name, | |
birth: item.birth, | |
address: item.address, | |
}); | |
}; | |
const localPageData = compositionApi(fetchApi, deleteApi, confirmAddApi, confirmEditApi, state.itemData); | |
return { | |
state, | |
...localPageData, | |
}; | |
}, | |
data() { | |
return { | |
thead: [ | |
'id', | |
'name', | |
'sex', | |
'birth', | |
'address', | |
'option', | |
], | |
}; | |
} | |
}); |
这样 List 页面的逻辑基本上就实现了。同样,About 页面的逻辑也就实现了,不同的就是在 About 页面更改一下接口申请的地址。
最终实现成果
composition API vs Mixin
在 vue3 之前,代码复用的话个别都是用 mixin
,然而mixin
相比于 composition API
的劣势,在官网中的解释如下:
mixin
很容易发生冲突:因为每个个性的属性都被合并到同一个组件中,所以为了防止 property 名抵触和调试,你依然须要理解其余每个个性。- 可重用性是无限的:咱们不能向
mixin
传递任何参数来扭转它的逻辑,这升高了它们在形象逻辑方面的灵活性
源代码
我的项目中用到的一些 TS 接口的定义、模仿数据及接口申请本文中没有具体介绍,如果想理解的话能够去看看源码。
戳这里:vue3_ts_admin