当前位置: 首页 > news >正文

【TypeScript】ts在vue中的使用

目录

一、Vue 3 + TypeScript 

1. 项目创建与配置

项目创建

 关键配置文件

2.完整项目结构示例

3. 组件 Props 类型定义

4. 响应式数据与 Ref

5. Composition 函数复用

二、组件开发

1.组合式API(Composition API)

2.选项式API(Options API)

三、Vue 2 + TypeScript

1. 安装依赖

2. 类组件(Vue Class Component)

3.Vuex类型安全

四、状态管理(Pinia + TypeScript)

1. 定义 Store

2. 在组件中使用

五、高级类型操作

1.全局属性扩展

2. 为无类型库添加声明

3.泛型组件

4.使用 Vue Router

六、最佳实践

严格类型检查:

类型导入:

避免 any:

性能优化

七、常见问题

Q1:模板中的类型检查

Q2:处理$refs类型

Q3:动态路由类型(Vue  Router)

Q2: 如何处理模板中的类型检查?

八、学习资源


一、Vue 3 + TypeScript 

Vue 3 原生支持 TypeScript,推荐使用 <script setup> 语法糖和 Composition API。

1. 项目创建与配置
项目创建

使用 Vue CLI 或 Vite 创建支持 TypeScript 的 Vue 项目:

# 使用 Vite 创建 Vue + TS 项目(推荐)
npm create vite@latest my-vue-app -- --template vue-ts

# 进入项目并运行
cd my-vue-app
npm install
npm run dev

# 使用 Vue CLI(需全局安装 @vue/cli)
vue create my-vue-app
# 选择 "Manually select features" → 勾选 TypeScript
 关键配置文件

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "strict": true,                   // 启用严格模式
    "jsx": "preserve",                 // 支持 JSX(可选)
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]                 // 路径别名
    },
    "types": ["vite/client"]           // Vite 环境类型
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"]
}

vite.config.ts(vite项目)

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': '/src' // 路径别名
    }
  }
});
2.完整项目结构示例
src/
  components/
    Button.vue      // 基础组件
    GenericList.vue // 泛型组件
  stores/
    counter.ts      // Pinia Store
  types/
    global.d.ts     // 全局类型扩展
    api.d.ts        // API 响应类型
  views/
    Home.vue
  App.vue
  main.ts
  vite-env.d.ts
3. 组件 Props 类型定义
<script setup lang="ts">
// 定义 Props 类型
interface Props {
  title: string;
  count?: number;   // 可选参数
  isActive: boolean;
}

// 声明 Props(withDefaults 设置默认值)
const props = withDefaults(defineProps<Props>(), {
  count: 0,
  isActive: false
});

// 使用 Props
console.log(props.title);
</script>

<template>
  <div :class="{ active: props.isActive }">
    {{ title }} - {{ count }}
  </div>
</template>
4. 响应式数据与 Ref
<script setup lang="ts">
import { ref, computed } from 'vue';

// 定义响应式数据(自动推断类型)
const count = ref(0); // 类型为 Ref<number>

// 显式指定复杂类型
interface User {
  name: string;
  age: number;
}
const user = ref<User>({ name: 'Alice', age: 30 });

// 计算属性
const doubleCount = computed(() => count.value * 2);

// 函数
const increment = () => {
  count.value++;
};
</script>
5. Composition 函数复用
// composables/useCounter.ts
import { ref } from 'vue';

export default function useCounter(initialValue: number = 0) {
  const count = ref(initialValue);

  const increment = () => {
    count.value++;
  };

  return {
    count,
    increment
  };
}

// 在组件中使用
<script setup lang="ts">
import useCounter from '@/composables/useCounter';

const { count, increment } = useCounter(10);
</script>

二、组件开发

1.组合式API(Composition API)
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';

// Props 类型定义
interface Props {
  title: string;
  initialCount?: number;
}
const props = withDefaults(defineProps<Props>(), {
  initialCount: 0
});

// 响应式数据
const count = ref(props.initialCount);
const doubleCount = computed(() => count.value * 2);

