Skip to main content

现代前端开发全栈指南

现代前端开发已经从简单的页面制作演进为复杂的工程化体系,涵盖了框架选型、架构设计、性能优化、工程化工具链等多个维度。本指南将深入解析现代前端开发的核心技术和最佳实践,帮助开发者构建高质量的前端应用。

核心价值

现代前端 = 组件化架构 + 工程化工具链 + 性能优化 + 用户体验

  • 🎯 组件化架构:可复用、可维护的组件体系
  • 🛠️ 工程化工具链:自动化构建、测试、部署流程
  • 性能优化:首屏加载、运行时性能、用户体验优化
  • 🎨 现代化UI:响应式设计、交互动效、无障碍访问
  • 🔧 开发体验:热更新、类型检查、调试工具
  • 🌐 跨平台能力:Web、移动端、桌面端统一开发

1. 前端技术栈全景图

1.1 技术栈架构层次

现代前端技术栈可以分为多个层次,每个层次都有其特定的职责和技术选型。

技术选型对比矩阵

技术类别主流方案优势适用场景学习成本
前端框架React生态丰富、灵活性高大型应用、复杂交互⭐⭐⭐
Vue学习曲线平缓、文档完善中小型项目、快速开发⭐⭐
Angular企业级、完整解决方案大型企业应用⭐⭐⭐⭐
状态管理Redux Toolkit可预测、时间旅行调试复杂状态逻辑⭐⭐⭐
Zustand轻量级、简单易用中小型应用⭐⭐
PiniaVue生态、组合式APIVue项目⭐⭐
构建工具Vite快速冷启动、HMR现代项目首选⭐⭐
Webpack成熟稳定、插件丰富复杂配置需求⭐⭐⭐⭐
CSS方案Tailwind CSS原子化、高度可定制快速原型、设计系统⭐⭐⭐
CSS Modules作用域隔离、零运行时组件化开发⭐⭐
Styled ComponentsCSS-in-JS、动态样式React生态⭐⭐⭐
发生命周期

现代前端开发遵循完整的软件开发生命周期,每个阶段都有相应的工具和最佳实践。

需求分析与技术选型

技术选型决策因素

  • 团队技能:团队对技术栈的熟悉程度
  • 项目规模:小型、中型、大型项目的不同需求
  • 性能要求:首屏加载、运行时性能指标
  • 维护成本:长期维护和扩展的便利性
  • 生态系统:第三方库、工具链的完善程度

2. React生态系统深度解析

2.1 React 18+ 新特性与最佳实践

React 18引入了并发特性、自动批处理、Suspense改进等重要更新,为构建高性能应用提供了更强大的能力。

并发渲染与Suspense

React 18并发特性示例
typescript
1import { Suspense, lazy, startTransition, useDeferredValue } from 'react';
2
3// 1. 代码分割与懒加载
4const LazyComponent = lazy(() => import('./LazyComponent'));
5
6// 2. 并发渲染组件
7function SearchResults({ query }: { query: string }) {
8 // 延迟值,降低搜索输入的优先级
9 const deferredQuery = useDeferredValue(query);
10
11 return (
12 <div>
13 <h2>搜索结果</h2>
14 <Suspense fallback={<SearchSkeleton />}>
15 <SearchList query={deferredQuery} />
16 </Suspense>
17 </div>
18 );
19}
20
21// 3. 使用startTransition标记非紧急更新
22function SearchInput() {
23 const [query, setQuery] = useState('');
24 const [isPending, startTransition] = useTransition();
25
26 const handleSearch = (value: string) => {
27 setQuery(value); // 紧急更新:立即更新输入框
28
29 startTransition(() => {
30 // 非紧急更新:搜索结果可以延迟
31 setSearchResults(value);
32 });
33 };
34
35 return (
36 <div>
37 <input
38 value={query}
39 onChange={(e) => handleSearch(e.target.value)}
40 placeholder="搜索..."
41 />
42 {isPending && <Spinner />}
43 </div>
44 );
45}

