vue项目中使用ts经验

你用ts干什么

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

代码实战

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

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

javascript 复制代码
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应用

javascript 复制代码
    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-route 定义类型

  1. 定义跳转添加RouteRecordRaw类型
javascript 复制代码
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可能都不同
  • 添加router-meta.d.ts
javascript 复制代码
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 引入
json 复制代码
{
  "compilerOptions": {
    // 编译选项
  },
  "include": [
    "src/**/*", // 包含 src 目录下的所有文件
    "types/**/*" // 也包含 types 目录下的所有文件
  ]
}

这样就可以获取meta类型了

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

vuex 定义类型

  1. 定义全局store.state类型

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

javascript 复制代码
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代码的时候就可以有类型提示了

ts 复制代码
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错误
  • state类型补全
user templates
  1. 应用页面

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

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

javascript 复制代码
   export function useStore<S = any>(injectKey?: InjectionKey<Store<S>> | string): Store<S>;
  1. vux module 模块分割
javascirpt 复制代码
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类型,后者为全局的

组件props 定义类型

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

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

javascirpt 复制代码
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>

如果漏传,错传都会提示

日常开发ts报错...

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

处理这个只能断言了

dom类型错误

javascript 复制代码
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> 元素,包含如 value、checked、type 等特有属性和方法。
  • HTMLSelectElement - 代表 <select> 元素,包含如 options、length、selectedIndex 等特有属性和方法。
  • HTMLTextAreaElement - 代表 <textarea> 元素,包含如 value、rows、cols 等特有属性和方法。
  • HTMLButtonElement - 代表 <button> 元素,包含值和状态相关的属性。
  • HTMLAnchorElement - 代表 <a>(锚)元素,包含如 href、target、download 等特有属性和方法。
  • HTMLDivElement - 代表 <div> 元素。
  • HTMLImageElement - 代表 <img> 元素,包含如 src、alt、width、height 等特有属性和方法。
  • HTMLFormElement - 代表 <form> 元素,包含如 submit、reset 方法以及表单相关的属性和方法。
相关推荐
定栓4 分钟前
Webpack迁到Vite第一节:模块系统迁移:require → import
运维·前端·webpack
小熊在忙fpga14 分钟前
N32G430芯片的flash表示
前端·javascript·html
Data analyse45619 分钟前
App埋点版本兼容的字段迁移校验
大数据·运维·服务器·前端·数据库·数据分析
todoitbo26 分钟前
本来只想给内网开个口子,Natapp 却多给了一道防护
前端·javascript·vue.js·内网穿透·网络防护·natapp
会灭火的程序员31 分钟前
SuperMap AgentX Server智能体接入WEB应用技术文档
前端·supermap·agentx·agentx server
CRMEB36 分钟前
商品 SKU 设计,从简单规格到多规格踩过什么
java·前端·数据库·开源
Chloe.Zz37 分钟前
一篇入门Agent
开发语言·前端·javascript
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - HTML5 表格标签
前端·html·html5
雪芽蓝域zzs1 小时前
JS 项目转 TS 项目(Vue3 + 若依这类后台项目)完整改造清单
前端·vue.js