引言:为什么选择SPA开发?
单页面应用(Single Page Application, SPA)是现代Web开发的主流架构模式。与传统的多页面应用不同,SPA通过JavaScript动态更新页面内容,无需每次操作都向服务器请求新页面,从而提供更流畅的用户体验。Vue和React作为目前最流行的两大前端框架,正是构建SPA的核心技术。
掌握Vue和React不仅能让你具备构建现代化Web应用的能力,更是通往高薪前端岗位的敲门砖。根据2023年前端开发薪资报告显示,熟练掌握Vue/React的开发者平均薪资比普通前端开发者高出30%-50%。
第一部分:零基础入门阶段(1-2个月)
1.1 必备基础技能储备
在开始学习框架之前,你需要掌握以下基础知识:
- HTML5/CSS3:语义化标签、Flexbox布局、Grid布局、CSS变量
- JavaScript ES6+:这是重中之重,包括:
- let/const变量声明
- 箭头函数
- 解构赋值
- 模板字符串
- Promise和async/await
- 模块化(import/export)
- 数组方法(map/filter/reduce)
示例代码:ES6+核心语法
// 解构赋值与模板字符串
const user = { name: '张三', age: 25 };
const { name, age } = user;
console.log(`用户${name},今年${age}岁`); // 输出:用户张三,今年25岁
// Promise与async/await
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('数据加载完成'), 1000);
});
}
async function loadData() {
const result = await fetchData();
console.log(result); // 1秒后输出:数据加载完成
}
1.2 第一个SPA应用体验
在学习框架前,先用原生JavaScript实现一个简单的SPA,理解核心概念:
<!DOCTYPE html>
<html>
<head>
<title>简易SPA</title>
<style>
.page { display: none; padding: 20px; }
.active { display: block; }
nav a { margin-right: 15px; text-decoration: none; }
</style>
</head>
<body>
<nav>
<a href="#home" onclick="showPage('home')">首页</a>
<a href="#about" onclick="showPage('about')">关于</a>
</nav>
<div id="home" class="page active">
<h1>欢迎来到首页</h1>
<p>这是SPA的简单示例</p>
</div>
<div id="about" class="page">
<h1>关于我们</h1>
<p>学习Vue和React的最佳起点</p>
</div>
<script>
function showPage(pageId) {
// 隐藏所有页面
document.querySelectorAll('.page').forEach(page => {
page.classList.remove('active');
});
// 显示目标页面
document.getElementById(pageId).classList.add('active');
}
// 监听浏览器前进后退
window.addEventListener('hashchange', () => {
const hash = location.hash.substring(1) || 'home';
showPage(hash);
});
</script>
</body>
</html>
这个例子展示了SPA的核心思想:通过JavaScript动态切换页面内容,而不是刷新整个页面。
第二部分:Vue.js核心技术掌握(2-3个月)
2.1 Vue基础语法与核心概念
Vue以易学易用著称,采用自底向上增量开发的设计。从Vue 3开始,推荐使用Composition API。
Vue 3基础示例:
<!DOCTYPE html>
<html>
<head>
<title>Vue 3基础</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<h1>{{ title }}</h1>
<p>计数器:{{ count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">减少</button>
<p v-if="count > 5">计数器超过5了!</p>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }} - 价格:{{ item.price }}
</li>
</ul>
</div>
<script>
const { createApp, ref, reactive, computed } = Vue;
createApp({
setup() {
// 响应式数据
const count = ref(0);
const title = ref('Vue 3 计数器示例');
// 响应式对象
const items = reactive([
{ id: 1, name: '苹果', price: 5 },
{ id: 2, name: '香蕉', price: 3 },
{ id: 3, name: '橙子', price: 4 }
]);
// 计算属性
const totalPrice = computed(() => {
return items.reduce((sum, item) => sum + item.price, 0);
});
// 方法
const increment = () => {
count.value++;
console.log(`当前计数:${count.value}`);
};
const decrement = () => {
if (count.value > 0) count.value--;
};
return {
count,
title,
items,
totalPrice,
increment,
decrement
};
}
}).mount('#app');
</script>
</body>
</head>
</html>
2.2 Vue组件化开发
组件化是Vue的核心思想之一。下面是一个完整的组件示例:
// TodoItem.vue - 单文件组件示例(伪代码)
<template>
<div class="todo-item" :class="{ completed: todo.completed }">
<input type="checkbox" v-model="todo.completed">
<span>{{ todo.text }}</span>
<button @click="$emit('delete', todo.id)">删除</button>
</div>
</template>
<script>
export default {
name: 'TodoItem',
props: {
todo: {
type: Object,
required: true
}
}
}
</script>
<style scoped>
.todo-item {
padding: 10px;
border-bottom: 1px solid #eee;
}
.completed {
text-decoration: line-through;
color: #999;
}
</style>
父组件使用:
// App.vue
<template>
<div id="app">
<h1>待办事项列表</h1>
<input v-model="newTodo" placeholder="输入新事项" @keyup.enter="addTodo">
<button @click="addTodo">添加</button>
<div v-for="todo in todos" :key="todo.id">
<TodoItem
:todo="todo"
@delete="deleteTodo"
/>
</div>
</div>
</template>
<script>
import TodoItem from './TodoItem.vue';
export default {
components: { TodoItem },
data() {
return {
newTodo: '',
todos: [
{ id: 1, text: '学习Vue', completed: false },
{ id: 2, text: '完成项目', completed: true }
]
}
},
methods: {
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({
id: Date.now(),
text: this.newTodo,
completed: false
});
this.newTodo = '';
}
},
deleteTodo(id) {
this.todos = this.todos.filter(todo => todo.id !== id);
}
}
}
</script>
</code>
2.3 Vue路由与状态管理
Vue Router配置:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';
import User from '../views/User.vue';
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
{
// 动态路由参数
path: '/user/:id',
name: 'User',
component: User,
props: true
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
Pinia状态管理(Vue推荐):
// store/user.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
currentUser: null,
isLoggedIn: false
}),
actions: {
async login(credentials) {
// 模拟API调用
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
});
const user = await response.json();
this.currentUser = user;
this.isLoggedIn = true;
localStorage.setItem('token', user.token);
},
logout() {
this.currentUser = null;
this.isLoggedIn = false;
localStorage.removeItem('token');
}
},
getters: {
username: (state) => state.currentUser?.name || '访客',
isAdmin: (state) => state.currentUser?.role === 'admin'
}
});
第三部分:React核心技术掌握(2-3个月)
3.1 React基础与JSX语法
React采用声明式编程范式,JSX是其核心特性之一。
React基础示例:
// index.js
import React from 'react';
import ReactDOM from '18/react-client';
import './index.css';
// 函数组件与Hooks
function Counter() {
const [count, setCount] = React.useState(0);
const [name, setName] = React.useState('');
// 计算属性
const message = React.useMemo(() => {
return `当前计数:${count}`;
}, [count]);
// 副作用
React.useEffect(() => {
document.title = `计数器:${count}`;
console.log('计数器已更新');
// 清理函数
return () => {
console.log('组件卸载');
};
}, [count]);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return (
<div style={{ padding: '20px' }}>
<h1>React 计数器</h1>
<p>{message}</p>
<div>
<button onClick={increment}>增加</button>
<button onClick={decrement}>减少</button>
</div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="输入你的名字"
/>
{name && <p>你好,{name}!</p>}
</div>
);
}
// 渲染到DOM
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);
3.2 React组件化与Props传递
组件拆分示例:
// UserCard.jsx - 用户卡片组件
import React from 'react';
import PropTypes from 'prop-types';
function UserCard({ user, onFollow, isFollowing }) {
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.bio}</p>
<p>粉丝:{user.followers}</p>
<button
onClick={() => onFollow(user.id)}
style={{
backgroundColor: isFollowing ? '#ccc' : '#007bff',
color: 'white',
padding: '8px 16px',
border: 'none',
borderRadius: '4px'
}}
>
{isFollowing ? '已关注' : '关注'}
</button>
</div>
);
}
UserCard.propTypes = {
user: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
avatar: PropTypes.string,
bio: PropTypes.string,
followers: PropTypes.number
}).isRequired,
onFollow: PropTypes.func.isRequired,
isFollowing: PropTypes.bool
};
// 父组件
function UserList() {
const [users, setUsers] = React.useState([
{ id: 1, name: 'Alice', bio: '前端开发者', followers: 100, avatar: 'avatar1.png' },
{ id: 2, name: 'Bob', bio: '全栈工程师', followers: 200, avatar: 'avatar2.png' }
]);
const [followingIds, setFollowingIds] = React.useState([]);
const handleFollow = (userId) => {
setFollowingIds(prev => {
if (prev.includes(userId)) {
return prev.filter(id => id !== userId);
} else {
return [...prev, userId];
}
});
};
return (
<div>
<h2>用户列表</h2>
<div style={{ display: 'grid', gap: '20px' }}>
{users.map(user => (
<UserCard
key={user.id}
user={user}
onFollow={handleFollow}
isFollowing={followingIds.includes(user.id)}
/>
))}
</div>
</div>
);
}
3.3 React Router与Context API
路由配置:
// App.jsx
import { BrowserRouter as Router, Routes, Route, Link, Navigate } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Profile from './pages/Profile';
import NotFound from './pages/NotFound';
function Navigation() {
return (
<nav>
<Link to="/">首页</Link> |
<Link to="/about">关于</Link> |
<Link to="/profile">个人中心</Link>
</nav>
);
}
function App() {
return (
<Router>
<div className="app">
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profile" element={<Profile />} />
<Route path="/user/:id" element={<Profile />} />
<Route path="/login" element={<Navigate to="/" replace />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
);
}
Context API状态管理:
// AuthContext.jsx
import React, { createContext, useContext, useReducer } from 'react';
const AuthContext = createContext();
// Reducer函数
const authReducer = (state, action) => {
switch (action.type) {
case 'LOGIN':
return { user: action.payload, isLoggedIn: true };
case 'LOGOUT':
return { user: null, isLoggedIn: false };
case 'UPDATE_PROFILE':
return { ...state, user: { ...state.user, ...action.payload } };
default:
return state;
}
};
// Provider组件
export function AuthProvider({ children }) {
const [state, dispatch] = useReducer(authReducer, {
user: null,
isLoggedIn: false
});
// 异步登录
const login = async (credentials) => {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const user = await response.json();
dispatch({ type: 'LOGIN', payload: user });
} catch (error) {
console.error('登录失败:', error);
}
};
const logout = () => {
dispatch({ type: 'LOGOUT' });
};
const updateProfile = (profileData) => {
dispatch({ type: 'UPDATE_PROFILE', payload: profileData });
};
return (
<AuthContext.Provider value={{
state,
login,
logout,
updateProfile
}}>
{children}
</AuthContext.Provider>
);
}
// 自定义Hook
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth必须在AuthProvider内使用');
}
return context;
}
第四部分:进阶技能与工程化(1-2个月)
4.1 现代化构建工具
Vite配置(Vue/React通用):
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import react from '@vitejs/plugin-react';
export default defineConfig({
// 开发服务器配置
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 路径别名
resolve: {
alias: {
'@': '/src',
'@components': '/src/components'
}
},
// CSS预处理
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
},
// 生产环境配置
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'react', 'react-dom'],
ui: ['element-plus', 'antd']
}
}
}
}
});
4.2 TypeScript集成
Vue 3 + TypeScript示例:
// components/UserProfile.vue
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
// 接口定义
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
interface UserProfile extends User {
bio?: string;
avatar?: string;
}
// 响应式数据
const user = ref<UserProfile | null>(null);
const loading = ref<boolean>(false);
const error = ref<string | null>(null);
// 计算属性
const displayName = computed<string>(() => {
return user.value?.name || '未登录';
});
// 方法
const fetchUser = async (id: number): Promise<void> => {
loading.value = true;
error.value = null;
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error('获取用户失败');
user.value = await response.json();
} catch (e) {
error.value = e instanceof Error ? e.message : '未知错误';
} finally {
loading.value = false;
}
};
// 生命周期
onMounted(() => {
fetchUser(1);
});
</script>
<template>
<div class="user-profile">
<div v-if="loading">加载中...</div>
<div v-else-if="error" class="error">{{ error }}</div>
<div v-else-if="user">
<h2>{{ displayName }}</h2>
<p>{{ user.email }}</p>
<p v-if="user.bio">{{ user.bio }}</p>
</div>
</div>
</template>
React + TypeScript示例:
// components/TaskList.tsx
import React, { useState, useEffect, useCallback } from 'react';
// 类型定义
interface Task {
id: number;
title: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
}
interface TaskListProps {
initialTasks?: Task[];
}
// 组件
const TaskList: React.FC<TaskListProps> = ({ initialTasks = [] }) => {
const [tasks, setTasks] = useState<Task[]>(initialTasks);
const [newTask, setNewTask] = useState<string>('');
const [filter, setFilter] = useState<'all' | 'completed' | 'active'>('all');
// 使用useCallback优化性能
const addTask = useCallback(() => {
if (newTask.trim()) {
const task: Task = {
id: Date.now(),
title: newTask.trim(),
completed: false,
priority: 'medium'
};
setTasks(prev => [...prev, task]);
setNewTask('');
}
}, [newTask]);
// 过滤任务
const filteredTasks = React.useMemo(() => {
return tasks.filter(task => {
if (filter === 'completed') return task.completed;
if (filter === 'active') return !task.completed;
return true;
});
}, [tasks, filter]);
// 获取优先级颜色
const getPriorityColor = (priority: string): string => {
const colors = { low: 'green', medium: 'orange', high: 'red' };
return colors[priority as keyof typeof colors];
};
return (
<div className="task-list">
<h2>任务管理器</h2>
<div>
<input
type="text"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTask()}
placeholder="输入新任务"
/>
<button onClick={addTask}>添加</button>
</div>
<div>
<button onClick={() => setFilter('all')}>全部</button>
<button onClick={() => setFilter('active')}>进行中</button>
<button onClick={() => setFilter('completed')}>已完成</button>
</div>
<ul>
{filteredTasks.map(task => (
<li key={task.id} style={{ textDecoration: task.completed ? 'line-through' : 'none' }}>
<input
type="checkbox"
checked={task.completed}
onChange={() => {
setTasks(prev => prev.map(t =>
t.id === task.id ? { ...t, completed: !t.completed } : t
));
}}
/>
<span style={{ color: getPriorityColor(task.priority) }}>
[{task.priority.toUpperCase()}]
</span>
{task.title}
</li>
))}
</ul>
</div>
);
};
export default TaskList;
4.3 测试与部署
单元测试示例(Vue Test Utils):
// components/__tests__/Counter.spec.js
import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import Counter from '../Counter.vue';
describe('Counter组件', () => {
it('初始计数为0', () => {
const wrapper = mount(Counter);
expect(wrapper.text()).toContain('计数:0');
});
it('点击增加按钮计数+1', async () => {
const wrapper = mount(Counter);
const button = wrapper.find('button');
await button.trigger('click');
expect(wrapper.text()).toContain('计数:1');
});
it('计数超过5显示警告', async () => {
const wrapper = mount(Counter);
for (let i = 0; i < 6; i++) {
await wrapper.find('button').trigger('click');
}
expect(wrapper.text()).toContain('计数器超过5了!');
});
});
React Testing Library测试:
// components/__tests__/TaskList.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import TaskList from '../TaskList';
describe('TaskList组件', () => {
const mockTasks = [
{ id: 1, title: '学习React', completed: false, priority: 'high' },
{ id: 2, title: '完成项目', completed: true, priority: 'medium' }
];
it('渲染初始任务', () => {
render(<TaskList initialTasks={mockTasks} />);
expect(screen.getByText('学习React')).toBeInTheDocument();
expect(screen.getByText('完成项目')).toBeInTheDocument();
});
it('添加新任务', () => {
render(<TaskList />);
const input = screen.getByPlaceholderText('输入新任务');
const button = screen.getByText('添加');
fireEvent.change(input, { target: { value: '新任务' } });
fireEvent.click(button);
expect(screen.getByText('新任务')).toBeInTheDocument();
});
it('过滤已完成任务', () => {
render(<TaskList initialTasks={mockTasks} />);
const completedButton = screen.getByText('已完成');
fireEvent.click(completedButton);
expect(screen.queryByText('学习React')).not.toBeInTheDocument();
expect(screen.getByText('完成项目')).toBeInTheDocument();
});
});
第五部分:高薪就业实战项目(1个月)
5.1 项目一:电商后台管理系统(Vue版)
项目结构:
admin-dashboard/
├── src/
│ ├── api/
│ │ ├── product.js
│ │ └── order.js
│ ├── components/
│ │ ├── ProductTable.vue
│ │ └── OrderChart.vue
│ ├── store/
│ │ └── modules/
│ │ ├── product.js
│ │ └── order.js
│ ├── router/
│ │ └── index.js
│ ├── views/
│ │ ├── Login.vue
│ │ ├── Dashboard.vue
│ │ ├── ProductList.vue
│ │ └── OrderList.vue
│ ├── App.vue
│ └── main.js
├── package.json
└── vite.config.js
核心功能实现:
// store/modules/product.js (Pinia)
import { defineStore } from 'pinia';
import { getProductList, createProduct, updateProduct, deleteProduct } from '@/api/product';
export const useProductStore = defineStore('product', {
state: () => ({
products: [],
total: 0,
loading: false,
currentProduct: null
}),
actions: {
async fetchProducts(params = {}) {
this.loading = true;
try {
const response = await getProductList(params);
this.products = response.data;
this.total = response.total;
} catch (error) {
console.error('获取产品列表失败:', error);
throw error;
} finally {
this.loading = false;
}
},
async createProduct(data) {
const response = await createProduct(data);
this.products.unshift(response.data);
this.total++;
return response;
},
async updateProduct(id, data) {
const response = await updateProduct(id, data);
const index = this.products.findIndex(p => p.id === id);
if (index !== -1) {
this.products[index] = response.data;
}
return response;
},
async removeProduct(id) {
await deleteProduct(id);
this.products = this.products.filter(p => p.id !== id);
this.total--;
}
},
getters: {
activeProducts: (state) => state.products.filter(p => p.status === 'active'),
productNames: (state) => state.products.map(p => p.name)
}
});
API封装:
// api/product.js
import request from '@/utils/request';
export function getProductList(params) {
return request({
url: '/api/products',
method: 'get',
params
});
}
export function createProduct(data) {
return request({
url: '/api/products',
method: 'post',
data
});
}
export function updateProduct(id, data) {
return request({
url: `/api/products/${id}`,
method: 'put',
data
});
}
export function deleteProduct(id) {
return request({
url: `/api/products/${id}`,
method: 'delete'
});
}
5.2 项目二:社交聊天应用(React版)
核心组件:
// ChatApp.jsx
import React, { useState, useEffect, useRef } from 'react';
import { useAuth } from './AuthContext';
function ChatApp() {
const { state: { user } } = useAuth();
const [messages, setMessages] = useState([]);
const [inputMessage, setInputMessage] = useState('');
const [ws, setWs] = useState(null);
const messagesEndRef = useRef(null);
// WebSocket连接
useEffect(() => {
const websocket = new WebSocket('ws://localhost:8080/chat');
websocket.onopen = () => {
console.log('WebSocket连接已建立');
websocket.send(JSON.stringify({ type: 'join', userId: user.id }));
};
websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'message') {
setMessages(prev => [...prev, data.message]);
}
};
websocket.onclose = () => {
console.log('WebSocket连接已关闭');
};
setWs(websocket);
return () => {
websocket.close();
};
}, [user.id]);
// 自动滚动到底部
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const sendMessage = () => {
if (inputMessage.trim() && ws) {
const message = {
id: Date.now(),
userId: user.id,
userName: user.name,
text: inputMessage,
timestamp: new Date().toISOString()
};
ws.send(JSON.stringify({
type: 'message',
message
}));
setInputMessage('');
}
};
return (
<div className="chat-app">
<div className="chat-header">
<h2>聊天室 ({user.name})</h2>
</div>
<div className="messages-container">
{messages.map(msg => (
<div key={msg.id} className={`message ${msg.userId === user.id ? 'own' : ''}`}>
<div className="message-author">{msg.userName}</div>
<div className="message-text">{msg.text}</div>
<div className="message-time">
{new Date(msg.timestamp).toLocaleTimeString()}
</div>
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="message-input">
<input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="输入消息..."
/>
<button onClick={sendMessage}>发送</button>
</div>
</div>
);
}
第六部分:高薪就业准备(2周)
6.1 简历优化与面试准备
简历模板要点:
个人技能:
- 熟练掌握Vue 3 + TypeScript,了解Vue 2
- 熟练掌握React 18 + Hooks,了解React Native
- 掌握状态管理:Pinia、Redux Toolkit、Zustand
- 掌握路由:Vue Router、React Router
- 掌握构建工具:Vite、Webpack
- 掌握测试:Vitest、Jest、React Testing Library
- 掌握UI库:Element Plus、Ant Design、Tailwind CSS
- 掌握TypeScript,能编写类型安全的代码
- 了解Node.js、Express、MongoDB
项目经验:
1. 电商后台管理系统(Vue 3 + TypeScript)
- 负责商品管理、订单管理模块开发
- 使用Pinia进行状态管理,实现数据缓存
- 使用Axios封装请求,实现请求拦截和错误处理
- 使用Element Plus实现响应式布局
- 项目成果:提升开发效率40%,减少bug率30%
2. 社交聊天应用(React 18 + TypeScript)
- 使用WebSocket实现实时通信
- 使用Context API + useReducer管理全局状态
- 实现消息已读/未读状态、文件上传功能
- 使用React Testing Library编写单元测试
- 项目成果:支持1000+并发用户,响应时间<100ms
6.2 面试高频问题
Vue相关问题:
Vue 2和Vue 3的区别?
- 响应式原理:Vue 2使用Object.defineProperty,Vue 3使用Proxy
- 组件API:Vue 2使用Options API,Vue 3推荐Composition API
- 性能:Vue 3编译优化更好,体积更小
- TypeScript支持:Vue 3原生支持更好
Vue的生命周期钩子有哪些?
- beforeCreate, created
- beforeMount, mounted
- beforeUpdate, updated
- beforeDestroy, destroyed (Vue 2) / beforeUnmount, unmounted (Vue 3)
- activated, deactivated (keep-alive)
- errorCaptured
Vue的响应式原理是什么?
// 简化版Proxy实现 function reactive(obj) { return new Proxy(obj, { get(target, key) { track(target, key); // 依赖收集 return target[key]; }, set(target, key, value) { target[key] = value; trigger(target, key); // 触发更新 return true; } }); }
React相关问题:
React Hooks解决了什么问题?
- 让函数组件也能拥有状态和生命周期
- 避免类组件的this指向问题
- 更好的逻辑复用(自定义Hook)
- 更容易拆分和测试
useEffect和useLayoutEffect的区别?
- useEffect在浏览器绘制后执行
- useLayoutEffect在浏览器绘制前执行
- useLayoutEffect可以同步读取DOM布局,避免闪烁
React的合成事件是什么?
- React封装的跨浏览器原生事件对象
- 通过事件委托机制,所有事件绑定在document上
- 解决浏览器兼容性问题
- 自动进行事件池优化
通用问题:
Vue和React的选择标准?
- 项目规模:小项目Vue更易上手,大项目React生态更完善
- 团队背景:熟悉JSX选React,熟悉模板语法选Vue
- 需求:需要移动端选React Native,需要丰富UI组件库选Vue
- 学习曲线:Vue更平缓,React更灵活
如何优化SPA性能?
- 路由懒加载
- 组件按需加载
- 图片懒加载
- 使用Web Workers处理复杂计算
- 合理使用缓存(localStorage、IndexedDB)
- 代码分割和Tree Shaking
- 使用CDN加速
第七部分:持续学习与职业发展
7.1 推荐学习资源
官方文档(必读):
- Vue 3官方文档:https://vuejs.org/
- React官方文档:https://react.dev/
- Vue Router文档:https://router.vuejs.org/
- React Router文档:https://reactrouter.com/
优质社区:
- Stack Overflow
- GitHub Trending
- Vue论坛:https://forum.vuejs.org/
- React讨论区:https://github.com/facebook/react/discussions
进阶书籍:
- 《Vue.js设计与实现》- 霍春阳
- 《React设计原理》- 钟灵
- 《JavaScript高级程序设计》- Nicholas C. Zakas
7.2 职业发展路径
初级前端工程师(0-2年)
- 熟练掌握Vue/React基础
- 能独立完成模块开发
- 了解基本的工程化概念
- 薪资范围:8k-15k
中级前端工程师(2-5年)
- 精通Vue/React,能进行架构设计
- 掌握TypeScript和测试
- 有性能优化经验
- 能指导初级工程师
- 薪资范围:15k-25k
高级前端工程师(5年以上)
- 前端技术专家,能制定技术方案
- 有大型项目架构经验
- 熟悉Node.js和后端技术
- 能进行技术选型和团队管理
- 薪资范围:25k-40k+
7.3 持续学习建议
每周投入10小时学习新技术
- 关注Vue和React的版本更新
- 学习新的状态管理方案(如Zustand、Pinia)
- 了解新的构建工具(如Turbopack)
参与开源项目
- 在GitHub上贡献代码
- 阅读优秀开源项目的源码
- 尝试提交PR
建立个人品牌
- 写技术博客
- 在GitHub上展示项目
- 参加技术分享会
关注行业趋势
- WebAssembly
- 微前端架构
- Server Components
- 边缘计算
结语
掌握Vue和React需要持续的学习和实践。从基础语法到工程化,从个人项目到团队协作,每一步都是通向高薪的必经之路。记住,框架只是工具,核心是解决问题的能力。保持好奇心,持续学习,你一定能在这个行业获得成功。
最后建议:
- 不要急于求成,扎实基础
- 多写代码,多做项目
- 建立自己的知识体系
- 保持对新技术的热情
- 善于总结和分享
祝你学习顺利,早日拿到心仪的offer!
