阅读 1005

Vue3 合理使用Composition API

在学习 Composition API 时,官方有个深入骨髓的小栗子,如果你没有看过也没关系,此文章对此精简和更具代码视觉的补充及说明如何合理使用。

例如有一个显示某用户的仓库列表视图。此外还有一个搜索列表功能。

image.png

Vue2 Options API

UserRepositories.vue

<template>
  <div>
    <RepositoriesSearch v-model="searchQuery" />
    <RepositoriesList :list="repositoriesMatchingSearchQuery" />
  </div>
</template>

<script>
import RepositoriesSearch from "./components/RepositoriesSearch.vue";
import RepositoriesList from "./components/RepositoriesList.vue";
import { fetchUserRepositories } from "./api/repositories";

export default {
  components: { RepositoriesSearch, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      repositories: [], // 1
      searchQuery: "" // 2
    };
  },
  computed: {
    repositoriesMatchingSearchQuery() {
      return this.repositories.filter(repository =>
        repository.name.includes(this.searchQuery)
      );
    } // 2
  },
  mounted() {
    this.getUserRepositories(); // 1
  },
  watch: {
    user: "getUserRepositories" // 1
  },
  methods: {
    async getUserRepositories() {
      this.repositories = await fetchUserRepositories(this.user); // 使用 `this.user` 获取用户仓库
    } // 1
  }
};
</script>
复制代码

Vue3 Composition API

第一步, 在setup中提取列表逻辑

UserRepositories.vue

<template>
  <div>
    <RepositoriesSearch v-model:value="searchQuery" />
    <RepositoriesList :list="repositoriesMatchingSearchQuery" />
  </div>
</template>

<script>
import RepositoriesSearch from "./components/RepositoriesSearch.vue";
import RepositoriesList from "./components/RepositoriesList.vue";
import { fetchUserRepositories } from "./api/repositories";
import { ref } from "vue";

export default {
  components: { RepositoriesSearch, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup(props) {
    const repositories = ref([]);
    const getUserRepositories = async () => {
      repositories.value = await fetchUserRepositories(props.user);
    };

    return {
      repositories,
      getUserRepositories // 返回的函数与方法的行为相同
    };
  },
  data() {
    return {
      searchQuery: "" // 2
    };
  },
  computed: {
    repositoriesMatchingSearchQuery() {
      return this.repositories.filter(repository =>
        repository.name.includes(this.searchQuery)
      );
    } // 2
  },
  mounted() {
    this.getUserRepositories(); // 1
  },
  watch: {
    user: "getUserRepositories" // 1
  }
};
</script>
复制代码

第二步,在setup中使用响应式porps、mounted、watch、compute

UserRepositories.vue

<template>
  <div>
    <RepositoriesSearch v-model:value="searchQuery" />
    <RepositoriesList :list="repositoriesMatchingSearchQuery" />
  </div>
</template>

<script>
import RepositoriesSearch from "./components/RepositoriesSearch.vue";
import RepositoriesList from "./components/RepositoriesList.vue";
import { fetchUserRepositories } from "./api/repositories";
import { ref, onMounted, toRefs, watch, computed } from "vue";

export default {
  components: { RepositoriesSearch, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup(props) {
    // 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
    const { user } = toRefs(props);

    const repositories = ref([]);
    const getUserRepositories = async () => {
      // 更新 `prop.user` 到 `user.value` 访问引用值
      repositories.value = await fetchUserRepositories(user);
    };

    onMounted(getUserRepositories); // 在 `mounted` 时调用 `getUserRepositories`

    // 在 user prop 的响应式引用上设置一个侦听器
    watch(user, getUserRepositories);

    const searchQuery = ref("");
    const repositoriesMatchingSearchQuery = computed(() => {
      return repositories.value.filter(repository =>
        repository.name.includes(searchQuery.value)
      );
    });

    return {
      repositories,
      getUserRepositories,
      searchQuery,
      repositoriesMatchingSearchQuery
    };
  }
};
</script>
复制代码

至此,已将原功能逻辑全部移植到setup中。但请不要怀疑它就是这样即 Vue一把梭。 一把梭是有它的优点的,它会使我们的逻辑更加集中,不至于像 OptionsAPI 那样逻辑间上下跳转才得以阅读。 如果不是第一个开发人员定会发懵。

如何更加合理的使用 Composition API(一把梭) 请往下看!

更合理使用 Composition API

如何合理,那我们就要把各个逻辑块独立封装提取,提取之后的方法体叫 组合式函数。 在项目中我们把它们放入约定的 composables 文件夹中

对上述两个逻辑单元进行封装提取如下:

composables/useUserRepositories.js

import { fetchUserRepositories } from "../api/repositories";
import { ref, onMounted, watch } from "vue";

export default function useUserRepositories(user) {
  const repositories = ref([]);
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(user.value);
  };

  onMounted(getUserRepositories);
  watch(user, getUserRepositories);

  return {
    repositories,
    getUserRepositories
  };
}
复制代码

composables/useRepositoryNameSearch.js

import { ref, computed } from "vue";

export default function useRepositoryNameSearch(repositories) {
  const searchQuery = ref("");
  const repositoriesMatchingSearchQuery = computed(() => {
    return repositories.value.filter(repository => {
      return repository.name.includes(searchQuery.value);
    });
  });

  return {
    searchQuery,
    repositoriesMatchingSearchQuery
  };
}
复制代码

之后的视图:

UserRepositories.vue

<template>
  <div>
    <RepositoriesSearch v-model:value="searchQuery" />
    <RepositoriesList :list="repositories" />
  </div>
</template>

<script>
import RepositoriesSearch from "./components/RepositoriesSearch.vue";
import RepositoriesList from "./components/RepositoriesList.vue";
import useUserRepositories from "./composables/useUserRepositories";
import useRepositoryNameSearch from "./composables/useRepositoryNameSearch";
import { toRefs } from "vue";

export default {
  components: { RepositoriesSearch, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup(props) {
    const { user } = toRefs(props);

    const { repositories, getUserRepositories } = useUserRepositories(user);

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories);

    return {
      // 因为我们并不关心未经过滤的仓库
      // 我们可以在 `repositories` 名称下暴露过滤后的结果
      repositories: repositoriesMatchingSearchQuery,
      getUserRepositories,
      searchQuery
    };
  }
};
</script>
复制代码

项目结构差异

v2项目结构

image.png

v3项目结构

image.png

总结

10年前, 一帮年轻人用着Sublime、HBiuder, 戴着有线耳机,梭着一手当下最流行的jQuery, 完全不顾中年人的感受! 10年后, 一帮中年人用着WebStrom、VSCode, 戴着无线耳机,梭着当下最流行的 组合式API,完全不顾年轻人的感受! 思来想去,其实这帮还是原班人马啊, 时光兜兜转转, 仿佛 jQuery 就在昨日!

image.png

扶我起来老夫还能继续梭!

参考文献

文章分类
前端
文章标签