Vue3结合TS项目开发实践:Composition API的风格理念、关注点分离、如何组织TS进行项目开发(采用声明文件来管理接口及所需类型/目录结构推荐)

一、composition Api

  compositon Api的本质体现在代码里面,也就是一个setup函数,在这个setup函数中,返回的数据,会用到该组件的模板中。return的这个对象,一定程度上,代表了之前vue2中的data属性。

  这时候,对于大多数初学者来说,可能存在的疑惑就是,那么我能不能定义options Api的写法,比如data、computed、watch、methods等等。这里我需要明确的是,Vue3是完全兼容Vue2的这种options Api的写法,但是从理念上来说,更加推荐setup的方式,来写我们的组件。

  原因如下:Vue3的存在,本身是为了解决Vue2的问题的,Vue2的问题就是在于,聚合性不足,会导致代码越来越臃肿!setup的方式,能够让data、方法逻辑、依赖关系等聚合在一块,更方便维护。

  也就是说,以后我们尽量不要写单独的data、computed、watch、methods等等,不是Vue3不支持,而是和Vue3的理念违背

  components属性,也就是一个组件的子组件,这个配置在Vue2和3的差异不大,Vue2怎么用,Vue3依然那么用。

  缺点:从options api切换到composition api最大的问题就是没有强制的代码分区,如果书写的人没有很好的代码习惯,那么后续的人将会看的十分难受。所以在写代码时需要注意做到很好的“关注点分离”:

1、自我代码分区并且尽量抽离方法(写好注释),分区如下:

(1)相关引入

(2)响应式数据、props、emit 定义

(3)生命周期以及 watch 书写

(4)方法定义

(5)方法、属性暴露

2、组件抽离:将页面拆成两个文件夹,一个为 views,一个为 components

  views 和 components 文件夹下有各自的文件。views 文件夹中为页面入口,掌管数据,而 components 则为页面中一些组件抽离。如果是公共组件,再抽离到 components 文件夹下其他位置。

3、hook 抽离:尽可能将逻辑抽离,并不一定要进行复用。

二、关注点分离

  关注点分离,应该分两层意思:第一层意思就是,Vue3的setup,本身就把相关的数据,处理逻辑放到一起,这就是一种关注点的聚合,更方便我们看业务代码。

  第二层意思,就是当setup变的更大的时候,我们可以在setup内部,提取相关的一块业务,做到第二层的关注点分离

import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { defineComponent, ref, computed } from 'vue';
import useMerchantList from './merchant.js';
export default defineComponent({
    name: 'Gift',
    setup() {
        const counter = ref(0);
        const router = useRouter();   // router 的使用
        const onClick = () => {
            router.push({ name: "AddGift" });
        }
        // 在该示例中,我们把获取商家列表的相关业务分离出去。也就是下面的merchant.ts
        const {merchantList} = useMerchantList();
        return {
            counter,
            onClick,
            merchantList
        }
    }
})

// merchant.ts
import { getMerchantlist } from "@/api/rights/gift";
import { ref, onMounted } from "vue";
export default function useMerchantList(): Record<string, any> {
  const merchantList = ref([]);
  const fetchMerchantList = async () => {
    let res = await getMerchantlist({});
    merchantList.value = res?.data?.child;
  };
  onMounted(fetchMerchantList);  // 周期函数的使用
  return {
    merchantList
  };
}

三、TypeScript支持

  TS与Vue3项目开发息息相关,所以真的想用Vue3,我们还是得了解TS的使用。下面我们主要了解在业务场景中如何组织TS。

  使用TS进行业务开发,一个核心的思维是,先关注数据结构,再根据数据结构进行页面开发。以前的前端开发模式是,先写页面,后关注数据。

  比如要写一个礼品列表的页面,我们可能要定义这么一些interface。总而言之,我们需要关注的是:页面数据的interface、接口返回的数据类型、接口的入参类型等等。

// 礼品创建、编辑、列表中的每一项,都会是这个数据类型。
interface IGiftItem {
  id: string | number;
  name: string;
  desc: string;
  [key: string]: any;
}
// 全局相应的类型定义
// 而且一般来说,我们不确认,接口返回的类型到底是什么(可能是null、可能是对象、也可能是数组)
所以使用范型来定义interface
interface IRes<T> { code: number; msg: string; data: T } // 接口返回数据类型定义 interface IGiftInfo { list: Array<IGiftItem>; pageNum: number; pageSize: number; total: number; }

  在一个常见的接口请求中,我们一般使用TS这么定义一个数据请求,数据请求的req类型,数据请求的res类型

export const getGiftlist = (
  params: Record<string, any>
): Promise<IRes<IGiftInfo>> => {
  return Http.get("/apis/gift/list", params);
};

  目录结构:在你编写大型前端项目时,推荐使用声明文件(Declaration Files)来管理接口或其他自定义类型。声明文件一般是 <module_name>.d.ts 的形式,在这类文件中只定义模块中的类型,没有任何实际的实现逻辑。声明文件可以单独放在一个目录里,我喜欢命名为 interfaces,意思就是接口。这样,就可以充分将抽象类型、方法、属性等与实际内容分开。以下例子是一个集成了 TS 的 Vue 项目目录。

.
├── babel.config.js   // Babel 编译配置文件
├── jest.config.ts   // 单元测试配置文件
├── package.json    // 项目配置文件
├── public       // 公共资源
├── src         // 源代码目录
│   ├── App.vue     // 主应用
│   ├── assets     // 静态资源
│   ├── components   // 组件
│   ├── constants    // 常量
│   ├── i18n      // 国际化
│   ├── interfaces   // 声明文件目录
│   │   ├── components  // 组件声明
│   │   ├── index.d.ts  // 主声明
│   │   ├── layout   // 布局声明
│   │   ├── store    // 状态管理声明
│   │   └── views    // 页面声明
│   ├── layouts     // 布局
│   ├── main.ts     // 主入口
│   ├── router     // 路由
│   ├── shims-vue.d.ts // 兼容 Vue 声明文件
│   ├── store      // 状态管理
│   ├── styles     // CSS/SCSS 样式
│   ├── test      // 测试
│   ├── utils      // 公共方法
│   └── views      // 页面
└── tsconfig.json    // TS 配置文件

  其中可以看到,interfaces 这个目录跟其他模块在同一级,而其子目录则是其他模块所对应的类型声明。编写代码前,尽量先创建并设计声明文件内容,设计好之后再到实际的模块下完成实现。当然,这个 “定义 -> 实现“ 是一个不断迭代的过程,可能实现过程中发现有类型设计问题,可以回到声明文件中完善定义,再到实现代码里进行优化。

posted @ 2021-10-17 22:33  古兰精  阅读(2487)  评论(0编辑  收藏  举报