并发特性优势

  • 可中断渲染:React可以暂停渲染工作,优先处理用户交互
  • 时间切片:将长时间的渲染工作分解为小块
  • 优先级调度:根据更新的重要性分配不同优先级
  • 更好的用户体验:减少页面卡顿,提升响应性

2.2 状态管理架构设计

现代React应用的状态管理需要考虑本地状态、全局状态、服务器状态等多个维度,选择合适的状态管理方案至关重要。

现代Redux最佳实践

Redux Toolkit完整实现
typescript
1// 1. Store配置
2import { configureStore } from '@reduxjs/toolkit';
3import { setupListeners } from '@reduxjs/toolkit/query';
4import { userSlice } from './slices/userSlice';
5import { apiSlice } from './api/apiSlice';
6
7export const store = configureStore({
8 reducer: {
9 user: userSlice.reducer,
10 api: apiSlice.reducer,
11 },
12 middleware: (getDefaultMiddleware) =>
13 getDefaultMiddleware({
14 serializableCheck: {
15 ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
16 },
17 }).concat(apiSlice.middleware),
18 devTools: process.env.NODE_ENV !== 'production',
19});
20
21setupListeners(store.dispatch);
22
23export type RootState = ReturnType<typeof store.getState>;
24export type AppDispatch = typeof store.dispatch;
25
26// 2. Slice定义
27import { createSlice, PayloadAction } from '@reduxjs/toolkit';
28
29interface UserState {
30 currentUser: User | null;
31 preferences: UserPreferences;
32 loading: boolean;
33 error: string | null;
34}
35
36const initialState: UserState = {
37 currentUser: null,
38 preferences: {
39 theme: 'light',
40 language: 'zh-CN',
41 notifications: true,
42 },
43 loading: false,
44 error: null,
45};
46
47export const userSlice = createSlice({
48 name: 'user',
49 initialState,
50 reducers: {
51 setUser: (state, action: PayloadAction<User>) => {
52 state.currentUser = action.payload;
53 state.error = null;
54 },
55 updatePreferences: (state, action: PayloadAction<Partial<UserPreferences>>) => {
56 state.preferences = { ...state.preferences, ...action.payload };
57 },
58 setLoading: (state, action: PayloadAction<boolean>) => {
59 state.loading = action.payload;
60 },
61 setError: (state, action: PayloadAction<string>) => {
62 state.error = action.payload;
63 state.loading = false;
64 },
65 clearError: (state) => {
66 state.error = null;
67 },
68 },
69 extraReducers: (builder) => {
70 builder
71 .addCase(loginUser.pending, (state) => {
72 state.loading = true;
73 state.error = null;
74 })
75 .addCase(loginUser.fulfilled, (state, action) => {
76 state.loading = false;
77 state.currentUser = action.payload;
78 })
79 .addCase(loginUser.rejected, (state, action) => {
80 state.loading = false;
81 state.error = action.error.message || '登录失败';
82 });
83 },
84});
85
86// 3. 异步Thunk
87import { createAsyncThunk } from '@reduxjs/toolkit';
88
89export const loginUser = createAsyncThunk(
90 'user/login',
91 async (credentials: LoginCredentials, { rejectWithValue }) => {
92 try {
93 const response = await authAPI.login(credentials);
94 localStorage.setItem('token', response.token);
95 return response.user;
96 } catch (error) {
97 return rejectWithValue(error.message);
98 }
99 }
100);
101
102// 4. RTK Query API定义
103import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
104
105export const apiSlice = createApi({
106 reducerPath: 'api',
107 baseQuery: fetchBaseQuery({
108 baseUrl: '/api',
109 prepareHeaders: (headers, { getState }) => {
110 const token = (getState() as RootState).user.token;
111 if (token) {
112 headers.set('authorization', `Bearer ${token}`);
113 }
114 return headers;
115 },
116 }),
117 tagTypes: ['User', 'Post', 'Comment'],
118 endpoints: (builder) => ({
119 getUsers: builder.query<User[], void>({
120 query: () => '/users',
121 providesTags: ['User'],
122 }),
123 getUserById: builder.query<User, string>({
124 query: (id) => `/users/${id}`,
125 providesTags: (result, error, id) => [{ type: 'User', id }],
126 }),
127 updateUser: builder.mutation<User, { id: string; updates: Partial<User> }>({
128 query: ({ id, updates }) => ({
129 url: `/users/${id}`,
130 method: 'PATCH',
131 body: updates,
132 }),
133 invalidatesTags: (result, error, { id }) => [{ type: 'User', id }],
134 }),
135 }),
136});
137
138export const { useGetUsersQuery, useGetUserByIdQuery, useUpdateUserMutation } = apiSlice;
139
140// 5. 类型安全的Hooks
141import { useSelector, useDispatch } from 'react-redux';
142import type { TypedUseSelectorHook } from 'react-redux';
143
144export const useAppDispatch = () => useDispatch<AppDispatch>();
145export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
146
147// 6. 组件中使用
148function UserProfile({ userId }: { userId: string }) {
149 const dispatch = useAppDispatch();
150 const { currentUser, preferences, loading } = useAppSelector(state => state.user);
151 const { data: user, error, isLoading } = useGetUserByIdQuery(userId);
152 const [updateUser] = useUpdateUserMutation();
153
154 const handleUpdatePreferences = (newPreferences: Partial<UserPreferences>) => {
155 dispatch(userSlice.actions.updatePreferences(newPreferences));
156 };
157
158 const handleUpdateProfile = async (updates: Partial<User>) => {
159 try {
160 await updateUser({ id: userId, updates }).unwrap();
161 toast.success('Profile updated successfully');
162 } catch (error) {
163 toast.error('Failed to update profile');
164 }
165 };
166
167 if (isLoading) return <ProfileSkeleton />;
168 if (error) return <ErrorMessage error={error} />;
169
170 return (
171 <div>
172 <UserInfo user={user} onUpdate={handleUpdateProfile} />
173 <UserSettings
174 preferences={preferences}
175 onUpdate={handleUpdatePreferences}
176 />
177 </div>
178 );
179}

