前端Skill全家桶:React+Vue+TypeScript开发实战
从组件设计到工程化落地,一文带你打通前端三大核心技术栈 ✨
开头碎碎念 👋
宝子们好呀!今天来聊一个超级硬核但又超级实用的话题------前端Skill全家桶!🔥
说实话,最近在项目里用到了React、Vue和TypeScript的组合拳,真的是越用越香 😊。很多小伙伴私信问我:"前端三大件到底怎么搭配使用?""TypeScript在前端项目里怎么落地?"------别急,今天这篇文章就是专门给你们准备的!
先碎碎念几句:前端技术栈更新太快了,React 19刚出、Vue 3.4也更新了、TypeScript 5.x又加了新特性......但是万变不离其宗,核心思想搞懂了,学什么都快 💪
好啦,言归正传,咱们开始吧!🚀
一、为什么需要前端Skill全家桶?🤔
很多同学可能会问:我学好一个框架不就行了吗?为什么还要搞React+Vue+TypeScript三件套?
这里我说几个现实场景:
- 公司技术栈切换:今天公司用Vue,明天被收购了要迁移到React,你不会就尴尬了 😅
- 开源项目贡献:GitHub上优质项目React和Vue各占半壁江山,只会一个就少了一半的机会
- 技术选型能力:只有都了解过,才能在架构设计时做出最优选择
- TypeScript是刚需:2024年了,不用TS的前端项目真的越来越少了
所以呀,掌握这三者的核心用法和最佳实践,是每个进阶前端工程师的必修课 ⭐
二、TypeScript:前端开发的基石 🏗️
2.1 为什么TypeScript这么重要?
宝宝们听我说,TypeScript不是"可选的加分项",而是"必须掌握的基本功"✨
typescript
// 没有TS的时候,这种bug可能要运行时才发现 😱
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}
// 有了TS,类型错误在写代码的时候就能捕获
interface Product {
id: string;
name: string;
price: number;
stock: number;
tags?: string[]; // 可选属性
}
// 定义一个购物车项的类型
interface CartItem {
product: Product;
quantity: number;
discount?: number;
}
// 类型安全的购物车计算
class ShoppingCart {
private items: CartItem[] = [];
addItem(product: Product, quantity: number, discount?: number): void {
if (quantity <= 0) {
throw new Error("数量必须大于0哦 🤔");
}
if (product.stock < quantity) {
throw new Error(`库存不足!当前库存:${product.stock}`);
}
this.items.push({ product, quantity, discount });
}
getTotal(): number {
return this.items.reduce((total, item) => {
const itemTotal = item.product.price * item.quantity;
const discount = item.discount || 0;
return total + itemTotal * (1 - discount);
}, 0);
}
getSummary(): { totalItems: number; totalPrice: number } {
return {
totalItems: this.items.reduce((sum, item) => sum + item.quantity, 0),
totalPrice: this.getTotal(),
};
}
}
2.2 泛型实战:写出可复用的工具函数
接下来看看泛型怎么用,这个在前端项目里超级常见 💡
typescript
// 通用的API请求封装
interface ApiResponse<T> {
code: number;
message: string;
data: T;
timestamp: number;
}
// 通用的分页数据结构
interface PaginatedData<T> {
list: T[];
total: number;
page: number;
pageSize: number;
}
// 泛型请求函数
async function fetchApi<T>(url: string, options?: RequestInit): Promise<ApiResponse<T>> {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
...options,
});
if (!response.ok) {
throw new Error(`请求失败: ${response.status}`);
}
return response.json();
}
// 使用示例
interface User {
id: string;
name: string;
email: string;
avatar: string;
}
// 类型安全的数据获取
const userData = await fetchApi<User[]>('/api/users');
const paginatedUsers = await fetchApi<PaginatedData<User>>('/api/users?page=1');
看到没?有了泛型,API请求的类型安全就有了保障,再也不用 any 到处飞了 🎉
三、React实战:组件化开发的艺术 🎨
3.1 React 19 + TypeScript 项目搭建
那么,咱们先来搭一个现代化的React项目 ⚡
bash
# 使用Vite创建React+TS项目
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm install
npm run dev
3.2 自定义Hook:封装业务逻辑
React最强大的地方之一就是自定义Hook,让我们把复杂逻辑抽离出来 🔥
tsx
// useFetch.ts - 通用数据请求Hook
import { useState, useEffect, useCallback } from 'react';
interface UseFetchState<T> {
data: T | null;
loading: boolean;
error: string | null;
}
interface UseFetchReturn<T> extends UseFetchState<T> {
refetch: () => void;
}
function useFetch<T>(url: string): UseFetchReturn<T> {
const [state, setState] = useState<UseFetchState<T>>({
data: null,
loading: true,
error: null,
});
const fetchData = useCallback(async () => {
setState(prev => ({ ...prev, loading: true, error: null }));
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data: T = await response.json();
setState({ data, loading: false, error: null });
} catch (err) {
setState({
data: null,
loading: false,
error: err instanceof Error ? err.message : '未知错误',
});
}
}, [url]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { ...state, refetch: fetchData };
}
// 使用示例:用户列表组件
function UserList() {
const { data: users, loading, error, refetch } = useFetch<User[]>('/api/users');
if (loading) return <div className="loading">加载中... ⏳</div>;
if (error) return <div className="error">出错了: {error} 😱</div>;
return (
<div className="user-list">
<h2>用户列表 👥</h2>
<button onClick={refetch}>刷新 🔄</button>
<ul>
{users?.map(user => (
<li key={user.id}>
<img src={user.avatar} alt={user.name} />
<span>{user.name}</span>
<span>{user.email}</span>
</li>
))}
</ul>
</div>
);
}
3.3 组件设计模式:复合组件
接下来给大家展示一个高级但非常实用的模式------复合组件模式 💡
tsx
// Card复合组件
interface CardProps {
children: React.ReactNode;
className?: string;
}
function Card({ children, className = '' }: CardProps) {
return <div className={`card ${className}`}>{children}</div>;
}
Card.Header = function Header({ children, title }: { children?: React.ReactNode; title: string }) {
return (
<div className="card-header">
<h3>{title}</h3>
{children}
</div>
);
};
Card.Body = function Body({ children }: { children: React.ReactNode }) {
return <div className="card-body">{children}</div>;
};
Card.Footer = function Footer({ children }: { children: React.ReactNode }) {
return <div className="card-footer">{children}</div>;
};
// 使用起来超级优雅 ✨
function UserProfile() {
return (
<Card className="profile-card">
<Card.Header title="用户信息">
<button>编辑 ✏️</button>
</Card.Header>
<Card.Body>
<p>姓名:张三</p>
<p>职位:前端工程师</p>
</Card.Body>
<Card.Footer>
<button>保存 💾</button>
<button>取消</button>
</Card.Footer>
</Card>
);
}
四、Vue 3实战:优雅的响应式编程 🖼️
4.1 Vue 3 + TypeScript组合式API
好啦,React看完了,咱们再来看看Vue 3的写法 🎯
bash
# 使用Vite创建Vue3+TS项目
npm create vite@latest my-vue-app -- --template vue-ts
cd my-vue-app
npm install
npm run dev
4.2 组合式函数(Composables)
Vue 3的Composables和React的Custom Hooks有异曲同工之妙,但写法更优雅 😊
typescript
// composables/useCounter.ts
import { ref, computed } from 'vue';
export function useCounter(initialValue: number = 0) {
const count = ref<number>(initialValue);
const doubleCount = computed(() => count.value * 2);
const isEven = computed(() => count.value % 2 === 0);
function increment(): void {
count.value++;
}
function decrement(): void {
count.value--;
}
function reset(): void {
count.value = initialValue;
}
function incrementBy(amount: number): void {
count.value += amount;
}
return {
count,
doubleCount,
isEven,
increment,
decrement,
reset,
incrementBy,
};
}
vue
<!-- Counter.vue -->
<template>
<div class="counter">
<h2>计数器 🔢</h2>
<p class="count">{{ count }}</p>
<p class="double">双倍值:{{ doubleCount }}</p>
<p :class="{ even: isEven, odd: !isEven }">
{{ isEven ? '偶数 ✅' : '奇数 ⭕' }}
</p>
<div class="buttons">
<button @click="decrement">-1</button>
<button @click="increment">+1</button>
<button @click="incrementBy(5)">+5</button>
<button @click="reset">重置 🔄</button>
</div>
</div>
</template>
<script setup lang="ts">
import { useCounter } from '@/composables/useCounter';
const {
count,
doubleCount,
isEven,
increment,
decrement,
reset,
incrementBy,
} = useCounter(0);
</script>
4.3 Pinia状态管理实战
Vue 3官方推荐的状态管理方案Pinia,用起来真的太丝滑了 🚀
typescript
// stores/userStore.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user' | 'guest';
}
export const useUserStore = defineStore('user', () => {
const users = ref<User[]>([]);
const currentUser = ref<User | null>(null);
const loading = ref(false);
const adminUsers = computed(() =>
users.value.filter(user => user.role === 'admin')
);
const totalUsers = computed(() => users.value.length);
const isLoggedIn = computed(() => currentUser.value !== null);
async function fetchUsers(): Promise<void> {
loading.value = true;
try {
const response = await fetch('/api/users');
users.value = await response.json();
} finally {
loading.value = false;
}
}
function login(user: User): void {
currentUser.value = user;
localStorage.setItem('currentUser', JSON.stringify(user));
}
function logout(): void {
currentUser.value = null;
localStorage.removeItem('currentUser');
}
return {
users, currentUser, loading,
adminUsers, totalUsers, isLoggedIn,
fetchUsers, login, logout,
};
});
五、React vs Vue:如何选择?⚖️
很多宝子们最纠结的问题来了:到底选React还是Vue?我给大家一个对比表 👀
| 对比维度 | React | Vue |
|---|---|---|
| 学习曲线 | 中等偏上 | 较低,上手快 |
| 生态规模 | 超大,社区活跃 | 大,中文生态好 |
| TypeScript支持 | 原生支持,体验好 | Vue 3支持优秀 |
| 状态管理 | Redux/Zustand/Jotai | Pinia(官方推荐) |
| 适用场景 | 大型复杂应用 | 中小型到大型均可 |
| 就业市场 | 需求量大 | 国内需求旺盛 |
我的建议💡:
- 如果你是新手入门,推荐先学Vue,上手快、成就感强
- 如果你想进大厂,React是必备技能
- 如果你想技术全面,两个都学!先学一个,再学第二个会很快
六、实战案例:打造一个Todo App 📝
最后,咱们用一个完整的Todo App来串联所有知识点 🔥
tsx
// React + TypeScript 版本的Todo App
import { useState, useCallback, useMemo } from 'react';
interface Todo {
id: string;
text: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
createdAt: number;
}
type FilterType = 'all' | 'active' | 'completed';
function TodoApp() {
const [todos, setTodos] = useState<Todo[]>([]);
const [inputText, setInputText] = useState('');
const [filter, setFilter] = useState<FilterType>('all');
const addTodo = useCallback(() => {
if (!inputText.trim()) return;
const newTodo: Todo = {
id: crypto.randomUUID(),
text: inputText.trim(),
completed: false,
priority: 'medium',
createdAt: Date.now(),
};
setTodos(prev => [newTodo, ...prev]);
setInputText('');
}, [inputText]);
const toggleTodo = useCallback((id: string) => {
setTodos(prev =>
prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
}, []);
const deleteTodo = useCallback((id: string) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
}, []);
const filteredTodos = useMemo(() => {
switch (filter) {
case 'active': return todos.filter(t => !t.completed);
case 'completed': return todos.filter(t => t.completed);
default: return todos;
}
}, [todos, filter]);
const stats = useMemo(() => ({
total: todos.length,
completed: todos.filter(t => t.completed).length,
active: todos.filter(t => !t.completed).length,
}), [todos]);
return (
<div className="todo-app">
<h1>📝 Todo App</h1>
<div className="input-group">
<input
value={inputText}
onChange={e => setInputText(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addTodo()}
placeholder="添加新任务..."
/>
<button onClick={addTodo}>添加 ➕</button>
</div>
<div className="filters">
{(['all', 'active', 'completed'] as FilterType[]).map(f => (
<button
key={f}
className={filter === f ? 'active' : ''}
onClick={() => setFilter(f)}
>
{f === 'all' ? '全部' : f === 'active' ? '进行中' : '已完成'}
</button>
))}
</div>
<div className="stats">
<span>总计: {stats.total}</span>
<span>已完成: {stats.completed}</span>
<span>进行中: {stats.active}</span>
</div>
<ul className="todo-list">
{filteredTodos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<input type="checkbox" checked={todo.completed}
onChange={() => toggleTodo(todo.id)} />
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>删除 🗑️</button>
</li>
))}
</ul>
</div>
);
}
七、总结与展望 🌟
好啦,今天的分享就到这里啦!咱们回顾一下重点内容 📋
- TypeScript是前端开发的基石,类型安全能帮你避免大量bug
- React的Hooks和组件模式让逻辑复用变得优雅
- Vue 3的组合式API和Pinia提供了简洁的开发体验
- 两个框架各有优势,建议都学,技多不压身 💪
接下来大家可以尝试:
- 用React+TS重构一个现有项目
- 用Vue 3写一个完整的CRUD应用
- 对比两个框架在同一个项目中的不同实现
希望对大家有帮助!如果觉得有用的话,记得点赞收藏哦 ⭐🎉
有问题欢迎在评论区留言,我会一一回复的~下次见啦 👋😊