vue项目中使用ts经验

你用ts干什么

  1. 跟风….
  2. 编写公用方法、全局配置、公用组件,提示调用者别传错参数。
  3. 使用类推推导,避免你拼写错,漏写等低级错误,提前发现问题。
  4. 提前定义类型,梳理开发思路,规划大大纲,避免后面开发偏离原有目标。

代码实战

api函数参数和返回 定义类型

对于这块,大概可以理解成是复制后端接口的入参和结果类型(结果只需要写上自己用得上的字段)

import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';

const instance = axios.create({
  baseURL: baseUrl,
  timeout: 30 * 1000,
  headers: {
    'Content-Type': 'application/json;charset=UTF-8',
  },
});

// 请求拦截器,一般是添加token, 格式化参数
instance.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    config.headers = {
      Authorization: getToken(),
      ...config.headers,
    };
    // 后端接收json数据
    config.data = JSON.stringify(config.data);
    return config;
  },
  (error) => {
    // 对请求错误做些什么
    return Promise.reject(error);
  },
);

// 响应拦截器,一般用于全局错误提示,特殊状态码判断跳转
service.interceptors.response.use((response: AxiosResponse) => {
  const { code, message, data } = response.data
 
  // 根据自定义错误码判断请求是否成功
  if (code === 0) {
    // 将组件用的数据返回
    return data
  } else {
    // 处理业务错误。
    Message.error(message)
    return Promise.reject(new Error(message))
  }
}, (error: AxiosError) => {
  // 处理 HTTP 网络错误
  let message = ''
  // HTTP 状态码401 403 404 500等状态处理
  const status = error.response?.status
  Message.error(message) 
  return Promise.reject(error)
})

// 接口统一数据结构
interface ApiResult<T> {
  code: number;
  hasError: boolean;
  message: null | string;
  result: T;
}

export default function request<T>(options: AxiosRequestConfig = {}) {
  return new Promise<T>((resolve, reject) => {
    instance(options)
      .then((res: AxiosResponse<ApiResult<T>>) => {
         resolve(res.data.result);
      })
      .catch((err) => {
        reject(err);
      })
  });
}

api文件ts应用

    import request from '@/utils/request';
    
    /* 登录接口参数类型 */
    export interface LoginData {
      username: string,
      password: string,
    }
 
    /* 登录接口返回值类型 */
    export interface LoginRes {
      token: string;
      username: string;
    }
    
    // 登录
    export const loginApi = (data: LoginData) => {
       return request<LoginRes>({ url: "/api/login", method: "GET", params: data });
    }
    

页面使用,就可以看到入参跟响应结构

vue项目中使用ts经验

vue项目中使用ts经验

vue项目中使用ts经验

vue-route 定义类型

  1. 定义跳转添加RouteRecordRaw类型
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";

const routes: RouteRecordRaw[] = [
  {
    path: "/",
    name: "home",
    meta: {
      isAuth: false,
    },
    component: () => import("@/views/home.vue"),
  },
  {
    path: "/detail",
    name: "detail",
    component: () => import("@/views/detail.vue"),
  },
  {
    path: "/list",
    name: "list",
    component: () => import("@/views/list.vue"),
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes: [],
});

router.addRoute({
  path: "/about",
  component: () => import("@/views/about.vue"),
});

const parseRouter = (arr: RouteRecordRaw[]) => {
  arr.forEach((item) => {
    router.addRoute(item);
  });
};

parseRouter(routes);
  1. 自定义meta类型, 在源码中有举例,每个项目的meta可能都不同

vue项目中使用ts经验

  • 添加router-meta.d.ts
project-root/
├─ src/
│  ├─ components/
│  ├─ views/
│  ├─ router/
│  └─ ...
├─ types/
│  └─ router-meta.d.ts
├─ tsconfig.json
└─ ...

// router-meta.d.ts
import 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    // 这些字段根据你的项目需求自定义
    requiresAuth?: boolean
    title?: string
    roles?: string[]
  }
}

  • tsconfig.json 引入
{
  "compilerOptions": {
    // 编译选项
  },
  "include": [
    "src/**/*", // 包含 src 目录下的所有文件
    "types/**/*" // 也包含 types 目录下的所有文件
  ]
}

这样就可以获取meta类型了

vue项目中使用ts经验

定义的时候也可以校验类型

vue项目中使用ts经验

vuex 定义类型

  1. 定义全局store.state类型

通过查看vuex的类型定义文件, 可以发现createStore支持泛型, 这个泛型S对应的就是state的类型