3. Vue.js生态系统与组合式API

3.1 Vue 3组合式API深度实践

Vue 3的组合式API为开发者提供了更灵活的逻辑复用和更好的TypeScript支持,是现代Vue开发的核心。

组合式API最佳实践

Vue 3组合式API完整示例
vue
1<template>
2 <div class="user-dashboard">
3 <!-- 用户信息卡片 -->
4 <UserCard
5 :user="user"
6 :loading="userLoading"
7 @update="handleUserUpdate"
8 />
9
10 <!-- 搜索和过滤 -->
11 <div class="search-section">
12 <input
13 v-model="searchQuery"
14 placeholder="搜索用户..."
15 class="search-input"
16 />
17 <select v-model="selectedRole" class="role-filter">
18 <option value="">所有角色</option>
19 <option value="admin">管理员</option>
20 <option value="user">普通用户</option>
21 </select>
22 </div>
23
24 <!-- 用户列表 -->
25 <div class="user-list">
26 <UserListItem
27 v-for="user in filteredUsers"
28 :key="user.id"
29 :user="user"
30 @edit="handleEditUser"
31 @delete="handleDeleteUser"
32 />
33 </div>
34
35 <!-- 分页 -->
36 <Pagination
37 :current="currentPage"
38 :total="totalUsers"
39 :page-size="pageSize"
40 @change="handlePageChange"
41 />
42 </div>
43</template>
44
45<script setup lang="ts">
46import { ref, computed, watch, onMounted, nextTick } from 'vue';
47import { useRouter, useRoute } from 'vue-router';
48import { storeToRefs } from 'pinia';
49import { useUserStore } from '@/stores/user';
50import { useNotification } from '@/composables/useNotification';
51import { useDebounce } from '@/composables/useDebounce';
52import type { User, UserRole } from '@/types/user';
53
54// Props和Emits定义
55interface Props {
56 initialUserId?: string;
57}
58
59const props = withDefaults(defineProps<Props>(), {
60 initialUserId: '',
61});
62
63const emit = defineEmits<{
64 userSelected: [user: User];
65 usersLoaded: [count: number];
66}>();
67
68// 路由和状态管理
69const router = useRouter();
70const route = useRoute();
71const userStore = useUserStore();
72const { user, users, loading: userLoading } = storeToRefs(userStore);
73const { showSuccess, showError } = useNotification();
74
75// 响应式数据
76const searchQuery = ref('');
77const selectedRole = ref<UserRole | ''>('');
78const currentPage = ref(1);
79const pageSize = ref(10);
80const totalUsers = ref(0);
81
82// 防抖搜索
83const debouncedSearchQuery = useDebounce(searchQuery, 300);
84
85// 计算属性
86const filteredUsers = computed(() => {
87 let result = users.value;
88
89 // 搜索过滤
90 if (debouncedSearchQuery.value) {
91 const query = debouncedSearchQuery.value.toLowerCase();
92 result = result.filter(user =>
93 user.name.toLowerCase().includes(query) ||
94 user.email.toLowerCase().includes(query)
95 );
96 }
97
98 // 角色过滤
99 if (selectedRole.value) {
100 result = result.filter(user => user.role === selectedRole.value);
101 }
102
103 return result;
104});
105
106const hasUsers = computed(() => filteredUsers.value.length > 0);
107const isFirstPage = computed(() => currentPage.value === 1);
108const isLastPage = computed(() => {
109 return currentPage.value >= Math.ceil(totalUsers.value / pageSize.value);
110});
111
112// 方法定义
113const fetchUsers = async (page = 1) => {
114 try {
115 const result = await userStore.fetchUsers({
116 page,
117 pageSize: pageSize.value,
118 search: debouncedSearchQuery.value,
119 role: selectedRole.value || undefined,
120 });
121
122 totalUsers.value = result.total;
123 emit('usersLoaded', result.total);
124 } catch (error) {
125 showError('获取用户列表失败');
126 console.error('Failed to fetch users:', error);
127 }
128};
129
130const handleUserUpdate = async (updates: Partial<User>) => {
131 try {
132 await userStore.updateUser(user.value!.id, updates);
133 showSuccess('用户信息更新成功');
134 } catch (error) {
135 showError('更新用户信息失败');
136 }
137};
138
139const handleEditUser = (user: User) => {
140 emit('userSelected', user);
141 router.push(`/users/${user.id}/edit`);
142};
143
144const handleDeleteUser = async (user: User) => {
145 if (!confirm(`确定要删除用户 ${user.name} 吗?`)) return;
146
147 try {
148 await userStore.deleteUser(user.id);
149 showSuccess('用户删除成功');
150 await fetchUsers(currentPage.value);
151 } catch (error) {
152 showError('删除用户失败');
153 }
154};
155
156const handlePageChange = (page: number) => {
157 currentPage.value = page;
158 fetchUsers(page);
159};
160
161// 监听器
162watch([debouncedSearchQuery, selectedRole], () => {
163 currentPage.value = 1;
164 fetchUsers(1);
165});
166
167watch(() => route.query, (newQuery) => {
168 if (newQuery.search) {
169 searchQuery.value = newQuery.search as string;
170 }
171 if (newQuery.role) {
172 selectedRole.value = newQuery.role as UserRole;
173 }
174}, { immediate: true });
175
176// 生命周期
177onMounted(async () => {
178 await fetchUsers();
179
180 // 如果有初始用户ID,选中该用户
181 if (props.initialUserId) {
182 const initialUser = users.value.find(u => u.id === props.initialUserId);
183 if (initialUser) {
184 emit('userSelected', initialUser);
185 }
186 }
187});
188
189// 暴露给模板的方法和数据
190defineExpose({
191 refreshUsers: () => fetchUsers(currentPage.value),
192 resetFilters: () => {
193 searchQuery.value = '';
194 selectedRole.value = '';
195 currentPage.value = 1;
196 },
197});
198</script>
199
200<style scoped>
201.user-dashboard {
202 padding: 20px;
203 max-width: 1200px;
204 margin: 0 auto;
205}
206
207.search-section {
208 display: flex;
209 gap: 16px;
210 margin-bottom: 24px;
211 align-items: center;
212}
213
214.search-input {
215 flex: 1;
216 padding: 8px 12px;
217 border: 1px solid #d1d5db;
218 border-radius: 6px;
219 font-size: 14px;
220}
221
222.role-filter {
223 padding: 8px 12px;
224 border: 1px solid #d1d5db;
225 border-radius: 6px;
226 font-size: 14px;
227 min-width: 120px;
228}
229
230.user-list {
231 display: grid;
232 gap: 16px;
233 margin-bottom: 24px;
234}
235
236@media (max-width: 768px) {
237 .search-section {
238 flex-direction: column;
239 align-items: stretch;
240 }
241}
242</style>

