在 Vue 中,Mixins 可以包含任意組件的選項。這使得用 Mixins 能很方便的抽象多個組件間的公共部分,但也會帶來一些問題:
- 命名衝突導致的運行結果的不確定性。組件 和 引入的 Mixins,引入的多個 Mixins 之間,都會出現數據名,方法名的命名衝突。出現命名衝突時,同名的數據或方法會被覆蓋,對應的業務也就出錯了。
- 隱式依賴導致的高耦合。組件 和 引入的 Mixins 之間會出現互相依賴的情況,如果依賴的數據或方法重命名了,數據或方法就找不到了。
Composition API 可以很好的解決這些問題。組件可以用 Composition API 暴露出的可響應數據。組件和 Composition API 不能讀取和修改各自內部的數據和方法。
解決方案
我們來看個 Demo。做一個管理後臺的列表頁。列表頁支持篩選搜索,顯示列表,列表分頁的功能。
用 Mixins 實現
可以將需要的組件的引入;列表,搜索條件分頁數據;以及數據的交互放到 Mixins。如下:
import SearchForm from '@/components/search-form.vue'
import SearchItem from '@/components/search-item.vue'
import TableGrid from '@/components/search-item.vue'
export default {
components: {
SearchForm, // 搜索條件表單組件
SearchItem, // 搜索條件組件
TableGrid //表格組件
},
data() {
return {
list: [/* 列表數據 */],
searchQuery: {/* 搜索條件 */},
pager: {/* 分頁 */}
}
},
mounted () {
this.fetchList();
},
watch: {
searchQuery: {
handler() {this.fetchList()},
deep: true
}
},
methods: {
fetchList() {/* 獲取列表 */},
}
}
列表頁這麼寫:
<template>
<div>
<!-- 搜索功能 -->
<SearchForm @search="fetchList">
<div>
<SearchItem title="姓名">
<input type="text" v-model="searchQuery.name">
</SearchItem>
</div>
</SearchForm>
<!-- 表格 -->
<TableGrid :list="list" :pager="pager"/>
</div>
</template>
<script>
import listMixins from './mixin'
export default {
mixins: [listMixins],
}
</script>
用 Composition API 重構
我們用 Composition API
來重構上面的 Mixins。如下:
import { onMounted, reactive, watch, toRefs } from 'vue'
import SearchForm from '@/components/search-form.vue'
import SearchItem from '@/components/search-item.vue'
import TableGrid from '@/components/search-item.vue'
export default function useList() {
const data = reactive({
searchQuery: {},
list: [],
pager: {}
})
const fetchList = () => {/* 獲取列表 */}
onMounted(fetchList)
watch(() => data.searchQuery, fetchList, { deep: true })
return {
...toRefs(data),
fetchList,
components: {
SearchForm,
SearchItem,
TableGrid
}
}
}
列表頁這麼寫:
<template>
<div>
<!-- 搜索功能 -->
<SearchForm @search="fetchList">
<SearchItem title="姓名">
<input type="text" v-model="searchQuery.name">
</SearchItem>
</SearchForm>
<!-- 表格 -->
<TableGrid :list="list" :pager="pager"/>
</div>
</template>
<script setup>
import useList from './use-list'
const {
components: {
SearchForm,
SearchItem,
TableGrid
},
list,
fetchList,
searchQuery,
} = useList()
</script>