// 事件触发
const emit = defineEmits<{
  (e: 'count-change', newCount: number): void;
}>();

// 方法
const increment = () => {
  count.value++;
  emit('count-change', count.value);
};

// 生命周期
onMounted(() => {
  console.log('Component mounted');
});
</script>

<template>
  <div>
    <h2>{{ title }}</h2>
    <p>Count: {{ count }}, Double: {{ doubleCount }}</p>
    <button @click="increment">+1</button>
  </div>
</template>
2.选项式API(Options API)
<script lang="ts">
import { defineComponent } from 'vue';

interface State {
  message: string;
  count: number;
}

export default defineComponent({
  props: {
    title: {
      type: String,
      required: true
    }
  },
  data(): State {
    return {
      message: 'Hello Vue!',
      count: 0
    };
  },
  computed: {
    reversedMessage(): string {
      return this.message.split('').reverse().join('');
    }
  },
  methods: {
    increment() {
      this.count++;
    }
  }
});
</script>




三、Vue 2 + TypeScript

Vue 2 需通过 vue-class-component 或 vue-property-decorator 增强类型支持。

1. 安装依赖
npm install vue-class-component vue-property-decorator --save
2. 类组件(Vue Class Component)
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="increment">Count: {{ count }}</button>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  // Props
  @Prop({ type: String, required: true }) readonly title!: string;
  @Prop({ default: 0 }) readonly initialCount!: number;

  // 数据
  private count: number = this.initialCount;

  // 计算属性
  get message(): string {
    return `${this.title}: ${this.count}`;
  }

  // 方法
  increment() {
    this.count++;
  }
}
</script>
3.Vuex类型安全
// store/modules/user.ts
import { Module } from 'vuex';

interface UserState {
  name: string;
  age: number;
}

const userModule: Module<UserState, RootState> = {
  namespaced: true,
  state: () => ({
    name: 'Alice',
    age: 30
  }),
  mutations: {
    SET_NAME(state, payload: string) {
      state.name = payload;
    }
  },
  actions: {
    updateName({ commit }, newName: string) {
      commit('SET_NAME', newName);
    }
  },
  getters: {
    fullInfo: (state) => `${state.name} (${state.age})`
  }
};

四、状态管理(Pinia + TypeScript)

Pinia 是 Vue 官方推荐的状态管理库,天然支持 TypeScript。

1. 定义 Store

// stores/counter.ts
import { defineStore } from 'pinia';

interface CounterState {
  count: number;
  lastUpdated?: Date;
}

export const useCounterStore = defineStore('counter', {
  state: (): CounterState => ({
    count: 0,
    lastUpdated: undefined
  }),
  actions: {
    increment() {
      this.count++;
      this.lastUpdated = new Date();
    }
  },
  getters: {
    doubleCount: (state) => state.count * 2
  }
});
2. 在组件中使用
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter';

const counterStore = useCounterStore();

// 直接修改状态
counterStore.count++;

// 通过 action 修改
counterStore.increment();

// 使用 getter
console.log(counterStore.doubleCount);
</script>
 

五、高级类型操作

1.全局属性扩展
// src/types/global.d.ts
import { ComponentCustomProperties } from 'vue';

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $formatDate: (date: Date) => string; // 全局方法
    $api: typeof import('./api').default; // 全局 API 实例
  }
}

// main.ts 中注入
app.config.globalProperties.$formatDate = (date: Date) => date.toLocaleString();
2. 为无类型库添加声明

创建 src/types/shims.d.ts

declare module 'untyped-vue-library' {
  export function doSomething(config: any): void;
}
3.泛型组件
<script setup lang="ts" generic="T">
import { ref } from 'vue';

interface GenericListProps<T> {
  items: T[];
  keyField: keyof T;
}

const props = defineProps<GenericListProps<T>>();

const selectedItem = ref<T>();
</script>

<template>
  <ul>
    <li 
      v-for="item in items" 
      :key="item[keyField]"
      @click="selectedItem = item"
    >
      {{ item }}
    </li>
  </ul>