4. TypeScript在前端开发中的应用

4.1 TypeScript高级类型系统

TypeScript为前端开发提供了强大的类型安全保障,通过高级类型系统可以构建更健壮的应用。

高级类型定义与应用

TypeScript高级类型实践
typescript
1// 1. 工具类型和条件类型
2type NonNullable<T> = T extends null | undefined ? never : T;
3type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
4type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
5
6// 2. 映射类型
7type Partial<T> = {
8 [P in keyof T]?: T[P];
9};
10
11type Required<T> = {
12 [P in keyof T]-?: T[P];
13};
14
15type Readonly<T> = {
16 readonly [P in keyof T]: T[P];
17};
18
19// 3. 自定义工具类型
20type DeepPartial<T> = {
21 [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
22};
23
24type DeepReadonly<T> = {
25 readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
26};
27
28type PickByType<T, U> = {
29 [P in keyof T as T[P] extends U ? P : never]: T[P];
30};
31
32type OmitByType<T, U> = {
33 [P in keyof T as T[P] extends U ? never : P]: T[P];
34};
35
36// 4. 字符串模板类型
37type EventName<T extends string> = `on${Capitalize<T>}`;
38type CSSProperty = `--${string}`;
39type APIEndpoint<T extends string> = `/api/${T}`;
40
41// 使用示例
42type UserEvents = EventName<'click' | 'hover' | 'focus'>; // 'onClick' | 'onHover' | 'onFocus'
43type UserAPI = APIEndpoint<'users' | 'posts'>; // '/api/users' | '/api/posts'
44
45// 5. 递归类型
46type JSONValue =
47 | string
48 | number
49 | boolean
50 | null
51 | JSONValue[]
52 | { [key: string]: JSONValue };
53
54type TreeNode<T> = {
55 value: T;
56 children?: TreeNode<T>[];
57};
58
59// 6. 品牌类型(Branded Types)
60type Brand<T, B> = T & { __brand: B };
61type UserId = Brand<string, 'UserId'>;
62type Email = Brand<string, 'Email'>;
63type URL = Brand<string, 'URL'>;
64
65const createUserId = (id: string): UserId => id as UserId;
66const createEmail = (email: string): Email => {
67 if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
68 throw new Error('Invalid email format');
69 }
70 return email as Email;
71};
72
73// 7. 函数重载类型
74interface APIClient {
75 get<T>(url: string): Promise<T>;
76 get<T>(url: string, config: RequestConfig): Promise<T>;
77 post<T, D>(url: string, data: D): Promise<T>;
78 post<T, D>(url: string, data: D, config: RequestConfig): Promise<T>;
79}
80
81// 8. 泛型约束
82interface Lengthwise {
83 length: number;
84}
85
86function loggingIdentity<T extends Lengthwise>(arg: T): T {
87 console.log(arg.length);
88 return arg;
89}
90
91// 9. 索引访问类型
92type Person = {
93 name: string;
94 age: number;
95 address: {
96 street: string;
97 city: string;
98 };
99};
100
101type PersonName = Person['name']; // string
102type PersonAddress = Person['address']; // { street: string; city: string; }
103type PersonAddressCity = Person['address']['city']; // string
104
105// 10. 键值映射类型
106type APIResponse<T> = {
107 data: T;
108 status: number;
109 message: string;
110};
111
112type UserResponse = APIResponse<User>;
113type ProductResponse = APIResponse<Product[]>;
114
115// 11. 条件类型的实际应用
116type ApiFunction<T> = T extends (...args: any[]) => Promise<infer R>
117 ? (...args: Parameters<T>) => Promise<ApiResponse<R>>
118 : never;
119
120// 原始函数
121declare function fetchUser(id: string): Promise<User>;
122declare function fetchProducts(): Promise<Product[]>;
123
124// 包装后的API函数类型
125type WrappedFetchUser = ApiFunction<typeof fetchUser>;
126type WrappedFetchProducts = ApiFunction<typeof fetchProducts>;

