在 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>