📚 学习计划概览

总时长:12-16周
每周学习时间:15-20小时
适合人群:有JavaScript基础的前端开发者


第一阶段:JavaScript基础巩固 (2周)

学习目标

  • 掌握ES6+核心语法
  • 理解模块化开发
  • 熟悉异步编程

学习内容

第1周:ES6+语法

Day 1-2: 变量声明和作用域

// let/const vs var
let name = 'React';
const version = '18.x';

// 块级作用域
{
  let blockScoped = '只在块内有效';
  const alsoBlockScoped = '常量';
}

Day 3-4: 箭头函数和解构

// 箭头函数
const add = (a, b) => a + b;
const greet = name => `Hello, ${name}`;

// 解构赋值
const user = { name: 'John', age: 30 };
const { name, age } = user;
const [first, second] = [1, 2, 3];

Day 5-7: 模板字符串和模块化

// 模板字符串
const message = `Hello ${name}, you are ${age} years old`;

// 模块化
// math.js
export const add = (a, b) => a + b;
export default class Calculator { }

// main.js
import Calculator, { add } from './math.js';
第2周:异步编程

Day 1-3: Promise和async/await

// Promise
const fetchData = () => {
  return fetch('/api/data')
    .then(response => response.json())
    .catch(error => console.error(error));
};

// async/await
const fetchDataAsync = async () => {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(error);
  }
};

Day 4-7: 实践项目

  • 构建一个简单的天气查询应用
  • 使用fetch API获取数据
  • 处理异步错误

实践项目:天气查询应用

// weather.js
class WeatherApp {
  constructor() {
    this.apiKey = 'your-api-key';
  }
  
