目录
一、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 中安装。
八、学习资源
本文有待更新,暂时不为最终版本
码字不易,各位大佬点点赞