</template>
4.使用 Vue Router
// router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue')
  }
];

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

export default router;

六、最佳实践

严格类型检查

在 tsconfig.json 中启用严格模式

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": false // 可逐步开启
  }
}
类型导入

优先使用 TypeScript 类型导入:

import type { Router } from 'vue-router';
避免 any

尽量使用精确类型,仅在紧急情况下使用 any

性能优化

1.类型安全的异步组件

// 显式定义加载的组件类型
const AsyncModal = defineAsyncComponent({
  loader: () => import('./Modal.vue'),
  loadingComponent: LoadingSpinner,
  delay: 200
});

2.避免不必要的类型断言

// Bad: 过度使用 as
const data = response.data as User[];

// Good: 使用类型守卫
function isUserArray(data: any): data is User[] {
  return Array.isArray(data) && data.every(item => 'id' in item);
}
if (isUserArray(response.data)) {
  // 安全使用 response.data
}

七、常见问题

Q1:模板中的类型检查

安装Volar VS Code插件

禁用Vetur(避免冲突)

Q2:处理$refs类型
// main.ts
import { createApp } from 'vue';

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $myGlobal: string;
  }
}

const app = createApp(App);
app.config.globalProperties.$myGlobal = 'hello';
Q3:动态路由类型(Vue  Router)
// router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean;
  }
}

const routes: RouteRecordRaw[] = [
  {
    path: '/user/:id',
    component: () => import('@/views/User.vue'),
    props: (route) => ({ id: Number(route.params.id) }) // 类型转换
  }
];

Q3: 如何为全局属性添加类型?

在 Vue 3 中:

// main.ts
import { createApp } from 'vue';

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $myGlobal: string;
  }
}

const app = createApp(App);
app.config.globalProperties.$myGlobal = 'hello';
Q2: 如何处理模板中的类型检查?

Vue 3 的 Volar 插件(替代 Vetur)提供模板内类型检查,需在 VS Code 中安装。

八、学习资源

  1. Vue 3 + TypeScript 官方指南

  2. Pinia 官方文档

  3. Vue 3 TypeScript 示例项目

本文有待更新,暂时不为最终版本

码字不易,各位大佬点点赞

相关文章:

  • 电脑显示屏亮度怎么调?电脑屏幕亮度调节步骤介绍
  • laravel11设置中文语言包
  • 技术架构和工程架构区别
  • 【论文解读】《C-Pack: Packed Resources For General Chinese Embeddings》
  • 深入探讨K8s资源管理和性能优化
  • 深入解析 Spring 中的 BeanDefinition 和 BeanDefinitionRegistry
  • 链表理论基础
  • Java并发编程面试题:并发工具类(10题)
  • [2/11]C#性能优化-不要使用空析构函数-每个细节都有示例代码
  • Windows和AD域提权枚举脚本及工具介绍
  • python-leetcode-乘积最大子数组
  • 【CI/CD】Jenkins + Docker +SpringCloud微服务项目持续集成
  • 项目总结nk
  • Android ObjectBox数据库使用与集成指南
  • Doris系列之基础使用
  • Magma:多模态 AI 智体的基础模型
  • DeepSeek 助力 Vue3 开发:打造丝滑的下拉选择框(Dropdown Select)
  • 数据结构——基于单链表创建通讯录
  • LangChain教程 - RAG - PDF问答
  • HarmonyOS+Django实现图片上传
  • 220名“特朗普币”持有者花1.48亿美元,获邀与特朗普共进晚餐
  • 中美是否计划讨论美方以芬太尼为由对华征收的特别关税?外交部回应
  • 民企老板被错羁212天续:申请国赔千万余元,要求恢复名誉赔礼道歉
  • 中美会谈前都发生了什么?美方为何坐不住了?
  • 汉斯·季默:不会指挥的声音工程师终成音乐“大神”
  • 5天完成1000多万元交易额,“一张手机膜”畅销海内外的启示