5. 前端性能优化全方位指南

5.1 加载性能优化策略

前端性能优化是提升用户体验的关键,需要从多个维度进行系统性优化。

代码分割与懒加载

代码分割最佳实践
typescript
1// 1. 路由级别的代码分割
2import { lazy, Suspense } from 'react';
3import { Routes, Route } from 'react-router-dom';
4
5// 懒加载组件
6const Home = lazy(() => import('./pages/Home'));
7const About = lazy(() => import('./pages/About'));
8const Dashboard = lazy(() => import('./pages/Dashboard'));
9const UserProfile = lazy(() => import('./pages/UserProfile'));
10
11// 预加载关键路由
12const AdminPanel = lazy(() =>
13 import(/* webpackChunkName: "admin" */ './pages/AdminPanel')
14);
15
16// 条件加载
17const AdvancedFeatures = lazy(() =>
18 import(/* webpackChunkName: "advanced" */ './components/AdvancedFeatures')
19);
20
21function App() {
22 return (
23 <Suspense fallback={<PageSkeleton />}>
24 <Routes>
25 <Route path="/" element={<Home />} />
26 <Route path="/about" element={<About />} />
27 <Route path="/dashboard" element={<Dashboard />} />
28 <Route path="/profile" element={<UserProfile />} />
29 <Route path="/admin" element={<AdminPanel />} />
30 </Routes>
31 </Suspense>
32 );
33}
34
35// 2. 组件级别的懒加载
36const LazyModal = lazy(() => import('./components/Modal'));
37const LazyChart = lazy(() => import('./components/Chart'));
38
39function Dashboard() {
40 const [showModal, setShowModal] = useState(false);
41 const [showChart, setShowChart] = useState(false);
42
43 return (
44 <div>
45 <h1>Dashboard</h1>
46
47 {/* 条件渲染懒加载组件 */}
48 {showModal && (
49 <Suspense fallback={<div>Loading modal...</div>}>
50 <LazyModal onClose={() => setShowModal(false)} />
51 </Suspense>
52 )}
53
54 {showChart && (
55 <Suspense fallback={<ChartSkeleton />}>
56 <LazyChart data={chartData} />
57 </Suspense>
58 )}
59
60 <button onClick={() => setShowModal(true)}>Open Modal</button>
61 <button onClick={() => setShowChart(true)}>Show Chart</button>
62 </div>
63 );
64}
65
66// 3. 动态导入工具函数
67async function loadUtility() {
68 const { heavyUtilityFunction } = await import('./utils/heavyUtils');
69 return heavyUtilityFunction;
70}
71
72// 4. 预加载策略
73function usePreloadRoute(routePath: string) {
74 useEffect(() => {
75 const timer = setTimeout(() => {
76 // 在空闲时间预加载路由
77 if ('requestIdleCallback' in window) {
78 requestIdleCallback(() => {
79 import(/* webpackChunkName: "[request]" */ `./pages/${routePath}`);
80 });
81 }
82 }, 2000);
83
84 return () => clearTimeout(timer);
85 }, [routePath]);
86}
87
88// 5. Webpack配置优化
89// webpack.config.js
90module.exports = {
91 optimization: {
92 splitChunks: {
93 chunks: 'all',
94 cacheGroups: {
95 // 第三方库单独打包
96 vendor: {
97 test: /[\\/]node_modules[\\/]/,
98 name: 'vendors',
99 chunks: 'all',
100 priority: 10,
101 },
102 // 公共组件
103 common: {
104 name: 'common',
105 minChunks: 2,
106 chunks: 'all',
107 priority: 5,
108 reuseExistingChunk: true,
109 },
110 // React相关库
111 react: {
112 test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
113 name: 'react',
114 chunks: 'all',
115 priority: 20,
116 },
117 },
118 },
119 // 运行时代码单独提取
120 runtimeChunk: {
121 name: 'runtime',
122 },
123 },
124};
125
126// 6. Vite配置优化
127// vite.config.ts
128export default defineConfig({
129 build: {
130 rollupOptions: {
131 output: {
132 manualChunks: {
133 // 第三方库分包
134 vendor: ['react', 'react-dom'],
135 router: ['react-router-dom'],
136 ui: ['antd', '@ant-design/icons'],
137 utils: ['lodash', 'dayjs'],
138 },
139 },
140 },
141 // 启用压缩
142 minify: 'terser',
143 terserOptions: {
144 compress: {
145 drop_console: true,
146 drop_debugger: true,
147 },
148 },
149 },
150});