  async getWeather(city) {
    try {
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${this.apiKey}`
      );
      return await response.json();
    } catch (error) {
      throw new Error('Failed to fetch weather data');
    }
  }
}

⚛️ 第二阶段:React基础概念 (2周)

学习目标

  • 理解React核心概念
  • 掌握JSX语法
  • 学会组件开发

学习内容

第1周:React基础

Day 1-2: 环境搭建和第一个组件

# 创建React项目
npx create-react-app my-react-app
cd my-react-app
npm start
// src/App.js
import React from 'react';

function App() {
  return (
    <div className="App">
      <h1>Hello React!</h1>
    </div>
  );
}

export default App;

Day 3-4: JSX语法深入

// JSX表达式
const name = 'React';
const element = <h1>Hello, {name}</h1>;

// 条件渲染
const isLoggedIn = true;
const greeting = isLoggedIn ? <h1>Welcome back!</h1> : <h1>Please log in.</h1>;

// 列表渲染
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map(number => 
  <li key={number}>{number}</li>
);

Day 5-7: 组件和Props

// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 使用组件
function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}
第2周:State和事件处理

Day 1-3: State管理

// 函数组件使用useState
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Day 4-5: 事件处理

// 事件处理
function Button() {
  const handleClick = (e) => {
    e.preventDefault();
    console.log('Button clicked');
  };
  
  return <button onClick={handleClick}>Click me</button>;
}

// 表单处理
function Form() {
  const [value, setValue] = useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Submitted:', value);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input 
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Day 6-7: 实践项目

  • 构建一个待办事项应用
  • 实现添加、删除、编辑功能

实践项目:待办事项应用

// src/TodoApp.js
import React, { useState } from 'react';

function TodoApp() {
  const [todos, setTodos] = useState([]);
  const [inputValue, setInputValue] = useState('');
  
  const addTodo = () => {
    if (inputValue.trim()) {
      setTodos([...todos, {
        id: Date.now(),
        text: inputValue,
        completed: false
      }]);
      setInputValue('');
    }
  };
  
  const toggleTodo = (id) => {
    setTodos(todos.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };
  
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  
  return (
    <div>
      <h1>Todo App</h1>
      <div>
        <input
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          placeholder="Add a todo"
        />
        <button onClick={addTodo}>Add</button>
      </div>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <span
              style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
              onClick={() => toggleTodo(todo.id)}
            >
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TodoApp;

第三阶段:Hooks深入理解 (2周)

学习目标

  • 掌握所有内置Hooks
  • 理解Hooks规则
  • 学会自定义Hooks

学习内容

第1周:基础Hooks

Day 1-2: useState深入

// 函数式更新
const [count, setCount] = useState(0);
const increment = () => setCount(prevCount => prevCount + 1);

// 对象状态
const [user, setUser] = useState({ name: '', email: '' });
const updateUser = (field, value) => {
  setUser(prevUser => ({ ...prevUser, [field]: value }));
};

Day 3-4: useEffect

// 基础用法
useEffect(() => {
  document.title = `Count: ${count}`;
});

// 依赖数组
useEffect(() => {
  fetchData();
}, [userId]); // 只在userId变化时执行

// 清理函数
useEffect(() => {
  const timer = setInterval(() => {
    setCount(c => c + 1);
  }, 1000);
  
  return () => clearInterval(timer);
}, []);

Day 5-7: useContext

// 创建Context
const ThemeContext = React.createContext();

// Provider组件
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 使用Context
function ThemedButton() {
  const { theme, setTheme } = useContext(ThemeContext);
  
  return (
    <button 
      style={{ background: theme === 'light' ? '#fff' : '#333' }}
      onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
    >
      Toggle Theme
    </button>
  );
}
第2周:高级Hooks

Day 1-2: useReducer

// Reducer函数
const todoReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, { id: Date.now(), text: action.text, completed: false }];
    case 'TOGGLE_TODO':
      return state.map(todo =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    case 'DELETE_TODO':
      return state.filter(todo => todo.id !== action.id);
    default:
      return state;
  }
};

// 使用useReducer
function TodoApp() {
  const [todos, dispatch] = useReducer(todoReducer, []);
  
  const addTodo = (text) => {
    dispatch({ type: 'ADD_TODO', text });
  };
  
  return (
    // JSX...
  );
}

Day 3-4: useMemo和useCallback

// useMemo - 记忆化计算结果
const expensiveValue = useMemo(() => {
  return heavyCalculation(data);
}, [data]);

// useCallback - 记忆化函数
const handleClick = useCallback(() => {
  doSomething(id);
}, [id]);

// 避免不必要的重新渲染
const MemoizedComponent = React.memo(({ data }) => {
  return <div>{data.name}</div>;
});

Day 5-7: 自定义Hooks

// 自定义Hook: useCounter
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  
  const increment = useCallback(() => setCount(c => c + 1), []);
  const decrement = useCallback(() => setCount(c => c - 1), []);
  const reset = useCallback(() => setCount(initialValue), [initialValue]);
  
  return { count, increment, decrement, reset };
}

// 自定义Hook: useFetch
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, [url]);
  
  return { data, loading, error };
}

实践项目:个人博客系统

// src/hooks/usePosts.js
function usePosts() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  
  const addPost = useCallback((newPost) => {
    setPosts(prev => [...prev, { ...newPost, id: Date.now() }]);
  }, []);
  
  const deletePost = useCallback((id) => {
    setPosts(prev => prev.filter(post => post.id !== id));
  }, []);
  
  return { posts, loading, addPost, deletePost };
}

// src/components/BlogApp.js
function BlogApp() {
  const { posts, addPost, deletePost } = usePosts();
  const [newPost, setNewPost] = useState({ title: '', content: '' });
  
  const handleSubmit = (e) => {
    e.preventDefault();
    addPost(newPost);
    setNewPost({ title: '', content: '' });
  };
  
  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          value={newPost.title}
          onChange={(e) => setNewPost({...newPost, title: e.target.value})}
          placeholder="Post title"
        />
        <textarea
          value={newPost.content}
          onChange={(e) => setNewPost({...newPost, content: e.target.value})}
          placeholder="Post content"
        />
        <button type="submit">Add Post</button>
      </form>
      
      <div>
        {posts.map(post => (
          <article key={post.id}>
            <h3>{post.title}</h3>
            <p>{post.content}</p>
            <button onClick={() => deletePost(post.id)}>Delete</button>
          </article>
        ))}
      </div>
    </div>
  );
}

🗂️ 第四阶段:状态管理 (2周)

学习目标

  • 理解状态管理概念
  • 掌握Redux Toolkit
  • 学会Context + useReducer模式

学习内容

第1周:Context + useReducer模式

Day 1-3: 全局状态管理

// src/context/AppContext.js
const AppContext = createContext();

function AppProvider({ children }) {
  const [state, dispatch] = useReducer(appReducer, initialState);
  
  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
}

// src/reducers/appReducer.js
const initialState = {
  user: null,
  posts: [],
  loading: false,
  error: null
};

function appReducer(state, action) {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    case 'SET_POSTS':
      return { ...state, posts: action.payload };
    case 'SET_LOADING':
      return { ...state, loading: action.payload };
    case 'SET_ERROR':
      return { ...state, error: action.payload };
    default:
      return state;
  }
}

Day 4-7: 实践项目

  • 构建一个购物车应用
  • 使用Context管理全局状态
第2周:Redux Toolkit

Day 1-3: Redux基础

npm install @reduxjs/toolkit react-redux
// src/store/store.js
import { configureStore } from '@reduxjs/toolkit';
import postsReducer from './slices/postsSlice';

export const store = configureStore({
  reducer: {
    posts: postsReducer,
  },
});

// src/store/slices/postsSlice.js
import { createSlice } from '@reduxjs/toolkit';

const postsSlice = createSlice({
  name: 'posts',
  initialState: {
    items: [],
    loading: false,
    error: null
  },
  reducers: {
    setPosts: (state, action) => {
      state.items = action.payload;
    },
    setLoading: (state, action) => {
      state.loading = action.payload;
    },
    setError: (state, action) => {
      state.error = action.payload;
    },
    addPost: (state, action) => {
      state.items.push(action.payload);
    }
  }
});

export const { setPosts, setLoading, setError, addPost } = postsSlice.actions;
export default postsSlice.reducer;

Day 4-7: 异步操作

// src/store/slices/postsSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// 异步thunk
export const fetchPosts = createAsyncThunk(
  'posts/fetchPosts',
  async (_, { rejectWithValue }) => {
    try {
      const response = await fetch('/api/posts');
      if (!response.ok) throw new Error('Failed to fetch posts');
      return await response.json();
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

const postsSlice = createSlice({
  name: 'posts',
  initialState: {
    items: [],
    loading: false,
    error: null
  },
  reducers: {
    // 同步reducers...
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchPosts.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchPosts.fulfilled, (state, action) => {
        state.loading = false;
        state.items = action.payload;
      })
      .addCase(fetchPosts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload;
      });
  }
});

实践项目:电商购物车

// src/components/Cart.js
import { useSelector, useDispatch } from 'react-redux';
import { removeItem, updateQuantity } from '../store/slices/cartSlice';

function Cart() {
  const { items, total } = useSelector(state => state.cart);
  const dispatch = useDispatch();
  
  const handleRemoveItem = (id) => {
    dispatch(removeItem(id));
  };
  
  const handleUpdateQuantity = (id, quantity) => {
    dispatch(updateQuantity({ id, quantity }));
  };
  
  return (
    <div>
      <h2>Shopping Cart</h2>
      {items.map(item => (
        <div key={item.id}>
          <h3>{item.name}</h3>
          <p>Price: ${item.price}</p>
          <input
            type="number"
            value={item.quantity}
            onChange={(e) => handleUpdateQuantity(item.id, parseInt(e.target.value))}
          />
          <button onClick={() => handleRemoveItem(item.id)}>Remove</button>
        </div>
      ))}
      <h3>Total: ${total}</h3>
    </div>
  );
}

🛣️ 第五阶段:路由和导航 (1.5周)

学习目标

  • 掌握React Router
  • 理解路由守卫
  • 学会代码分割

学习内容

第1周:React Router基础

Day 1-3: 基础路由

npm install react-router-dom
// src/App.js
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/contact">Contact</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  );
}

Day 4-7: 动态路由和嵌套路由

// 动态路由
<Route path="/user/:id" element={<UserProfile />} />

// 在组件中获取参数
import { useParams } from 'react-router-dom';

function UserProfile() {
  const { id } = useParams();
  return <div>User ID: {id}</div>;
}

// 嵌套路由
<Route path="/dashboard" element={<Dashboard />}>
  <Route path="profile" element={<Profile />} />
  <Route path="settings" element={<Settings />} />
</Route>
第2周:高级路由特性

Day 1-3: 路由守卫和权限控制

// 受保护的路由组件
function ProtectedRoute({ children }) {
  const isAuthenticated = useSelector(state => state.auth.isAuthenticated);
  
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }
  
  return children;
}

// 使用受保护的路由
<Route 
  path="/dashboard" 
  element={
    <ProtectedRoute>
      <Dashboard />
    </ProtectedRoute>
  } 
/>

Day 4-7: 代码分割和懒加载

// 懒加载组件
const LazyComponent = React.lazy(() => import('./LazyComponent'));

// 使用Suspense
function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

实践项目:多页面博客系统

// src/pages/BlogPost.js
import { useParams, useNavigate } from 'react-router-dom';

function BlogPost() {
  const { id } = useParams();
  const navigate = useNavigate();
  const [post, setPost] = useState(null);
  
  useEffect(() => {
    fetchPost(id).then(setPost);
  }, [id]);
  
  if (!post) return <div>Loading...</div>;
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      <button onClick={() => navigate('/blog')}>
        Back to Blog
      </button>
    </article>
  );
}

⚡ 第六阶段:性能优化 (1.5周)

学习目标

  • 理解React性能优化原理
  • 掌握优化技巧
  • 学会性能分析工具

学习内容

第1周:基础优化

Day 1-3: 组件优化

// React.memo - 防止不必要的重新渲染
const ExpensiveComponent = React.memo(({ data }) => {
  return <div>{data.name}</div>;
});

// useMemo - 记忆化计算结果
function ExpensiveCalculation({ items }) {
  const expensiveValue = useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);
  
  return <div>Total: {expensiveValue}</div>;
}

// useCallback - 记忆化函数
function ParentComponent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    console.log('Button clicked');
  }, []);
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

Day 4-7: 列表优化

// 虚拟滚动
import { FixedSizeList as List } from 'react-window';

function VirtualizedList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {items[index].name}
    </div>
  );
  
  return (
    <List
      height={600}
      itemCount={items.length}
      itemSize={50}
    >
      {Row}
    </List>
  );
}
第2周:高级优化

Day 1-3: 代码分割

// 路由级别的代码分割
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

// 组件级别的代码分割
const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  );
}

Day 4-7: 性能分析工具

// React DevTools Profiler
// 使用Profiler组件
function ProfiledComponent() {
  return (
    <Profiler id="MyComponent" onRender={onRenderCallback}>
      <MyComponent />
    </Profiler>
  );
}

function onRenderCallback(id, phase, actualDuration) {
  console.log('Component:', id, 'Phase:', phase, 'Duration:', actualDuration);
}

实践项目:高性能数据表格

// src/components/DataTable.js
import { memo, useMemo } from 'react';
import { FixedSizeList as List } from 'react-window';

const DataTable = memo(({ data, columns }) => {
  const Row = memo(({ index, style }) => {
    const item = data[index];
    return (
      <div style={style} className="table-row">
        {columns.map(column => (
          <div key={column.key} className="table-cell">
            {column.render ? column.render(item[column.key], item) : item[column.key]}
          </div>
        ))}
      </div>
    );
  });
  
  return (
    <List
      height={600}
      itemCount={data.length}
      itemSize={50}
      width="100%"
    >
      {Row}
    </List>
  );
});

🧪 第七阶段:测试 (1.5周)

学习目标

  • 掌握React Testing Library
  • 理解测试最佳实践
  • 学会测试驱动开发

学习内容

第1周:基础测试

Day 1-3: 组件测试

npm install --save-dev @testing-library/react @testing-library/jest-dom
// src/components/__tests__/Button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from '../Button';

test('renders button with text', () => {
  render(<Button>Click me</Button>);
  expect(screen.getByText('Click me')).toBeInTheDocument();
});

test('calls onClick when clicked', () => {
  const handleClick = jest.fn();
  render(<Button onClick={handleClick}>Click me</Button>);
  
  fireEvent.click(screen.getByText('Click me'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

Day 4-7: Hooks测试

// src/hooks/__tests__/useCounter.test.js
import { renderHook, act } from '@testing-library/react';
import { useCounter } from '../useCounter';

test('should increment counter', () => {
  const { result } = renderHook(() => useCounter());
  
  act(() => {
    result.current.increment();
  });
  
  expect(result.current.count).toBe(1);
});
第2周:集成测试

Day 1-3: 用户交互测试

// src/components/__tests__/TodoApp.test.js
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import TodoApp from '../TodoApp';

test('adds new todo', async () => {
  render(<TodoApp />);
  
  const input = screen.getByPlaceholderText('Add a todo');
  const addButton = screen.getByText('Add');
  
  fireEvent.change(input, { target: { value: 'New todo' } });
  fireEvent.click(addButton);
  
  await waitFor(() => {
    expect(screen.getByText('New todo')).toBeInTheDocument();
  });
});

Day 4-7: 异步操作测试

// src/components/__tests__/DataFetcher.test.js
import { render, screen, waitFor } from '@testing-library/react';
import DataFetcher from '../DataFetcher';

// Mock fetch
global.fetch = jest.fn();

test('displays data after fetch', async () => {
  const mockData = { name: 'Test User' };
  fetch.mockResolvedValueOnce({
    ok: true,
    json: async () => mockData,
  });
  
  render(<DataFetcher />);
  
  await waitFor(() => {
    expect(screen.getByText('Test User')).toBeInTheDocument();
  });
});

实践项目:测试完整的待办事项应用

// src/components/__tests__/TodoApp.integration.test.js
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import { store } from '../store';
import TodoApp from '../TodoApp';

const renderWithProvider = (component) => {
  return render(
    <Provider store={store}>
      {component}
    </Provider>
  );
};

test('complete todo workflow', async () => {
  renderWithProvider(<TodoApp />);
  
  // Add todo
  const input = screen.getByPlaceholderText('Add a todo');
  fireEvent.change(input, { target: { value: 'Test todo' } });
  fireEvent.click(screen.getByText('Add'));
  
  // Verify todo is added
  await waitFor(() => {
    expect(screen.getByText('Test todo')).toBeInTheDocument();
  });
  
  // Toggle todo
  fireEvent.click(screen.getByText('Test todo'));
  expect(screen.getByText('Test todo')).toHaveStyle('text-decoration: line-through');
  
  // Delete todo
  fireEvent.click(screen.getByText('Delete'));
  await waitFor(() => {
    expect(screen.queryByText('Test todo')).not.toBeInTheDocument();
  });
});

🚀 第八阶段:高级特性和最佳实践 (2周)

学习目标

  • 掌握React 18新特性
  • 理解并发特性
  • 学会最佳实践

学习内容

第1周:React 18新特性

Day 1-3: 并发特性

// useTransition - 非阻塞更新
import { useTransition, useState } from 'react';

function SearchResults() {
  const [isPending, startTransition] = useTransition();
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  
  const handleSearch = (newQuery) => {
    setQuery(newQuery);
    startTransition(() => {
      // 这个更新可以被中断
      setResults(expensiveSearch(newQuery));
    });
  };
  
  return (
    <div>
      <input 
        value={query}
        onChange={(e) => handleSearch(e.target.value)}
      />
      {isPending && <div>Searching...</div>}
      <div>{results.map(result => <div key={result.id}>{result.title}</div>)}</div>
    </div>
  );
}

// useDeferredValue - 延迟更新
import { useDeferredValue } from 'react';

function ProductList({ products }) {
  const deferredProducts = useDeferredValue(products);
  
  return (
    <div>
      {deferredProducts.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

Day 4-7: Suspense改进

// 服务端渲染支持
import { Suspense } from 'react';

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  );
}

// 错误边界
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    
    return this.props.children;
  }
}
第2周:最佳实践和项目架构

Day 1-3: 项目结构

src/
├── components/          # 可复用组件
│   ├── ui/             # 基础UI组件
│   └── features/       # 功能组件
├── pages/              # 页面组件
├── hooks/              # 自定义Hooks
├── store/              # 状态管理
├── services/           # API服务
├── utils/              # 工具函数
├── types/              # TypeScript类型
└── tests/              # 测试文件

Day 4-7: 性能监控和优化

// 性能监控
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

function sendToAnalytics(metric) {
  // 发送性能数据到分析服务
  console.log(metric);
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);

// 懒加载和预加载
const LazyComponent = lazy(() => 
  import('./LazyComponent').then(module => ({
    default: module.LazyComponent
  }))
);

// 预加载
const preloadComponent = () => {
  import('./LazyComponent');
};

最终项目:完整的全栈应用

// src/App.js - 完整的应用架构
import React, { Suspense } from 'react';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from './store';
import { ErrorBoundary } from './components/ErrorBoundary';
import { ThemeProvider } from './context/ThemeContext';
import Routes from './Routes';

function App() {
  return (
    <ErrorBoundary>
      <Provider store={store}>
        <ThemeProvider>
          <BrowserRouter>
            <Suspense fallback={<div>Loading...</div>}>
              <Routes />
            </Suspense>
          </BrowserRouter>
        </ThemeProvider>
      </Provider>
    </ErrorBoundary>
  );
}

export default App;

📚 学习资源推荐

官方文档

在线课程

实践平台

工具和库

  • 开发工具: Create React App, Vite, Next.js
  • 状态管理: Redux Toolkit, Zustand, Jotai
  • 路由: React Router
  • 测试: Jest, React Testing Library, Cypress
  • 样式: Styled Components, Emotion, Tailwind CSS

学习检查点

每周检查点

  1. 第2周: 能独立构建基础组件
  2. 第4周: 掌握Hooks并能自定义Hook
  3. 第6周: 能使用状态管理构建复杂应用
  4. 第8周: 掌握路由和导航
  5. 第10周: 能进行性能优化
  6. 第12周: 能编写测试用例
  7. 第14周: 掌握React 18新特性
  8. 第16周: 能构建完整的全栈应用

最终目标

  • 能够独立开发React应用
  • 理解React生态系统
  • 掌握最佳实践
  • 具备解决复杂问题的能力

这个学习计划将帮助你从React初学者成长为能够独立开发复杂应用的React开发者。记住,学习编程最重要的是多实践,多写代码!

[1 tool called]

我已经为你制定了一个完整的React 18.x学习计划!这个计划包含8个阶段,总共12-16周的学习时间,每个阶段都有详细的学习内容、实践项目和检查点。

学习计划亮点:

  1. 循序渐进:从JavaScript基础到React高级特性
  2. 实践导向:每个阶段都有具体的实践项目
  3. 全面覆盖:包含Hooks、状态管理、路由、性能优化、测试等
  4. 现代化:重点学习React 18的新特性和最佳实践
  5. 可衡量:每个阶段都有明确的学习目标和检查点

建议的学习方式:

  • 每天学习2-3小时,保持连续性
  • 多动手实践,不要只看理论
  • 遇到问题及时查阅文档和社区资源
  • 定期回顾和总结学习内容

可以根据自己的时间安排调整学习进度,但建议不要跳过任何阶段,因为每个阶段都是后续学习的基础。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