【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 示例项目

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

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

### Vue3 项目中 TypeScript 配置和使用 Vue Router 的方法 在 Vue3 和 TypeScript 结合使用的场景下,Vue Router 提供了强大的功能支持。以下是关于如何配置和使用 Vue Router 的详细说明。 #### 1. 创建 Vue3 + TypeScript 项目 为了确保项目的正常运行,首先需要通过 `@vue/cli` 工具创建一个新的 Vue3 项目,并启用 TypeScript 支持: ```bash npm install -g @vue/cli vue create my-vue3-typescript-project ``` 在交互式提示中选择 **Manually select features** 并勾选 **TypeScript** 和其他所需的功能模块[^4]。 #### 2. 安装 Vue Router 4 Vue Router 4 是专门为 Vue3 设计的版本,在安装之前需确认已卸载旧版 Vue CLI 及其依赖项: ```bash npm uninstall vue-cli -g npm install -g @vue/cli npm install vue-router@next --save ``` 上述命令将安装最新版本的 Vue Router (即 v4),并将其保存到项目依赖中。 #### 3. 配置 Vue Router 在 Vue3 中推荐使用组合 API (`setup`) 来初始化路由实例。下面是一个完整的示例代码展示如何定义路由以及集成至应用中: ##### 路由文件定义 (`src/router/index.ts`) ```typescript import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'; // 定义路由表 const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Home', component: () => import('@/views/Home.vue') // 动态加载视图组件 }, { path: '/about', name: 'About', component: () => import('@/views/About.vue') } ]; // 初始化路由器 export const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes, }); ``` 此部分利用了 TypeScript 类型推断机制自动识别 `RouteRecordRaw` 接口中的字段结构[^2]。 #### 4. 在主入口引入路由 修改 `main.ts` 文件以注册全局路由实例: ```typescript import { createApp } from 'vue'; import App from './App.vue'; import { router } from './router'; // 导入自定义路由配置 const app = createApp(App); app.use(router); app.mount('#app'); ``` 这样就完成了基础框架搭建工作[^1]。 #### 5. 组件内部调用导航逻辑 当希望基于用户操作触发页面切换时,可以借助于 `useRouter()` 方法实现无状态化管理方式: ```javascript <script lang="ts"> import { defineComponent } from 'vue'; import { useRouter } from 'vue-router'; export default defineComponent({ setup() { const router = useRouter(); const navigateToHomePage = (): void => { router.push('/'); }; return { navigateToHomePage }; } }); </script> ``` 以上片段展示了如何声明一个函数用于重定向回首页的操作流程。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值