6. 现代CSS与样式解决方案

6.1 CSS-in-JS与原子化CSS

现代前端开发中,CSS的组织和管理方式发生了重大变革,从传统的CSS文件到CSS-in-JS,再到原子化CSS。

Styled Components与Emotion

CSS-in-JS最佳实践
typescript
1// 1. Styled Components基础用法
2import styled, { css, ThemeProvider, createGlobalStyle } from 'styled-components';
3
4// 主题定义
5const theme = {
6 colors: {
7 primary: '#007bff',
8 secondary: '#6c757d',
9 success: '#28a745',
10 danger: '#dc3545',
11 warning: '#ffc107',
12 info: '#17a2b8',
13 light: '#f8f9fa',
14 dark: '#343a40',
15 },
16 spacing: {
17 xs: '0.25rem',
18 sm: '0.5rem',
19 md: '1rem',
20 lg: '1.5rem',
21 xl: '3rem',
22 },
23 breakpoints: {
24 mobile: '576px',
25 tablet: '768px',
26 desktop: '992px',
27 wide: '1200px',
28 },
29 shadows: {
30 sm: '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)',
31 md: '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)',
32 lg: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)',
33 },
34};
35
36// 全局样式
37const GlobalStyle = createGlobalStyle`
38 * {
39 box-sizing: border-box;
40 margin: 0;
41 padding: 0;
42 }
43
44 body {
45 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
46 line-height: 1.6;
47 color: ${props => props.theme.colors.dark};
48 background-color: ${props => props.theme.colors.light};
49 }
50
51 a {
52 color: ${props => props.theme.colors.primary};
53 text-decoration: none;
54
55 &:hover {
56 text-decoration: underline;
57 }
58 }
59`;
60
61// 2. 基础组件样式
62interface ButtonProps {
63 variant?: 'primary' | 'secondary' | 'danger';
64 size?: 'small' | 'medium' | 'large';
65 fullWidth?: boolean;
66 disabled?: boolean;
67}
68
69const Button = styled.button<ButtonProps>`
70 display: inline-flex;
71 align-items: center;
72 justify-content: center;
73 border: none;
74 border-radius: 4px;
75 font-weight: 500;
76 cursor: pointer;
77 transition: all 0.2s ease-in-out;
78
79 /* 尺寸变体 */
80 ${props => {
81 switch (props.size) {
82 case 'small':
83 return css`
84 padding: ${props.theme.spacing.xs} ${props.theme.spacing.sm};
85 font-size: 0.875rem;
86 `;
87 case 'large':
88 return css`
89 padding: ${props.theme.spacing.md} ${props.theme.spacing.lg};
90 font-size: 1.125rem;
91 `;
92 default:
93 return css`
94 padding: ${props.theme.spacing.sm} ${props.theme.spacing.md};
95 font-size: 1rem;
96 `;
97 }
98 }}
99
100 /* 颜色变体 */
101 ${props => {
102 const color = props.theme.colors[props.variant || 'primary'];
103 return css`
104 background-color: ${color};
105 color: white;
106
107 &:hover:not(:disabled) {
108 background-color: ${color}dd;
109 transform: translateY(-1px);
110 box-shadow: ${props.theme.shadows.md};
111 }
112
113 &:active:not(:disabled) {
114 transform: translateY(0);
115 box-shadow: ${props.theme.shadows.sm};
116 }
117 `;
118 }}
119
120 /* 全宽度 */
121 ${props => props.fullWidth && css`
122 width: 100%;
123 `}
124
125 /* 禁用状态 */
126 ${props => props.disabled && css`
127 opacity: 0.6;
128 cursor: not-allowed;
129
130 &:hover {
131 transform: none;
132 box-shadow: none;
133 }
134 `}
135
136 /* 响应式设计 */
137 @media (max-width: ${props => props.theme.breakpoints.mobile}) {
138 padding: ${props => props.theme.spacing.sm};
139 font-size: 0.875rem;
140 }
141`;
142
143// 3. 复杂组件样式
144const Card = styled.div`
145 background: white;
146 border-radius: 8px;
147 box-shadow: ${props => props.theme.shadows.sm};
148 overflow: hidden;
149 transition: box-shadow 0.2s ease-in-out;
150
151 &:hover {
152 box-shadow: ${props => props.theme.shadows.md};
153 }
154`;
155
156const CardHeader = styled.div`
157 padding: ${props => props.theme.spacing.lg};
158 border-bottom: 1px solid ${props => props.theme.colors.light};
159
160 h3 {
161 margin: 0;
162 color: ${props => props.theme.colors.dark};
163 }
164`;
165
166const CardBody = styled.div`
167 padding: ${props => props.theme.spacing.lg};
168`;
169
170const CardFooter = styled.div`
171 padding: ${props => props.theme.spacing.md} ${props => props.theme.spacing.lg};
172 background-color: ${props => props.theme.colors.light};
173 border-top: 1px solid #dee2e6;
174
175 display: flex;
176 justify-content: flex-end;
177 gap: ${props => props.theme.spacing.sm};
178`;
179
180// 4. 动画和过渡
181const fadeIn = css`
182 @keyframes fadeIn {
183 from {
184 opacity: 0;
185 transform: translateY(20px);
186 }
187 to {
188 opacity: 1;
189 transform: translateY(0);
190 }
191 }
192`;
193
194const AnimatedContainer = styled.div`
195 ${fadeIn}
196 animation: fadeIn 0.3s ease-out;
197`;
198
199// 5. 响应式工具
200const media = {
201 mobile: (styles: TemplateStringsArray | string) => css`
202 @media (max-width: ${props => props.theme.breakpoints.mobile}) {
203 ${styles}
204 }
205 `,
206 tablet: (styles: TemplateStringsArray | string) => css`
207 @media (max-width: ${props => props.theme.breakpoints.tablet}) {
208 ${styles}
209 }
210 `,
211 desktop: (styles: TemplateStringsArray | string) => css`
212 @media (min-width: ${props => props.theme.breakpoints.desktop}) {
213 ${styles}
214 }
215 `,
216};
217
218const ResponsiveGrid = styled.div`
219 display: grid;
220 gap: ${props => props.theme.spacing.md};
221 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
222
223 ${media.mobile`
224 grid-template-columns: 1fr;
225 gap: ${props => props.theme.spacing.sm};
226 `}
227`;
228
229// 6. 使用示例
230function App() {
231 return (
232 <ThemeProvider theme={theme}>
233 <GlobalStyle />
234 <div>
235 <ResponsiveGrid>
236 <Card>
237 <CardHeader>
238 <h3>Card Title</h3>
239 </CardHeader>
240 <CardBody>
241 <p>Card content goes here...</p>
242 </CardBody>
243 <CardFooter>
244 <Button variant="secondary" size="small">
245 Cancel
246 </Button>
247 <Button variant="primary" size="small">
248 Save
249 </Button>
250 </CardFooter>
251 </Card>
252 </ResponsiveGrid>
253 </div>
254 </ThemeProvider>
255 );
256}

参与讨论