export function createStore<S>(options: StoreOptions<S>): Store<S>;

export interface StoreOptions<S> {
  state?: S | (() => S);
  getters?: GetterTree<S, S>;
  actions?: ActionTree<S, S>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<S>;
  plugins?: Plugin<S>[];
  strict?: boolean;
  devtools?: boolean;
}

那么就可以提前定义好state的类型,在编写state代码的时候就可以有类型提示了

import { createStore } from "vuex";

interface UserProps {
  userName: string;
  token: string;
  isLogin: boolean;
}

interface TemplateItem {
  id: number;
  name: string; 
  coverImg: string;
}

interface GlobalDataProps {
  user: UserProps;
  templates: TemplateItem[];
}

const store = createStore<GlobalDataProps>({
  state: {},
  getters: {},
  actions: {},
  mutations: {},
});

export default store;
  • 不写state的话vscode插件就会提示ts错误
    vue项目中使用ts经验
  • state类型补全
user templates
vue项目中使用ts经验 vue项目中使用ts经验
  1. 应用页面

如果是平时的写在js或template模板中无法进行类型推导

vue项目中使用ts经验

vue项目中使用ts经验

实现这个就注意useStore的泛型,ctrl+鼠标点击可以跳转查看文档

   export function useStore<S = any>(injectKey?: InjectionKey<Store<S>> | string): Store<S>;
  1. vux module 模块分割
export interface Module<S, R> {
  namespaced?: boolean;
  state?: S | (() => S);
  getters?: GetterTree<S, R>;
  actions?: ActionTree<S, R>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<R>;
}

模块下面的代码,需要添加Model进行泛型传递才能正常推荐,第一个为当前模块的state类型,后者为全局的

vue项目中使用ts经验

组件props 定义类型

  1. 组件内props定义类型,使用PropTyp传递泛型参数,使得模板获取类型推断

vue项目中使用ts经验
2. 在使用子组件的父组件中,判断传递的prop是否满足,子组件中defineProps使用泛型定义类型

export declare function defineProps<PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions>(props: PP): Readonly<ExtractPropTypes<PP>>;

export declare type ComponentObjectPropsOptions<P = Data> = {
    [K in keyof P]: Prop<P[K]> | null;
};

<template>
  <div class="">
    <div v-for="item in list" :key="item.id">{{ item.name }}</div>
  </div>
</template>

<script setup lang="ts">
import { defineProps } from "vue";
import type { TemplateItem } from "../store/templates";

interface TemplateProps {
  list: TemplateItem[];
  name: string;
}

defineProps<TemplateProps>();
</script>
<style lang="scss" scoped></style>

如果漏传,错传都会提示
vue项目中使用ts经验

vue项目中使用ts经验

日常开发ts报错…

定义一个空对象,但后面会赋值,模板上使用了,就会报错

vue项目中使用ts经验

处理这个只能断言了

vue项目中使用ts经验

vue项目中使用ts经验

dom类型错误

const onClick = (event: MouseEvent) => {
  const target = event.target as HTMLElement;
  console.log(target.tagName); // 访问 tagName 属性来检查元素类型

  // 具体 HTMLElement 子类
  // input 元素
  // const target = event.target as HTMLInputElement; // 如果你知道这是一个 input 元素,可以断言为 HTMLInputElement 类型
  // console.log(target.value); // 现在你可以访问 value 属性了
};

下面是一些常见的具体 HTMLElement 子类,它们代表了不同功能的 HTML 元素:

  • HTMLInputElement – 代表 <input> 元素,包含如 valuecheckedtype 等特有属性和方法。
  • HTMLSelectElement – 代表 <select> 元素,包含如 optionslengthselectedIndex 等特有属性和方法。
  • HTMLTextAreaElement – 代表 <textarea> 元素,包含如 valuerowscols 等特有属性和方法。
  • HTMLButtonElement – 代表 <button> 元素,包含值和状态相关的属性。
  • HTMLAnchorElement – 代表 <a>(锚)元素,包含如 hreftargetdownload 等特有属性和方法。
  • HTMLDivElement – 代表 <div> 元素。
  • HTMLImageElement – 代表 <img> 元素,包含如 srcaltwidthheight 等特有属性和方法。
  • HTMLFormElement – 代表 <form> 元素,包含如 submitreset 方法以及表单相关的属性和方法。

原文链接:https://juejin.cn/post/7314850529112129571 作者:三十五前端

(0)
上一篇 2023年12月22日 下午4:37
下一篇 2023年12月22日 下午4:48

相关推荐

发表回复

登录后才能评论