在 Vue 中,Mixins 可以包含任意组件的选项。这使得用 Mixins 能很方便的抽象多个组件间的公共部分,但也会带来一些问题:

  1. 命名冲突导致的运行结果的不确定性。组件 和 引入的 Mixins,引入的多个 Mixins 之间,都会出现数据名,方法名的命名冲突。出现命名冲突时,同名的数据或方法会被覆盖,对应的业务也就出错了。
  2. 隐式依赖导致的高耦合。组件 和 引入的 Mixins 之间会出现互相依赖的情况,如果依赖的数据或方法重命名了,数据或方法就找不到了。

Composition API 可以很好的解决这些问题。组件可以用 Composition API 暴露出的可响应数据。组件和 Composition API 不能读取和修改各自内部的数据和方法。

解决方案

我们来看个 Demo。做一个管理后台的列表页。列表页支持筛选搜索,显示列表,列表分页的功能。

用 Mixins 实现

可以将需要的组件的引入;列表,搜索条件分页数据;以及数据的交互放到 Mixins。如下:

  1. import SearchForm from '@/components/search-form.vue'
  2. import SearchItem from '@/components/search-item.vue'
  3. import TableGrid from '@/components/search-item.vue'
  4. export default {
  5. components: {
  6. SearchForm, // 搜索条件表单组件
  7. SearchItem, // 搜索条件组件
  8. TableGrid //表格组件
  9. },
  10. data() {
  11. return {
  12. list: [/* 列表数据 */],
  13. searchQuery: {/* 搜索条件 */},
  14. pager: {/* 分页 */}
  15. }
  16. },
  17. mounted () {
  18. this.fetchList();
  19. },
  20. watch: {
  21. searchQuery: {
  22. handler() {this.fetchList()},
  23. deep: true
  24. }
  25. },
  26. methods: {
  27. fetchList() {/* 获取列表 */},
  28. }
  29. }

列表页这么写:

  1. <template>
  2. <div>
  3. <!-- 搜索功能 -->
  4. <SearchForm @search="fetchList">
  5. <div>
  6. <SearchItem title="姓名">
  7. <input type="text" v-model="searchQuery.name">
  8. </SearchItem>
  9. </div>
  10. </SearchForm>
  11. <!-- 表格 -->
  12. <TableGrid :list="list" :pager="pager"/>
  13. </div>
  14. </template>
  15. <script>
  16. import listMixins from './mixin'
  17. export default {
  18. mixins: [listMixins],
  19. }
  20. </script>

用 Composition API 重构

我们用 Composition API 来重构上面的 Mixins。如下:

  1. import { onMounted, reactive, watch, toRefs } from 'vue'
  2. import SearchForm from '@/components/search-form.vue'
  3. import SearchItem from '@/components/search-item.vue'
  4. import TableGrid from '@/components/search-item.vue'
  5. export default function useList() {
  6. const data = reactive({
  7. searchQuery: {},
  8. list: [],
  9. pager: {}
  10. })
  11. const fetchList = () => {/* 获取列表 */}
  12. onMounted(fetchList)
  13. watch(() => data.searchQuery, fetchList, { deep: true })
  14. return {
  15. ...toRefs(data),
  16. fetchList,
  17. components: {
  18. SearchForm,
  19. SearchItem,
  20. TableGrid
  21. }
  22. }
  23. }

列表页这么写:

  1. <template>
  2. <div>
  3. <!-- 搜索功能 -->
  4. <SearchForm @search="fetchList">
  5. <SearchItem title="姓名">
  6. <input type="text" v-model="searchQuery.name">
  7. </SearchItem>
  8. </SearchForm>
  9. <!-- 表格 -->
  10. <TableGrid :list="list" :pager="pager"/>
  11. </div>
  12. </template>
  13. <script setup>
  14. import useList from './use-list'
  15. const {
  16. components: {
  17. SearchForm,
  18. SearchItem,
  19. TableGrid
  20. },
  21. list,
  22. fetchList,
  23. searchQuery,
  24. } = useList()
  25. </script>

参考文档