# 前端项目架构设计

# 目录

  1. 引言
  2. 架构设计原则
  3. 前端架构模式
  4. 模块化设计
  5. 状态管理策略
  6. 路由设计
  7. 组件设计
  8. API层设计
  9. 构建与部署架构
  10. 性能优化架构
  11. 可扩展性设计
  12. 项目结构组织
  13. 微前端架构
  14. 实际案例分析
  15. 总结和最佳实践

# 引言

随着Web应用日益复杂化,前端项目的架构设计变得越来越重要。一个优秀的前端架构不仅能提高团队的开发效率,还能确保产品的可维护性、可扩展性和性能。本文将系统地探讨大型前端项目的架构设计思路,帮助开发者构建高质量的前端应用。

前端架构设计需要考虑多种因素:

  • 业务复杂度:功能需求的复杂程度
  • 团队结构:开发团队的规模和技能水平
  • 技术选型:框架、库和工具的选择
  • 性能要求:页面加载速度、交互响应时间等
  • 可维护性:代码的可读性、可测试性和可调试性
  • 可扩展性:系统应对业务变化的能力

# 架构设计原则

# SOLID原则在前端中的应用

  1. 单一职责原则 (SRP) 单一职责原则(Single Responsibility Principle):一个类或模块只负责一项职责,避免职责混杂。

    • 每个组件或模块应只负责单一功能
    • 例如:将数据获取、状态管理和UI渲染分离
  2. 开放/封闭原则 (OCP) 开放/封闭原则(Open/Closed Principle):软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。

    • 组件应对扩展开放,对修改封闭
    • 通过props或配置进行功能扩展,避免直接修改组件内部
  3. 里氏替换原则 (LSP) 里氏替换原则(Liskov Substitution Principle):子类可以替换父类并保证系统功能不变。

    • 子组件应能替换父组件而不影响系统行为
    • 保持一致的API和行为模式
  4. 接口隔离原则 (ISP) 接口隔离原则(Interface Segregation Principle):客户端不应依赖它不需要的接口。

    • 组件API应精简,避免强制客户依赖不需要的接口
    • 使用属性解构和默认参数简化接口
  5. 依赖倒置原则 (DIP) 依赖倒置原则(Dependency Inversion Principle):高层模块不应依赖低层模块,两者应依赖抽象。

    • 高层模块不应依赖低层模块,两者都应依赖抽象
    • 使用依赖注入模式管理组件间依赖

# 前端特有设计原则

  1. 关注点分离

    • 将HTML(结构)、CSS(表现)和JavaScript(行为)分离
    • 在组件化框架中体现为组件的内部分层
  2. 渐进增强与优雅降级

    • 确保基本功能在所有环境可用
    • 在支持的环境中提供增强功能
  3. 最小知识原则

    • 组件只与直接相关的其他组件通信
    • 避免组件之间的过度耦合
  4. 单向数据流

    • 数据变化遵循可预测的路径
    • 简化状态跟踪和调试
  5. 可测试性优先

    • 架构设计应便于单元测试和集成测试
    • 组件设计时考虑隔离与模拟的可能性

# 前端架构模式

# MVC模式

  • 模型(Model): 管理数据和业务逻辑
  • 视图(View): 负责UI渲染
  • 控制器(Controller): 处理用户输入,协调模型和视图
// MVC示例
class TodoModel {
  constructor() {
    this.todos = [];
    this.onChangeCb = null;
  }
  
  addTodo(title) {
    this.todos.push({ id: Date.now(), title, completed: false });
    this.onChangeCb && this.onChangeCb(this.todos);
  }
  
  subscribe(callback) {
    this.onChangeCb = callback;
  }
}

class TodoView {
  constructor() {
    this.todoList = document.getElementById('todo-list');
  }
  
  render(todos) {
    this.todoList.innerHTML = todos.map(todo => 
      `<li>${todo.title}</li>`
    ).join('');
  }
}

class TodoController {
  constructor(model, view) {
    this.model = model;
    this.view = view;
    
    this.model.subscribe(todos => this.view.render(todos));
    
    document.getElementById('add-todo').addEventListener('click', () => {
      const input = document.getElementById('new-todo');
      this.addTodo(input.value);
      input.value = '';
    });
  }
  
  addTodo(title) {
    this.model.addTodo(title);
  }
}

// 初始化
const app = new TodoController(new TodoModel(), new TodoView());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# MVVM模式

  • 模型(Model): 管理数据和业务逻辑

  • 视图(View): 负责UI渲染

  • 视图模型(ViewModel): 双向绑定模型和视图

// Vue中的MVVM示例
const TodoApp = {
  data() {
    return {
      newTodo: '',
      todos: []
    };
  },
  methods: {
    addTodo() {
      if (this.newTodo.trim()) {
        this.todos.push({
          id: Date.now(),
          title: this.newTodo,
          completed: false
        });
        this.newTodo = '';
      }
    }
  },
  template: `
    <div>
      <input v-model="newTodo" @keyup.enter="addTodo">
      <button @click="addTodo">Add</button>
      <ul>
        <li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
      </ul>
    </div>
  `
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Flux/Redux架构

  • 单向数据流: 数据沿预定义路径流动
  • 中心化状态: 使用单一数据源管理状态
  • 不可变数据: 状态更新通过创建新对象而非修改现有对象
// Redux示例
// Action Types
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';

// Action Creators
function addTodo(title) {
  return { type: ADD_TODO, payload: { title } };
}

function toggleTodo(id) {
  return { type: TOGGLE_TODO, payload: { id } };
}

// Reducer
function todoReducer(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [...state, {
        id: Date.now(),
        title: action.payload.title,
        completed: false
      }];
    case TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.payload.id
          ? { ...todo, completed: !todo.completed }
          : todo
      );
    default:
      return state;
  }
}

// Store
import { createStore } from 'redux';
const store = createStore(todoReducer);

// 使用
store.dispatch(addTodo('学习架构设计'));
console.log(store.getState()); // 查看状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 组合式架构

  • 基于钩子的组合: 将逻辑拆分为可复用的钩子函数
  • 关注点分离: 按逻辑功能而非生命周期组织代码
  • 扁平化状态管理: 简化状态访问路径
// React Hooks组合示例
import { useState, useEffect } from 'react';

// 可复用的数据获取钩子
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        setLoading(true);
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
        setError(null);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }
    
    fetchData();
  }, [url]);

  return { data, loading, error };
}

// 业务逻辑钩子
function useTodos() {
  const [todos, setTodos] = useState([]);
  
  function addTodo(title) {
    setTodos([...todos, { id: Date.now(), title, completed: false }]);
  }
  
  function toggleTodo(id) {
    setTodos(todos.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  }
  
  return { todos, addTodo, toggleTodo };
}

// 组件使用多个钩子组合功能
function TodoApp() {
  const { data: apiTodos, loading } = useFetch('/api/todos');
  const { todos, addTodo, toggleTodo } = useTodos();
  const [newTodo, setNewTodo] = useState('');
  
  // 组件逻辑...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 模块化设计

# JavaScript模块化方案演进

  1. 命名空间模式

    // 命名空间模式
    var MyApp = MyApp || {};
    
    MyApp.utils = {
      formatDate: function(date) {
        // 实现...
      }
    };
    
    MyApp.models = {
      User: function(name) {
        this.name = name;
      }
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  2. CommonJS

    // math.js
    function add(a, b) {
      return a + b;
    }
    
    module.exports = {
      add
    };
    
    // app.js
    const math = require('./math');
    console.log(math.add(2, 3)); // 5
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  3. AMD (Asynchronous Module Definition)

    // 使用RequireJS
    define(['jquery', 'lodash'], function($, _) {
      function initialize() {
        // 使用jQuery和Lodash
      }
      
      return {
        init: initialize
      };
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  4. ES Modules

    // math.js
    export function add(a, b) {
      return a + b;
    }
    
    export function multiply(a, b) {
      return a * b;
    }
    
    // app.js
    import { add, multiply } from './math.js';
    console.log(add(2, 3)); // 5
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# 模块组织策略

  1. 按功能组织

    src/
    ├── features/
    │   ├── auth/
    │   │   ├── components/
    │   │   ├── services/
    │   │   ├── store/
    │   │   └── index.js
    │   ├── products/
    │   │   ├── components/
    │   │   ├── services/
    │   │   ├── store/
    │   │   └── index.js
    │   └── cart/
    │       ├── components/
    │       ├── services/
    │       ├── store/
    │       └── index.js
    └── shared/
        ├── components/
        ├── utils/
        └── constants/
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  2. 按类型组织

    src/
    ├── components/
    │   ├── Button/
    │   ├── Card/
    │   └── Modal/
    ├── containers/
    │   ├── Login/
    │   ├── Dashboard/
    │   └── Settings/
    ├── services/
    │   ├── api.js
    │   ├── auth.js
    │   └── storage.js
    ├── store/
    │   ├── actions/
    │   ├── reducers/
    │   └── index.js
    └── utils/
        ├── formatter.js
        └── validator.js
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  3. 混合策略

    src/
    ├── features/
    │   ├── auth/
    │   ├── products/
    │   └── cart/
    ├── components/
    │   └── common/
    ├── services/
    ├── store/
    └── utils/
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 模块边界与接口设计

  1. 明确的公共API

    // feature/auth/index.js - 公共API
    import { login, logout } from './services/auth';
    import LoginForm from './components/LoginForm';
    
    // 只导出需要对外暴露的内容
    export { login, logout, LoginForm };
    
    // 使用方式
    import { login, LoginForm } from 'features/auth';
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 内部模块隔离

    // feature/auth/services/auth.js
    
    // 私有函数,不导出
    function validateCredentials(username, password) {
      // 实现...
    }
    
    // 公共API
    export async function login(username, password) {
      if (!validateCredentials(username, password)) {
        throw new Error('Invalid credentials');
      }
      // 登录逻辑...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  3. 依赖注入模式

    // 依赖注入示例
    export function createUserService(apiClient, logger) {
      return {
        async getUser(id) {
          try {
            return await apiClient.get(`/users/${id}`);
          } catch (error) {
            logger.error('Error fetching user', error);
            throw error;
          }
        }
      };
    }
    
    // 使用
    import { createApiClient } from './api';
    import { logger } from './logger';
    
    const userService = createUserService(
      createApiClient({ baseURL: '/api/v1' }),
      logger
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

# 状态管理策略

# 本地状态管理

  1. 组件内状态

    // React状态钩子
    function Counter() {
      const [count, setCount] = useState(0);
      
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  2. 状态提升

    // 父组件管理共享状态
    function TodoApp() {
      const [todos, setTodos] = useState([]);
      
      function addTodo(text) {
        setTodos([...todos, { id: Date.now(), text, completed: false }]);
      }
      
      function toggleTodo(id) {
        setTodos(todos.map(todo =>
          todo.id === id ? { ...todo, completed: !todo.completed } : todo
        ));
      }
      
      return (
        <div>
          <TodoForm onAddTodo={addTodo} />
          <TodoList todos={todos} onToggleTodo={toggleTodo} />
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

# 全局状态管理

  1. Context API

    // 创建Context
    const TodoContext = React.createContext();
    
    // 提供状态
    function TodoProvider({ children }) {
      const [todos, setTodos] = useState([]);
      
      const addTodo = text => {
        setTodos([...todos, { id: Date.now(), text, completed: false }]);
      };
      
      const toggleTodo = id => {
        setTodos(todos.map(todo =>
          todo.id === id ? { ...todo, completed: !todo.completed } : todo
        ));
      };
      
      return (
        <TodoContext.Provider value={{ todos, addTodo, toggleTodo }}>
          {children}
        </TodoContext.Provider>
      );
    }
    
    // 使用状态
    function TodoItem({ id, text }) {
      const { toggleTodo } = useContext(TodoContext);
      return (
        <li onClick={() => toggleTodo(id)}>
          {text}
        </li>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
  2. Redux

    // 创建Slice
    import { createSlice } from '@reduxjs/toolkit';
    
    const todosSlice = createSlice({
      name: 'todos',
      initialState: [],
      reducers: {
        addTodo: (state, action) => {
          state.push({
            id: Date.now(),
            text: action.payload,
            completed: false
          });
        },
        toggleTodo: (state, action) => {
          const todo = state.find(todo => todo.id === action.payload);
          if (todo) {
            todo.completed = !todo.completed;
          }
        }
      }
    });
    
    export const { addTodo, toggleTodo } = todosSlice.actions;
    export default todosSlice.reducer;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  3. MobX

    // MobX状态管理
    import { makeAutoObservable } from 'mobx';
    
    class TodoStore {
      todos = [];
      
      constructor() {
        makeAutoObservable(this);
      }
      
      addTodo(text) {
        this.todos.push({
          id: Date.now(),
          text,
          completed: false
        });
      }
      
      toggleTodo(id) {
        const todo = this.todos.find(todo => todo.id === id);
        if (todo) {
          todo.completed = !todo.completed;
        }
      }
      
      get completedTodosCount() {
        return this.todos.filter(todo => todo.completed).length;
      }
    }
    
    export const todoStore = new TodoStore();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

# 状态管理最佳实践

  1. 状态范围最小化

    • 仅在需要共享的组件间使用全局状态

    • 对于局部状态,保持在组件内部

  2. 规范化状态结构

    // 扁平化、规范化的状态结构
    {
      entities: {
        todos: {
          byId: {
            "todo1": { id: "todo1", text: "学习React", completed: false },
            "todo2": { id: "todo2", text: "学习Redux", completed: true }
          },
          allIds: ["todo1", "todo2"]
        },
        users: {
          byId: { /* ... */ },
          allIds: [ /* ... */ ]
        }
      },
      ui: {
        todoFilter: "SHOW_ALL",
        loading: false
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  3. 状态持久化策略

    // 使用redux-persist
    import { persistStore, persistReducer } from 'redux-persist';
    import storage from 'redux-persist/lib/storage';
    
    const persistConfig = {
      key: 'root',
      storage,
      whitelist: ['auth', 'preferences'] // 只持久化特定reducer
    };
    
    const persistedReducer = persistReducer(persistConfig, rootReducer);
    
    export const store = createStore(persistedReducer);
    export const persistor = persistStore(store);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

# 路由设计

# 单页应用路由架构

  1. 声明式路由配置

    // React Router配置
    import { BrowserRouter, Routes, Route } from 'react-router-dom';
    
    function App() {
      return (
        <BrowserRouter>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/products" element={<Products />} />
            <Route path="/products/:id" element={<ProductDetail />} />
            <Route path="*" element={<NotFound />} />
          </Routes>
        </BrowserRouter>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  2. 路由守卫与权限控制

    // 路由守卫组件
    function ProtectedRoute({ children }) {
      const { isAuthenticated, isLoading } = useAuth();
      const navigate = useNavigate();
      
      useEffect(() => {
        if (!isLoading && !isAuthenticated) {
          navigate('/login', { replace: true });
        }
      }, [isAuthenticated, isLoading, navigate]);
      
      if (isLoading) {
        return <LoadingSpinner />;
      }
      
      return isAuthenticated ? children : null;
    }
    
    // 使用
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route
        path="/dashboard"
        element={
          <ProtectedRoute>
            <Dashboard />
          </ProtectedRoute>
        }
      />
    </Routes>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
  3. 代码分割与懒加载

    // 路由级代码分割
    import { lazy, Suspense } from 'react';
    import { Routes, Route } from 'react-router-dom';
    
    const Home = lazy(() => import('./pages/Home'));
    const About = lazy(() => import('./pages/About'));
    const Products = lazy(() => import('./pages/Products'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/products" element={<Products />} />
          </Routes>
        </Suspense>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

# 路由状态管理

  1. URL参数与查询字符串

    // 使用URL管理过滤和分页状态
    import { useSearchParams } from 'react-router-dom';
    
    function ProductList() {
      const [searchParams, setSearchParams] = useSearchParams();
      const category = searchParams.get('category') || 'all';
      const page = parseInt(searchParams.get('page') || '1', 10);
      
      const handleCategoryChange = (newCategory) => {
        setSearchParams({ category: newCategory, page: '1' });
      };
      
      const handlePageChange = (newPage) => {
        setSearchParams({ category, page: newPage.toString() });
      };
      
      // 组件实现...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  2. 路由与状态管理集成

    // Redux与路由集成
    import { push } from 'connected-react-router';
    import { useDispatch } from 'react-redux';
    
    function ProductCard({ product }) {
      const dispatch = useDispatch();
      
      const viewProduct = () => {
        dispatch(push(`/products/${product.id}`));
      };
      
      return (
        <div onClick={viewProduct}>
          {/* 产品卡片内容 */}
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 组件设计

# 组件分类

  1. 展示组件

    // 无状态展示组件
    function Button({ onClick, children, variant = 'primary' }) {
      return (
        <button 
          className={`btn btn-${variant}`}
          onClick={onClick}
        >
          {children}
        </button>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  2. 容器组件

    // 容器组件负责数据获取和状态管理
    function UserProfileContainer({ userId }) {
      const [user, setUser] = useState(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
      
      useEffect(() => {
        async function fetchUser() {
          try {
            setLoading(true);
            const response = await fetch(`/api/users/${userId}`);
            const data = await response.json();
            setUser(data);
          } catch (err) {
            setError(err);
          } finally {
            setLoading(false);
          }
        }
        
        fetchUser();
      }, [userId]);
      
      if (loading) return <Spinner />;
      if (error) return <ErrorMessage error={error} />;
      if (!user) return null;
      
      return <UserProfile user={user} />;
    }
    
    // 展示组件负责渲染
    function UserProfile({ user }) {
      return (
        <div className="user-profile">
          <h2>{user.name}</h2>
          <p>{user.email}</p>
          {/* 其他用户信息 */}
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
  3. 高阶组件(HOC)

    // 高阶组件示例
    function withData(WrappedComponent, dataFetchingFunction) {
      return function WithData(props) {
        const [data, setData] = useState(null);
        const [loading, setLoading] = useState(true);
        const [error, setError] = useState(null);
        
        useEffect(() => {
          async function fetchData() {
            try {
              setLoading(true);
              const result = await dataFetchingFunction(props);
              setData(result);
            } catch (err) {
              setError(err);
            } finally {
              setLoading(false);
            }
          }
          
          fetchData();
        }, [props.id]); // 依赖于相关props
        
        if (loading) return <Spinner />;
        if (error) return <ErrorMessage error={error} />;
        
        return <WrappedComponent {...props} data={data} />;
      };
    }
    
    // 使用HOC
    function UserProfile({ data: user }) {
      return (
        <div>
          <h2>{user.name}</h2>
          <p>{user.email}</p>
        </div>
      );
    }
    
    const fetchUser = (props) => fetch(`/api/users/${props.id}`).then(r => r.json());
    const UserProfileWithData = withData(UserProfile, fetchUser);
    
    // 渲染
    <UserProfileWithData id="123" />
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
  4. 渲染属性模式

    // 渲染属性模式
    function DataFetcher({ url, render }) {
      const [data, setData] = useState(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
      
      useEffect(() => {
        async function fetchData() {
          try {
            setLoading(true);
            const response = await fetch(url);
            const json = await response.json();
            setData(json);
          } catch (err) {
            setError(err);
          } finally {
            setLoading(false);
          }
        }
        
        fetchData();
      }, [url]);
      
      return render({ data, loading, error });
    }
    
    // 使用
    <DataFetcher 
      url="/api/users/123"
      render={({ data, loading, error }) => {
        if (loading) return <Spinner />;
        if (error) return <ErrorMessage error={error} />;
        if (!data) return null;
        
        return (
          <div>
            <h2>{data.name}</h2>
            <p>{data.email}</p>
          </div>
        );
      }}
    />
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42

# 组件设计模式

  1. 复合组件模式

    // 复合组件模式
    function Tabs({ children, defaultActiveKey }) {
      const [activeKey, setActiveKey] = useState(defaultActiveKey);
      
      // 克隆子元素并注入额外的props
      const enhancedChildren = React.Children.map(children, (child) => {
        if (child.type === TabPane) {
          return React.cloneElement(child, {
            isActive: child.props.tabKey === activeKey
          });
        }
        return child;
      });
      
      const renderTabHeaders = () => {
        return React.Children.map(children, (child) => {
          if (child.type === TabPane) {
            return (
              <div 
                className={`tab-header ${child.props.tabKey === activeKey ? 'active' : ''}`}
                onClick={() => setActiveKey(child.props.tabKey)}
              >
                {child.props.tabTitle}
              </div>
            );
          }
          return null;
        });
      };
      
      return (
        <div className="tabs-container">
          <div className="tabs-header">
            {renderTabHeaders()}
          </div>
          <div className="tabs-content">
            {enhancedChildren}
          </div>
        </div>
      );
    }
    
    function TabPane({ children, isActive }) {
      if (!isActive) return null;
      return <div className="tab-pane">{children}</div>;
    }
    
    Tabs.TabPane = TabPane;
    
    // 使用复合组件
    <Tabs defaultActiveKey="1">
      <Tabs.TabPane tabKey="1" tabTitle="个人信息">
        <UserInfo />
      </Tabs.TabPane>
      <Tabs.TabPane tabKey="2" tabTitle="账户设置">
        <AccountSettings />
      </Tabs.TabPane>
    </Tabs>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
  2. 状态提升模式

    // 状态提升示例
    function FilterableProductTable({ products }) {
      const [filterText, setFilterText] = useState('');
      const [inStockOnly, setInStockOnly] = useState(false);
      
      return (
        <div>
          <SearchBar 
            filterText={filterText} 
            inStockOnly={inStockOnly} 
            onFilterTextChange={setFilterText} 
            onInStockChange={setInStockOnly} 
          />
          <ProductTable 
            products={products} 
            filterText={filterText} 
            inStockOnly={inStockOnly} 
          />
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  3. 容器分离模式

    // 逻辑容器
    function UserProfileContainer(props) {
      // 数据获取和状态管理逻辑
      return <UserProfilePresentation {...stateAndCallbacks} />;
    }
    
    // 展示组件
    function UserProfilePresentation({ user, isEditing, onEdit, onSave, ...props }) {
      // 纯渲染逻辑
      return (/* JSX */);
    }
    
    // 使用
    export default UserProfileContainer;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  4. Provider模式

    // 主题Provider
    const ThemeContext = React.createContext('light');
    
    function ThemeProvider({ theme = 'light', children }) {
      return (
        <ThemeContext.Provider value={theme}>
          {children}
        </ThemeContext.Provider>
      );
    }
    
    // 使用主题的组件
    function ThemedButton() {
      const theme = useContext(ThemeContext);
      return (
        <button className={`btn-${theme}`}>
          Themed Button
        </button>
      );
    }
    
    // 应用中使用
    <ThemeProvider theme="dark">
      <App />
    </ThemeProvider>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

# 组件API设计

  1. Props命名与结构

    // 清晰的props命名和解构
    function UserCard({ 
      // 必需props
      user, 
      // 可选props带默认值
      variant = 'standard',
      size = 'medium',
      // 布尔标志
      isActive = false,
      // 回调函数
      onUserClick,
      // 自定义渲染
      renderActions,
      // 其他props通过解构传递
      ...otherProps
    }) {
      return (
        <div 
          className={`user-card ${variant} ${size} ${isActive ? 'active' : ''}`}
          onClick={() => onUserClick && onUserClick(user.id)}
          {...otherProps}
        >
          <h3>{user.name}</h3>
          {renderActions && renderActions(user)}
        </div>
      );
    }
    
    // 使用PropTypes
    UserCard.propTypes = {
      user: PropTypes.shape({
        id: PropTypes.string.isRequired,
        name: PropTypes.string.isRequired,
        email: PropTypes.string
      }).isRequired,
      variant: PropTypes.oneOf(['standard', 'compact', 'detailed']),
      size: PropTypes.oneOf(['small', 'medium', 'large']),
      isActive: PropTypes.bool,
      onUserClick: PropTypes.func,
      renderActions: PropTypes.func
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
  2. 组件参考设计

    // 使用Ref转发
    import { forwardRef, useImperativeHandle, useRef } from 'react';
    
    const CustomInput = forwardRef(function CustomInput(props, ref) {
      const inputRef = useRef();
      
      // 暴露自定义API
      useImperativeHandle(ref, () => ({
        // 只暴露需要的方法
        focus: () => {
          inputRef.current.focus();
        },
        clear: () => {
          inputRef.current.value = '';
        },
        getValue: () => {
          return inputRef.current.value;
        }
      }));
      
      return <input ref={inputRef} {...props} />;
    });
    
    // 使用
    function Form() {
      const inputRef = useRef();
      
      const handleSubmit = () => {
        // 使用自定义API
        const value = inputRef.current.getValue();
        // 提交逻辑...
        inputRef.current.clear();
      };
      
      return (
        <form onSubmit={handleSubmit}>
          <CustomInput ref={inputRef} placeholder="输入内容" />
          <button type="submit">提交</button>
        </form>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

# API层设计

# 数据请求架构

  1. 集中式API客户端

    // API客户端实现
    import axios from 'axios';
    
    // 创建实例
    const apiClient = axios.create({
      baseURL: '/api/v1',
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json'
      }
    });
    
    // 请求拦截器
    apiClient.interceptors.request.use(
      config => {
        // 添加认证信息
        const token = localStorage.getItem('token');
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      error => Promise.reject(error)
    );
    
    // 响应拦截器
    apiClient.interceptors.response.use(
      response => response.data,
      error => {
        // 集中处理错误
        if (error.response?.status === 401) {
          // 认证失败处理
          window.location.href = '/login';
        }
        return Promise.reject(error);
      }
    );
    
    export default apiClient;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
  2. 服务层模式

    // 用户服务
    import apiClient from './apiClient';
    
    export const userService = {
      async getProfile() {
        return apiClient.get('/users/me');
      },
      
      async updateProfile(data) {
        return apiClient.put('/users/me', data);
      },
      
      async getUsers(params) {
        return apiClient.get('/users', { params });
      }
    };
    
    // 产品服务
    export const productService = {
      async getProduct(id) {
        return apiClient.get(`/products/${id}`);
      },
      
      async createProduct(data) {
        return apiClient.post('/products', data);
      },
      
      async updateProduct(id, data) {
        return apiClient.put(`/products/${id}`, data);
      },
      
      async deleteProduct(id) {
        return apiClient.delete(`/products/${id}`);
      }
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
  3. 数据获取Hooks

    // React Query数据获取Hook
    import { useQuery, useMutation, useQueryClient } from 'react-query';
    import { userService } from '../services/userService';
    
    export function useUsers(params) {
      return useQuery(['users', params], () => userService.getUsers(params), {
        staleTime: 5 * 60 * 1000, // 5分钟缓存
        keepPreviousData: true // 保留之前数据减少加载闪烁
      });
    }
    
    export function useUser(id) {
      return useQuery(['user', id], () => userService.getUser(id), {
        enabled: !!id // 只在id存在时执行查询
      });
    }
    
    export function useUpdateProfile() {
      const queryClient = useQueryClient();
      
      return useMutation(
        (data) => userService.updateProfile(data),
        {
          // 乐观更新
          onMutate: async (newData) => {
            // 取消相关查询以避免覆盖乐观更新
            await queryClient.cancelQueries('profile');
            
            // 保存之前状态
            const previousProfile = queryClient.getQueryData('profile');
            
            // 乐观更新缓存
            queryClient.setQueryData('profile', old => ({
              ...old,
              ...newData
            }));
            
            // 返回上下文对象
            return { previousProfile };
          },
          // 错误回滚
          onError: (err, newData, context) => {
            queryClient.setQueryData('profile', context.previousProfile);
          },
          // 成功后使相关查询无效并重新获取
          onSuccess: () => {
            queryClient.invalidateQueries('profile');
          }
        }
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51

# API错误处理策略

  1. 集中式错误处理

    // 全局错误处理器
    import { toast } from 'react-toastify';
    
    // 错误码映射
    const ERROR_MESSAGES = {
      400: '请求参数错误',
      401: '未授权,请重新登录',
      403: '无权访问该资源',
      404: '请求的资源不存在',
      500: '服务器内部错误',
      502: '网关错误',
      503: '服务不可用',
      504: '网关超时'
    };
    
    // 集中处理API错误
    export function handleApiError(error) {
      const status = error.response?.status;
      const message = error.response?.data?.message || 
                      ERROR_MESSAGES[status] ||
                      '请求失败,请稍后重试';
      
      // 显示错误消息
      toast.error(message);
      
      // 特殊状态处理
      if (status === 401) {
        // 清除认证状态
        localStorage.removeItem('token');
        // 重定向到登录
        window.location.href = '/login';
      }
      
      // 返回处理后的错误消息,方便UI展示
      return {
        status,
        message
      };
    }
    
    // 使用
    try {
      await apiClient.get('/some-endpoint');
    } catch (error) {
      const { message } = handleApiError(error);
      // 可以在UI中使用message
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
  2. 逐级错误处理

    // 服务层错误处理
    async function getUser(id) {
      try {
        const response = await apiClient.get(`/users/${id}`);
        return response.data;
      } catch (error) {
        // 特定服务层面的错误转换
        if (error.response?.status === 404) {
          throw new NotFoundError('用户不存在');
        }
        
        // 重新抛出其他错误
        throw error;
      }
    }
    
    // 组件层错误处理
    function UserProfile({ userId }) {
      const { data, error, isLoading } = useQuery(
        ['user', userId], 
        () => getUser(userId),
        {
          // 组件级错误处理
          onError: (error) => {
            if (error instanceof NotFoundError) {
              // 特定错误UI处理
              setShowNotFoundMessage(true);
            } else {
              // 通用错误处理
              handleApiError(error);
            }
          }
        }
      );
      
      if (isLoading) return <Spinner />;
      // 渲染逻辑...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38

# 前后端接口规范

  1. API路径与版本控制

    // API路径命名规范
    
    // 资源集合
    GET /api/v1/users         // 获取用户列表
    POST /api/v1/users        // 创建新用户
    
    // 特定资源
    GET /api/v1/users/:id     // 获取特定用户
    PUT /api/v1/users/:id     // 更新特定用户
    DELETE /api/v1/users/:id  // 删除特定用户
    
    // 嵌套资源
    GET /api/v1/users/:id/orders        // 获取用户的订单列表
    GET /api/v1/users/:id/orders/:orderId  // 获取用户的特定订单
    
    // 不遵循CRUD的操作
    POST /api/v1/auth/login   // 用户登录
    POST /api/v1/orders/:id/cancel  // 取消订单
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  2. API响应格式规范

    // 成功响应格式
    {
      "success": true,
      "data": {
        // 返回的实际数据
      },
      "meta": {
        // 元数据,如分页信息
        "total": 100,
        "page": 1,
        "limit": 10
      }
    }
    
    // 错误响应格式
    {
      "success": false,
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "输入数据验证失败",
        "details": [
          {
            "field": "email",
            "message": "无效的邮箱格式"
          }
        ]
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
  3. 模拟API与前后端并行开发

    // 使用MSW进行API模拟
    import { setupWorker, rest } from 'msw';
    
    // 定义处理程序
    const handlers = [
      // 获取用户列表
      rest.get('/api/users', (req, res, ctx) => {
        // 提取查询参数
        const page = req.url.searchParams.get('page') || '1';
        
        // 返回模拟数据
        return res(
          ctx.status(200),
          ctx.json({
            success: true,
            data: [
              { id: '1', name: '张三', email: 'zhangsan@example.com' },
              { id: '2', name: '李四', email: 'lisi@example.com' }
            ],
            meta: {
              total: 20,
              page: parseInt(page, 10),
              limit: 10
            }
          })
        );
      }),
      
      // 获取特定用户
      rest.get('/api/users/:id', (req, res, ctx) => {
        const { id } = req.params;
        
        // 模拟用户不存在
        if (id === '999') {
          return res(
            ctx.status(404),
            ctx.json({
              success: false,
              error: {
                code: 'NOT_FOUND',
                message: '用户不存在'
              }
            })
          );
        }
        
        // 返回模拟用户数据
        return res(
          ctx.status(200),
          ctx.json({
            success: true,
            data: {
              id,
              name: id === '1' ? '张三' : '李四',
              email: id === '1' ? 'zhangsan@example.com' : 'lisi@example.com'
            }
          })
        );
      })
    ];
    
    // 设置服务工作者
    const worker = setupWorker(...handlers);
    
    // 启动模拟服务
    export function startMockServer() {
      if (process.env.NODE_ENV === 'development') {
        worker.start();
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70

# 构建与部署架构

# 构建流程设计

  1. 构建阶段划分

    // package.json
    {
      "scripts": {
        // 开发构建
        "dev": "vite",
        
        // 类型检查
        "type-check": "tsc --noEmit",
        
        // 代码质量检查
        "lint": "eslint src --ext .js,.jsx,.ts,.tsx",
        
        // 单元测试
        "test": "jest",
        
        // 端到端测试
        "test:e2e": "cypress run",
        
        // 生产构建
        "build": "npm run type-check && npm run lint && npm run test && vite build",
        
        // 预览构建结果
        "preview": "vite preview",
        
        // 分析构建输出
        "analyze": "vite build --mode analyze"
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
  2. 环境配置管理

    // .env.development
    VITE_API_URL=http://localhost:3000/api
    VITE_ENABLE_MOCKS=true
    VITE_LOG_LEVEL=debug
    
    // .env.production
    VITE_API_URL=https://api.example.com
    VITE_ENABLE_MOCKS=false
    VITE_LOG_LEVEL=error
    
    // .env.staging
    VITE_API_URL=https://staging-api.example.com
    VITE_ENABLE_MOCKS=false
    VITE_LOG_LEVEL=info
    
    // 使用环境变量
    const apiUrl = import.meta.env.VITE_API_URL;
    const enableMocks = import.meta.env.VITE_ENABLE_MOCKS === 'true';
    
    if (enableMocks) {
      startMockServer();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  3. 模块打包策略

    // vite.config.js
    import { defineConfig } from 'vite';
    import react from '@vitejs/plugin-react';
    import { visualizer } from 'rollup-plugin-visualizer';
    
    export default defineConfig(({ mode }) => {
      const isAnalyze = mode === 'analyze';
      
      return {
        plugins: [
          react(),
          // 仅在分析模式下启用可视化工具
          isAnalyze && visualizer({
            open: true,
            filename: 'dist/stats.html',
            gzipSize: true
          })
        ],
        build: {
          // 代码拆分策略
          rollupOptions: {
            output: {
              manualChunks: {
                // 将React相关库打包到一起
                'vendor-react': ['react', 'react-dom', 'react-router-dom'],
                // 将其他依赖第三方库打包到一起
                'vendor-utils': ['lodash', 'axios', 'date-fns'],
                // UI组件库单独打包
                'vendor-ui': ['antd', '@ant-design/icons']
              }
            }
          },
          // 资源内联大小限制
          assetsInlineLimit: 10000,
          // 源码映射文件生成
          sourcemap: mode !== 'production'
        },
        // 开发服务器配置
        server: {
          port: 3000,
          proxy: {
            '/api': {
              target: 'http://localhost:8080',
              changeOrigin: true
            }
          }
        }
      };
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

# 持续集成与部署

  1. CI/CD流水线

    # .github/workflows/ci.yml
    name: CI/CD Pipeline
    
    on:
      push:
        branches: [main, develop]
      pull_request:
        branches: [main, develop]
    
    jobs:
      # 质量检查
      quality:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - name: Setup Node.js
            uses: actions/setup-node@v3
            with:
              node-version: '16'
              cache: 'npm'
          - name: Install dependencies
            run: npm ci
          - name: Check types
            run: npm run type-check
          - name: Lint
            run: npm run lint
          - name: Unit tests
            run: npm run test
    
      # 构建
      build:
        needs: quality
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - name: Setup Node.js
            uses: actions/setup-node@v3
            with:
              node-version: '16'
              cache: 'npm'
          - name: Install dependencies
            run: npm ci
          - name: Build
            run: npm run build
          - name: Upload build artifacts
            uses: actions/upload-artifact@v3
            with:
              name: build-files
              path: dist/
    
      # 部署到测试环境
      deploy-staging:
        # 仅从develop分支部署到测试环境
        if: github.ref == 'refs/heads/develop'
        needs: build
        runs-on: ubuntu-latest
        steps:
          - name: Download build artifacts
            uses: actions/download-artifact@v3
            with:
              name: build-files
              path: dist/
          - name: Deploy to Firebase Hosting (Staging)
            uses: FirebaseExtended/action-hosting-deploy@v0
            with:
              repoToken: '${{ secrets.GITHUB_TOKEN }}'
              firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
              projectId: my-app-staging
              channelId: live
    
      # 部署到生产环境
      deploy-production:
        # 仅从main分支部署到生产环境
        if: github.ref == 'refs/heads/main'
        needs: build
        runs-on: ubuntu-latest
        steps:
          - name: Download build artifacts
            uses: actions/download-artifact@v3
            with:
              name: build-files
              path: dist/
          - name: Deploy to Firebase Hosting (Production)
            uses: FirebaseExtended/action-hosting-deploy@v0
            with:
              repoToken: '${{ secrets.GITHUB_TOKEN }}'
              firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_PROD }}'
              projectId: my-app-production
              channelId: live
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
  2. 静态资源分发

    // CDN配置
    
    // 配置文件
    // vite.config.js
    export default defineConfig({
      base: process.env.NODE_ENV === 'production'
        ? 'https://cdn.example.com/assets/'
        : '/',
      build: {
        // ...其他构建配置
      }
    });
    
    // 缓存配置 (Firebase Hosting示例)
    // firebase.json
    {
      "hosting": {
        "public": "dist",
        "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
        "headers": [
          {
            "source": "**/*.@(js|css)",
            "headers": [
              {
                "key": "Cache-Control",
                "value": "public, max-age=31536000, immutable"
              }
            ]
          },
          {
            "source": "**/*.@(jpg|jpeg|gif|png|svg|webp)",
            "headers": [
              {
                "key": "Cache-Control",
                "value": "public, max-age=31536000, immutable"
              }
            ]
          },
          {
            "source": "**/*.@(html|json)",
            "headers": [
              {
                "key": "Cache-Control",
                "value": "public, max-age=0, must-revalidate"
              }
            ]
          }
        ],
        "rewrites": [
          {
            "source": "**",
            "destination": "/index.html"
          }
        ]
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
  3. 自动化版本与发布管理

    // package.json
    {
      "scripts": {
        "version:patch": "npm version patch && git push && git push --tags",
        "version:minor": "npm version minor && git push && git push --tags",
        "version:major": "npm version major && git push && git push --tags",
        "release": "standard-version"
      }
    }
    
    // standard-version配置
    // .versionrc
    {
      "types": [
        {"type": "feat", "section": "新特性"},
        {"type": "fix", "section": "Bug修复"},
        {"type": "chore", "hidden": true},
        {"type": "docs", "section": "文档"},
        {"type": "style", "hidden": true},
        {"type": "refactor", "section": "代码重构"},
        {"type": "perf", "section": "性能优化"},
        {"type": "test", "hidden": true}
      ],
      "commitUrlFormat": "https://github.com/your-username/your-repo/commit/{{hash}}",
      "compareUrlFormat": "https://github.com/your-username/your-repo/compare/{{previousTag}}...{{currentTag}}"
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

# 运行时环境配置

  1. 前端运行时配置

    // 运行时配置
    window.APP_CONFIG = {
      apiUrl: "https://api.example.com",
      features: {
        enableNewUI: true,
        enableAnalytics: true,
        betaFeatures: false
      },
      theme: "light"
    };
    
    // 配置服务
    export class ConfigService {
      // 默认配置
      private defaultConfig = {
        apiUrl: "https://default-api.example.com",
        features: {
          enableNewUI: false,
          enableAnalytics: false,
          betaFeatures: false
        },
        theme: "system"
      };
      
      private config;
      
      constructor() {
        this.config = {
          ...this.defaultConfig,
          ...window.APP_CONFIG
        };
      }
      
      get(path, defaultValue) {
        return get(this.config, path, defaultValue);
      }
      
      isFeatureEnabled(featureName) {
        return Boolean(this.get(`features.${featureName}`, false));
      }
    }
    
    // 使用
    const config = new ConfigService();
    const apiUrl = config.get('apiUrl');
    
    if (config.isFeatureEnabled('enableNewUI')) {
      // 启用新UI
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
  2. 特性开关(Feature Flags)

    // 特性开关服务
    class FeatureFlagService {
      private flags = {};
      private subscribers = new Set();
      
      constructor() {
        // 初始化默认标志
        this.flags = {
          newNavigation: false,
          darkMode: false,
          betaFeatures: false
        };
        
        // 从API加载标志
        this.loadFlags();
      }
      
      async loadFlags() {
        try {
          const response = await fetch('/api/feature-flags');
          const data = await response.json();
          this.flags = {...this.flags, ...data};
          this.notifySubscribers();
        } catch (error) {
          console.error('Failed to load feature flags', error);
        }
      }
      
      isEnabled(flagName) {
        return Boolean(this.flags[flagName]);
      }
      
      subscribe(callback) {
        this.subscribers.add(callback);
        return () => this.subscribers.delete(callback);
      }
      
      private notifySubscribers() {
        this.subscribers.forEach(callback => callback(this.flags));
      }
    }
    
    // React Hook
    function useFeatureFlag(flagName) {
      const [isEnabled, setIsEnabled] = useState(
        featureFlagService.isEnabled(flagName)
      );
      
      useEffect(() => {
        const unsubscribe = featureFlagService.subscribe(flags => {
          setIsEnabled(Boolean(flags[flagName]));
        });
        
        return unsubscribe;
      }, [flagName]);
      
      return isEnabled;
    }
    
    // 使用
    function App() {
      const showNewNavigation = useFeatureFlag('newNavigation');
      
      return (
        <div>
          {showNewNavigation ? <NewNavigation /> : <OldNavigation />}
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69

# 性能优化架构

# 性能优化架构设计

  1. 前端性能预算

    // webpack-bundle-analyzer配置
    module.exports = {
      plugins: [
        new BundleAnalyzerPlugin({
          analyzerMode: 'static',
          reportFilename: 'bundle-report.html',
          openAnalyzer: false,
          generateStatsFile: true,
          statsFilename: 'bundle-stats.json',
        })
      ],
      performance: {
        // 性能预算限制
        maxAssetSize: 200000, // 单个资源最大200KB
        maxEntrypointSize: 400000, // 入口点最大400KB
        hints: 'warning' // 超过限制显示警告
      }
    };
    
    // 性能预算CI检查脚本
    // scripts/check-perf-budget.js
    const fs = require('fs');
    const stats = JSON.parse(fs.readFileSync('./bundle-stats.json', 'utf8'));
    
    const BUDGET = {
      totalSize: 1000000, // 总体积1MB
      initialJS: 400000,  // 初始JS不超过400KB
      initialCSS: 100000, // 初始CSS不超过100KB
      maxChunks: 10       // 最大分块数
    };
    
    let failed = false;
    
    // 检查总大小
    const totalSize = stats.assets.reduce((size, asset) => size + asset.size, 0);
    if (totalSize > BUDGET.totalSize) {
      console.error(`❌ 总体积超出预算: ${(totalSize / 1000).toFixed(2)}KB > ${(BUDGET.totalSize / 1000).toFixed(2)}KB`);
      failed = true;
    }
    
    // 检查其他指标...
    
    process.exit(failed ? 1 : 0);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
  2. 渲染性能优化架构

    // 虚拟滚动列表实现
    import { useState, useRef, useEffect } from 'react';
    
    function VirtualList({ items, itemHeight, windowHeight }) {
      const [scrollTop, setScrollTop] = useState(0);
      const containerRef = useRef();
      
      const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight));
      const endIndex = Math.min(
        items.length - 1,
        Math.floor((scrollTop + windowHeight) / itemHeight)
      );
      
      const visibleItems = items.slice(startIndex, endIndex + 1);
      const containerHeight = items.length * itemHeight;
      const offsetY = startIndex * itemHeight;
      
      useEffect(() => {
        const container = containerRef.current;
        const handleScroll = () => {
          setScrollTop(container.scrollTop);
        };
        
        container.addEventListener('scroll', handleScroll);
        return () => container.removeEventListener('scroll', handleScroll);
      }, []);
      
      return (
        <div 
          ref={containerRef}
          style={{ height: windowHeight, overflow: 'auto' }}
        >
          <div style={{ height: containerHeight, position: 'relative' }}>
            {visibleItems.map((item, index) => (
              <div
                key={startIndex + index}
                style={{
                  position: 'absolute',
                  top: (startIndex + index) * itemHeight,
                  height: itemHeight
                }}
              >
                {item.content}
              </div>
            ))}
          </div>
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
  3. 关键渲染路径优化

    // 关键CSS内联
    // 构建插件配置
    module.exports = {
      plugins: [
        new HtmlWebpackPlugin({
          template: 'src/index.html',
          // 内联关键CSS
          inlineSource: '.(css)$',
          // 延迟加载非关键CSS
          styles: [
            { href: '/non-critical.css', rel: 'preload', as: 'style', onload: "this.onload=null;this.rel='stylesheet'" }
          ]
        })
      ]
    };
    
    // 路由级代码分割
    const Home = lazy(() => import('./pages/Home'));
    const About = lazy(() => import('./pages/About'));
    const ProductList = lazy(() => import('./pages/ProductList'));
    const ProductDetail = lazy(() => import('./pages/ProductDetail'));
    
    function App() {
      return (
        <BrowserRouter>
          <Suspense fallback={<Loading />}>
            <Routes>
              <Route path="/" element={<Home />} />
              <Route path="/about" element={<About />} />
              <Route path="/products" element={<ProductList />} />
              <Route path="/products/:id" element={<ProductDetail />} />
            </Routes>
          </Suspense>
        </BrowserRouter>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36

# 可扩展性设计

# 前端扩展点设计

  1. 插件系统

    // 插件系统实现
    class PluginSystem {
      private plugins = [];
      private hooks = {};
      
      // 注册插件
      register(plugin) {
        if (!plugin.name) {
          throw new Error('插件必须有name属性');
        }
        
        this.plugins.push(plugin);
        
        // 注册钩子
        if (plugin.hooks) {
          Object.entries(plugin.hooks).forEach(([hookName, hookFn]) => {
            if (!this.hooks[hookName]) {
              this.hooks[hookName] = [];
            }
            this.hooks[hookName].push(hookFn);
          });
        }
        
        // 初始化插件
        if (plugin.init && typeof plugin.init === 'function') {
          plugin.init(this.getAPI());
        }
      }
      
      // 执行钩子
      applyHook(hookName, ...args) {
        const hookFns = this.hooks[hookName] || [];
        return hookFns.reduce((result, hookFn) => {
          return hookFn(result, ...args);
        }, args[0]);
      }
      
      // 获取API
      getAPI() {
        return {
          applyHook: this.applyHook.bind(this)
        };
      }
    }
    
    // 插件示例
    const analyticsPlugin = {
      name: 'analytics',
      hooks: {
        // 路由变更钩子
        'router:change': (route, prevRoute) => {
          // 发送页面浏览事件
          sendPageView(route.path);
          return route;
        },
        // 渲染前钩子
        'render:before': (component, props) => {
          // 测量渲染时间
          performance.mark(`render-start-${component.name}`);
          return component;
        },
        // 渲染后钩子
        'render:after': (result, component) => {
          performance.mark(`render-end-${component.name}`);
          performance.measure(
            `render-${component.name}`,
            `render-start-${component.name}`,
            `render-end-${component.name}`
          );
          return result;
        }
      },
      init(api) {
        console.log('Analytics plugin initialized');
      }
    };
    
    // 注册插件
    const pluginSystem = new PluginSystem();
    pluginSystem.register(analyticsPlugin);
    
    // 使用插件系统
    function Router() {
      const navigate = (path) => {
        const prevRoute = { path: window.location.pathname };
        const newRoute = { path };
        
        // 应用路由变更钩子
        const finalRoute = pluginSystem.applyHook('router:change', newRoute, prevRoute);
        
        // 更新路由
        window.history.pushState(null, '', finalRoute.path);
      };
      
      return { navigate };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
  2. 主题系统

    // 主题系统
    class ThemeManager {
      private themes = {
        light: {
          colors: {
            primary: '#4361ee',
            secondary: '#3f37c9',
            background: '#ffffff',
            text: '#333333',
            border: '#e0e0e0'
          },
          typography: {
            fontSize: {
              small: '12px',
              medium: '16px',
              large: '20px'
            },
            fontFamily: '"Segoe UI", Roboto, sans-serif'
          },
          spacing: {
            small: '8px',
            medium: '16px',
            large: '24px'
          }
        },
        dark: {
          colors: {
            primary: '#4cc9f0',
            secondary: '#4895ef',
            background: '#1a1a2e',
            text: '#f1f1f1',
            border: '#4e4e50'
          },
          typography: {
            fontSize: {
              small: '12px',
              medium: '16px',
              large: '20px'
            },
            fontFamily: '"Segoe UI", Roboto, sans-serif'
          },
          spacing: {
            small: '8px',
            medium: '16px',
            large: '24px'
          }
        }
      };
      
      private activeTheme = 'light';
      private subscribers = new Set();
      
      constructor() {
        // 从本地存储恢复主题
        const savedTheme = localStorage.getItem('theme');
        if (savedTheme && this.themes[savedTheme]) {
          this.activeTheme = savedTheme;
        } else {
          // 检测系统主题
          const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
          this.activeTheme = prefersDark ? 'dark' : 'light';
        }
        
        // 应用主题
        this.applyTheme();
      }
      
      // 获取当前主题
      getTheme() {
        return this.themes[this.activeTheme];
      }
      
      // 设置主题
      setTheme(themeName) {
        if (this.themes[themeName]) {
          this.activeTheme = themeName;
          localStorage.setItem('theme', themeName);
          this.applyTheme();
          this.notifySubscribers();
        }
      }
      
      // 注册新主题
      registerTheme(name, themeConfig) {
        this.themes[name] = {
          ...this.themes.light, // 默认扩展light主题
          ...themeConfig
        };
      }
      
      // 覆盖主题变量
      override(themeName, overrides) {
        if (this.themes[themeName]) {
          this.themes[themeName] = deepMerge(this.themes[themeName], overrides);
          if (this.activeTheme === themeName) {
            this.applyTheme();
            this.notifySubscribers();
          }
        }
      }
      
      // 应用CSS变量
      private applyTheme() {
        const theme = this.getTheme();
        const root = document.documentElement;
        
        // 设置CSS变量
        Object.entries(theme.colors).forEach(([key, value]) => {
          root.style.setProperty(`--color-${key}`, value);
        });
        
        Object.entries(theme.typography.fontSize).forEach(([key, value]) => {
          root.style.setProperty(`--font-size-${key}`, value);
        });
        
        root.style.setProperty('--font-family', theme.typography.fontFamily);
        
        Object.entries(theme.spacing).forEach(([key, value]) => {
          root.style.setProperty(`--spacing-${key}`, value);
        });
        
        // 设置数据属性
        root.setAttribute('data-theme', this.activeTheme);
      }
      
      // 订阅主题变更
      subscribe(callback) {
        this.subscribers.add(callback);
        return () => this.subscribers.delete(callback);
      }
      
      // 通知订阅者
      private notifySubscribers() {
        const theme = this.getTheme();
        this.subscribers.forEach(callback => callback(theme));
      }
    }
    
    // 使用主题
    const themeManager = new ThemeManager();
    
    // React Hook
    function useTheme() {
      const [theme, setTheme] = useState(themeManager.getTheme());
      
      useEffect(() => {
        const unsubscribe = themeManager.subscribe(newTheme => {
          setTheme(newTheme);
        });
        
        return unsubscribe;
      }, []);
      
      return {
        theme,
        setTheme: themeManager.setTheme.bind(themeManager),
        isDark: themeManager.activeTheme === 'dark'
      };
    }
    
    // 使用示例
    function ThemeToggle() {
      const { isDark, setTheme } = useTheme();
      
      return (
        <button 
          onClick={() => setTheme(isDark ? 'light' : 'dark')}
        >
          切换到{isDark ? '浅色' : '深色'}主题
        </button>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
  3. 多语言支持

    // i18n实现
    class I18nManager {
      private translations = {};
      private currentLocale = 'zh-CN';
      private subscribers = new Set();
      
      constructor() {
        // 从本地存储或浏览器设置获取语言
        const savedLocale = localStorage.getItem('locale');
        if (savedLocale) {
          this.currentLocale = savedLocale;
        } else {
          this.currentLocale = navigator.language || 'zh-CN';
        }
      }
      
      // 加载语言包
      async loadTranslations(locale) {
        if (!this.translations[locale]) {
          try {
            const response = await fetch(`/locales/${locale}.json`);
            this.translations[locale] = await response.json();
          } catch (error) {
            console.error(`Failed to load translations for ${locale}`, error);
            // 回退到默认语言
            if (locale !== 'zh-CN') {
              return this.loadTranslations('zh-CN');
            }
          }
        }
        return this.translations[locale];
      }
      
      // 设置当前语言
      async setLocale(locale) {
        await this.loadTranslations(locale);
        this.currentLocale = locale;
        localStorage.setItem('locale', locale);
        document.documentElement.setAttribute('lang', locale);
        this.notifySubscribers();
      }
      
      // 获取翻译
      translate(key, params = {}) {
        const translations = this.translations[this.currentLocale] || {};
        let text = key.split('.').reduce((obj, part) => obj && obj[part], translations);
        
        if (!text) {
          console.warn(`Missing translation for key: ${key}`);
          return key;
        }
        
        // 替换参数
        return text.replace(/\{(\w+)\}/g, (match, p1) => params[p1] || match);
      }
    }
    
    // 使用
    function useTranslation() {
      const [t, setLocale] = useState(() => new I18nManager().translate);
      
      useEffect(() => {
        const unsubscribe = new I18nManager().subscribe(newTranslations => {
          setLocale(newTranslations);
        });
        
        return unsubscribe;
      }, []);
      
      return t;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71

# 项目结构组织

# 推荐的项目结构

  1. 按功能(Feature)组织 src/ ├── assets/ # 静态资源 │ ├── images/ │ ├── fonts/ │ └── styles/ ├── core/ # 核心模块 │ ├── api/ # API客户端 │ ├── router/ # 路由配置 │ ├── store/ # 全局状态 │ └── hooks/ # 通用Hooks ├── features/ # 功能模块 │ ├── auth/ # 认证功能 │ │ ├── components/ # 组件 │ │ ├── hooks/ # 功能相关Hooks │ │ ├── services/ # API服务 │ │ ├── store/ # 功能状态 │ │ ├── utils/ # 工具函数 │ │ ├── types.ts # 类型定义 │ │ └── index.ts # 公共API │ ├── products/ # 产品功能 │ │ ├── components/ │ │ └── ... │ └── checkout/ # 结账功能 │ ├── components/ │ └── ... ├── shared/ # 共享资源 │ ├── components/ # 通用组件 │ ├── hooks/ # 通用Hooks │ ├── utils/ # 工具函数 │ └── constants/ # 常量 ├── App.tsx # 应用根组件 └── index.tsx # 入口文件

  2. 单一特性目录结构

    features/auth/
    ├── components/            # 组件
    │   ├── LoginForm.tsx
    │   ├── RegisterForm.tsx
    │   ├── ForgotPassword.tsx
    │   └── index.ts           # 导出所有组件
    ├── hooks/                 # 功能专用Hooks
    │   ├── useAuth.ts
    │   ├── useLoginForm.ts
    │   └── index.ts
    ├── services/              # API服务
    │   ├── authService.ts
    │   └── index.ts
    ├── store/                 # 状态管理
    │   ├── authSlice.ts
    │   ├── selectors.ts
    │   └── index.ts
    ├── utils/                 # 工具函数
    │   ├── validation.ts
    │   └── index.ts
    ├── types.ts               # 类型定义
    ├── routes.ts              # 路由配置
    └── index.ts               # 公共API
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
  3. index.ts作为公共API

    // features/auth/index.ts
    // 只导出需要对外暴露的内容
    
    // 组件
    export { LoginForm, RegisterForm } from './components';
    
    // Hooks
    export { useAuth } from './hooks';
    
    // 服务
    export { login, logout, register } from './services/authService';
    
    // 类型
    export type { User, LoginCredentials } from './types';
    
    // 路由
    export { authRoutes } from './routes';
    
    // 不导出内部实现细节
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

# 文件命名约定

  1. 文件命名约定

    • 组件文件: 使用PascalCase (e.g., Button.tsx, UserProfile.tsx)
    • Hooks文件: 使用camelCase,前缀为"use" (e.g., useAuth.ts, useWindowSize.ts)
    • 服务和工具函数: 使用camelCase (e.g., authService.ts, dateUtils.ts)
    • 类型定义: 使用kebab-case或descriptive (e.g., types.ts, auth-types.ts)
    • 测试文件: 使用与被测文件相同的名称,加上.test或.spec后缀 (e.g., Button.test.tsx)
  2. 示例

    components/
    ├── Button.tsx
    ├── Button.test.tsx
    ├── Button.styles.ts      # 组件样式
    ├── UserCard/             # 复杂组件目录
    │   ├── UserCard.tsx      # 主组件
    │   ├── UserAvatar.tsx    # 子组件
    │   ├── UserInfo.tsx      # 子组件
    │   ├── index.ts          # 导出
    │   └── styles.ts         # 样式
    └── index.ts              # 导出所有组件
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 导入与别名策略

  1. 导入别名配置 // tsconfig.json { "compilerOptions": { "baseUrl": "src", "paths": { "@core/": ["core/"], "@features/": ["features/"], "@shared/": ["shared/"], "@assets/": ["assets/"], "@utils/": ["shared/utils/"], "@components/": ["shared/components/"] } } }

    // webpack.config.js const path = require('path');

    module.exports = { // ...其他配置 resolve: { alias: { '@core': path.resolve(__dirname, 'src/core'), '@features': path.resolve(__dirname, 'src/features'), '@shared': path.resolve(__dirname, 'src/shared'), '@assets': path.resolve(__dirname, 'src/assets'), '@utils': path.resolve(__dirname, 'src/shared/utils'), '@components': path.resolve(__dirname, 'src/shared/components') } } };

  2. 导入顺序约定

    // 推荐的导入顺序
    
    // 1. React和第三方库
    import React, { useState, useEffect } from 'react';
    import { useNavigate } from 'react-router-dom';
    import { useQuery } from 'react-query';
    
    // 2. 全局/核心导入
    import { apiClient } from '@core/api';
    
    // 3. 特性/模块导入
    import { useAuth } from '@features/auth';
    
    // 4. 共享组件和工具
    import { Button, Card } from '@components';
    import { formatDate } from '@utils/date';
    
    // 5. 样式导入
    import './MyComponent.css';
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  3. 内聚的模块导出

    // shared/components/index.ts - 集中导出
    export { Button } from './Button';
    export { Card } from './Card';
    export { Modal } from './Modal';
    
    // 使用时可以从单一来源导入
    import { Button, Card, Modal } from '@components';
    
    1
    2
    3
    4
    5
    6
    7

# 项目结构最佳实践

  1. 遵循有意义的目录结构
    • 目录结构应当反映业务领域和技术架构
    • 相关文件应该放在一起,遵循共同闭包原则
    • 避免过深的嵌套层次(通常不超过4层)
  2. 适度重构
    • 定期重新评估项目结构,防止结构老化
    • 模块和组件增长时考虑拆分
    • 频繁一起修改的文件应该放在同一目录下
  3. 封装边界明确
    • 使用index.ts文件控制模块的公共API
    • 不向外部暴露内部实现细节
    • 遵循"最小知识原则",组件只知道直接依赖
  4. 文档化
    • 为复杂的模块结构添加README.md
    • 使用JSDoc或类型系统记录API接口
    • 记录非常规的组织方式或特殊约定

# 微前端架构

# 微前端的核心概念

  1. 独立部署
    • 各个微前端可以独立构建、测试和部署
    • 减少大型应用的发布风险
    • 使不同团队能够按照自己的节奏开发
  2. 技术栈独立**
    • 不同微前端可以使用不同的前端框架和库
    • 逐步升级旧系统的技术栈
    • 允许团队根据需求选择最适合的技术
  3. 团队自治
    • 每个微前端由一个独立的团队负责
    • 清晰的责任边界
    • 减少跨团队沟通协调成本

# 微前端实现方案

  1. 基于iframe的集成

    <!-- 主应用 -->
    <div id="app-container">
      <header>通用头部</header>
      <nav>
        <button onclick="loadApp('app1')">应用1</button>
        <button onclick="loadApp('app2')">应用2</button>
      </nav>
      <main>
        <iframe id="micro-frontend-container" src=""></iframe>
      </main>
      <footer>通用底部</footer>
    </div>
    
    <script>
      function loadApp(appName) {
        const container = document.getElementById('micro-frontend-container');
        container.src = `/${appName}/index.html`;
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  2. Web Components

    // 微前端1 - 使用Web Components
    class MicroApp1 extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
      }
      
      connectedCallback() {
        this.shadowRoot.innerHTML = `
          <style>
            /* 组件级样式,不会泄露 */
            h2 { color: blue; }
          </style>
          <div>
            <h2>Micro App 1</h2>
            <p>This is a micro frontend using Web Components</p>
          </div>
        `;
        
        // 可以在这里初始化React、Vue等框架
      }
    }
    
    customElements.define('micro-app-1', MicroApp1);
    
    // 主应用中使用
    <micro-app-1></micro-app-1>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  3. 模块联邦 (Webpack Module Federation)

    // 微前端1的webpack配置
    const { ModuleFederationPlugin } = require('webpack').container;
    
    module.exports = {
      // ...其他配置
      plugins: [
        new ModuleFederationPlugin({
          name: 'microApp1',
          filename: 'remoteEntry.js',
          exposes: {
            './MicroApp1': './src/MicroApp1',
            './Button': './src/components/Button'
          },
          shared: ['react', 'react-dom']
        })
      ]
    };
    
    // 主应用的webpack配置
    const { ModuleFederationPlugin } = require('webpack').container;
    
    module.exports = {
      // ...其他配置
      plugins: [
        new ModuleFederationPlugin({
          name: 'host',
          remotes: {
            microApp1: 'microApp1@http://localhost:3001/remoteEntry.js',
            microApp2: 'microApp2@http://localhost:3002/remoteEntry.js'
          },
          shared: ['react', 'react-dom']
        })
      ]
    };
    
    // 主应用中使用微前端
    import React, { lazy, Suspense } from 'react';
    
    const MicroApp1 = lazy(() => import('microApp1/MicroApp1'));
    const Button = lazy(() => import('microApp1/Button'));
    
    function App() {
      return (
        <div>
          <h1>主应用</h1>
          <Suspense fallback={<div>Loading...</div>}>
            <MicroApp1 />
            <Button>共享组件</Button>
          </Suspense>
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
  4. 基于路由的集成

    // 主应用路由配置
    import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
    
    function MainApp() {
      return (
        <BrowserRouter>
          <div>
            <header>
              <nav>
                <Link to="/">首页</Link>
                <Link to="/app1">应用1</Link>
                <Link to="/app2">应用2</Link>
              </nav>
            </header>
            
            <Routes>
              <Route path="/" element={<Home />} />
              <Route path="/app1/*" element={<MicroFrontend name="app1" host="http://localhost:3001" />} />
              <Route path="/app2/*" element={<MicroFrontend name="app2" host="http://localhost:3002" />} />
            </Routes>
          </div>
        </BrowserRouter>
      );
    }
    
    // 微前端容器组件
    function MicroFrontend({ name, host }) {
      useEffect(() => {
        // 动态加载微前端脚本
        const scriptId = `micro-frontend-script-${name}`;
        if (document.getElementById(scriptId)) {
          renderMicroFrontend();
          return;
        }
        
        const script = document.createElement('script');
        script.id = scriptId;
        script.src = `${host}/static/js/main.js`;
        script.onload = renderMicroFrontend;
        document.head.appendChild(script);
        
        return () => {
          // 调用微前端的卸载方法
          window[`unmount${name}`] && window[`unmount${name}`]();
        };
      }, [name, host]);
      
      // 渲染微前端
      const renderMicroFrontend = () => {
        window[`mount${name}`] && window[`mount${name}`](`${name}-container`, { basePath: `/${name}` });
      };
      
      return <div id={`${name}-container`} />;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54

# 微前端的通信方案

  1. 基于CustomEvent的通信

    // 在微前端1中发送事件
    const payload = { userId: 123, action: 'view' };
    const event = new CustomEvent('microapp:event', { 
      detail: payload,
      bubbles: true 
    });
    window.dispatchEvent(event);
    
    // 在主应用或其他微前端中监听
    window.addEventListener('microapp:event', (event) => {
      const payload = event.detail;
      console.log('Received event from micro app:', payload);
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  2. 全局状态管理

    // 创建全局状态存储
    class MicroAppStore {
      constructor() {
        this.state = {};
        this.listeners = new Set();
      }
      
      getState() {
        return this.state;
      }
      
      setState(newState) {
        this.state = { ...this.state, ...newState };
        this.notify();
      }
      
      subscribe(listener) {
        this.listeners.add(listener);
        return () => this.listeners.delete(listener);
      }
      
      notify() {
        for (const listener of this.listeners) {
          listener(this.state);
        }
      }
    }
    
    // 在全局作用域中初始化
    window.microAppStore = window.microAppStore || new MicroAppStore();
    
    // 在微前端中使用
    const store = window.microAppStore;
    
    // 订阅更新
    const unsubscribe = store.subscribe(state => {
      console.log('Store updated:', state);
    });
    
    // 更新状态
    store.setState({ user: { id: 1, name: 'Alice' } });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
  3. 基于URL的状态共享

    // 使用URL查询参数共享状态
    import { useLocation, useNavigate } from 'react-router-dom';
    
    function MicroApp() {
      const location = useLocation();
      const navigate = useNavigate();
      
      // 从URL获取状态
      const query = new URLSearchParams(location.search);
      const filter = query.get('filter') || 'all';
      const page = parseInt(query.get('page') || '1', 10);
      
      // 更新URL状态
      const updateFilters = (newFilter) => {
        query.set('filter', newFilter);
        query.set('page', '1'); // 重置页码
        navigate({ search: query.toString() });
      };
      
      // 组件实现...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

# 微前端架构的挑战与解决方案

  1. 样式隔离

    // CSS Module方案
    // my-component.module.css
    .container {
      padding: 20px;
    }
    
    // 使用
    import styles from './my-component.module.css';
    
    function MyComponent() {
      return <div className={styles.container}>内容</div>;
    }
    
    // CSS-in-JS方案
    import styled from 'styled-components';
    
    const Container = styled.div`
      padding: 20px;
      color: ${props => props.theme.textColor};
    `;
    
    function MyComponent() {
      return <Container>内容</Container>;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  2. 依赖共享与重复资源

    // package.json中标记共享依赖
    {
      "name": "micro-app-1",
      "dependencies": {
        "react": "^18.2.0",
        "react-dom": "^18.2.0"
      },
      "peerDependencies": {
        "react": "^18.2.0",
        "react-dom": "^18.2.0"
      }
    }
    
    // webpack模块联邦配置共享库
    new ModuleFederationPlugin({
      name: 'microApp1',
      filename: 'remoteEntry.js',
      exposes: {
        './App': './src/App'
      },
      shared: {
        react: { 
          singleton: true, 
          requiredVersion: '^18.2.0' 
        },
        'react-dom': { 
          singleton: true, 
          requiredVersion: '^18.2.0' 
        }
      }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
  3. 认证与权限管理

    // 主应用中的认证服务
    class AuthService {
      constructor() {
        this.token = localStorage.getItem('token');
        this.user = JSON.parse(localStorage.getItem('user') || 'null');
      }
      
      isAuthenticated() {
        return !!this.token;
      }
      
      getUser() {
        return this.user;
      }
      
      getToken() {
        return this.token;
      }
      
      login(credentials) {
        // 登录逻辑...
        return fetch('/api/login', {
          method: 'POST',
          body: JSON.stringify(credentials)
        })
        .then(res => res.json())
        .then(data => {
          this.token = data.token;
          this.user = data.user;
          
          localStorage.setItem('token', this.token);
          localStorage.setItem('user', JSON.stringify(this.user));
          
          // 通知微前端
          window.dispatchEvent(new CustomEvent('auth:login', { 
            detail: { token: this.token, user: this.user } 
          }));
          
          return data;
        });
      }
      
      logout() {
        this.token = null;
        this.user = null;
        
        localStorage.removeItem('token');
        localStorage.removeItem('user');
        
        // 通知微前端
        window.dispatchEvent(new CustomEvent('auth:logout'));
      }
    }
    
    // 暴露给微前端
    window.authService = new AuthService();
    
    // 微前端中使用
    function useAuth() {
      const [state, setState] = useState({
        isAuthenticated: window.authService.isAuthenticated(),
        user: window.authService.getUser()
      });
      
      useEffect(() => {
        const handleLogin = (event) => {
          setState({
            isAuthenticated: true,
            user: event.detail.user
          });
        };
        
        const handleLogout = () => {
          setState({
            isAuthenticated: false,
            user: null
          });
        };
        
        window.addEventListener('auth:login', handleLogin);
        window.addEventListener('auth:logout', handleLogout);
        
        return () => {
          window.removeEventListener('auth:login', handleLogin);
          window.removeEventListener('auth:logout', handleLogout);
        };
      }, []);
      
      return {
        isAuthenticated: state.isAuthenticated,
        user: state.user,
        login: window.authService.login.bind(window.authService),
        logout: window.authService.logout.bind(window.authService)
      };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95

# 实际案例分析

# 案例1: 大型电商平台架构

架构描述:

  • 基于React和Redux的单页应用
  • 按业务领域划分的模块化结构
  • 微前端架构用于集成第三方应用和旧系统 关键架构决策:
  1. 采用Feature-first的目录结构

    src/
    ├── features/
    │   ├── product-catalog/
    │   ├── shopping-cart/
    │   ├── checkout/
    │   ├── user-account/
    │   └── order-management/
    ├── core/
    │   ├── api/
    │   ├── auth/
    │   └── store/
    └── shared/
        ├── components/
        └── utils/
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  2. 状态管理分层

    • 全局状态: 用户认证、全站配置、购物车
    • 特性状态: 各功能模块自己管理的状态
    • 本地状态: 组件内的UI状态
  3. 性能优化策略

    • 路由级代码分割
    • 商品列表虚拟滚动
    • 静态资源CDN分发
    • 服务端渲染关键页面
  4. 跨领域关注点处理

    • 统一的错误边界处理
    • 全局的数据加载状态
    • 一致的表单验证策略

# 案例2: SaaS管理平台架构

架构描述:

  • 基于Vue和Pinia的企业级应用
  • 支持多租户的插件化架构
  • 动态主题和国际化支持 关键架构决策:
  1. 采用插件化架构
    // 插件定义
    export default {
      name: 'analytics',
      routes: [/* 路由配置 */],
      store: {/* 状态定义 */},
      install(app, options) {
        // 注册组件、指令等
        app.component('AnalyticsDashboard', AnalyticsDashboard);
        
        // 扩展功能
        app.config.globalProperties.$analytics = analyticsService;
      }
    }
    
    // 主应用加载插件
    import Analytics from './plugins/analytics';
    
    const app = createApp(App);
    app.use(Analytics, { apiKey: 'xxx' });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  2. 动态权限控制
    • 基于RBAC模型的权限系统
    • 动态路由生成
    • 组件级权限控制
  3. 动态主题实现
    • CSS变量方案
    • 预设主题切换
    • 自定义主题配置界面
  4. 可扩展的表单系统
    • 基于JSON Schema的表单生成器
    • 自定义字段和验证规则
    • 表单状态历史追踪

# 案例3: 渐进式重构旧系统

架构描述:

  • 将jQuery老系统逐步迁移到现代框架
  • 基于微前端的混合架构
  • 增量式采用现代开发实践 关键架构决策:
  1. 路由级别微前端集成

    • 主应用负责认证和导航
    • 旧页面保持原样
    • 新功能使用React开发
  2. 共享认证状态

    // 旧系统中暴露认证状态
    window.appAuth = {
      getToken: () => localStorage.getItem('token'),
      getUserInfo: () => JSON.parse(localStorage.getItem('userInfo') || 'null'),
      isAuthenticated: () => !!localStorage.getItem('token')
    };
    
    // 新应用中使用
    function useAuth() {
      const [isAuthenticated, setIsAuthenticated] = useState(
        window.appAuth.isAuthenticated()
      );
      
      useEffect(() => {
        const checkAuth = () => {
          setIsAuthenticated(window.appAuth.isAuthenticated());
        };
        
        window.addEventListener('storage', checkAuth);
        window.addEventListener('auth:changed', checkAuth);
        
        return () => {
          window.removeEventListener('storage', checkAuth);
          window.removeEventListener('auth:changed', checkAuth);
        };
      }, []);
      
      return {
        isAuthenticated,
        token: window.appAuth.getToken(),
        userInfo: window.appAuth.getUserInfo()
      };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
  3. 渐进式API更新

    • 包装旧API为Promise风格
    • 新API遵循RESTful设计
    • 使用适配器模式处理差异
  4. 混合式构建流程

    • 旧系统保持Gulp构建
    • 新功能使用Webpack
    • 共享静态资源CDN

# 总结和最佳实践

# 架构设计原则回顾

1.关注点分离

  • 将应用分解为独立、聚焦的模块
  • 分离数据、UI和业务逻辑 2.单一职责
  • 每个组件、模块只做一件事,做好这一件事
  • 降低耦合,提高复用性 3.可测试性
  • 架构设计考虑测试需求
  • 便于单元测试和集成测试 4.渐进式增强
  • 确保基本功能在最简环境可用
  • 根据环境能力增强体验

# 大型前端项目成功的关键因素

  1. 明确的架构文档
    • 记录架构决策理由
    • 定义清晰的开发规范
    • 保持文档更新
  2. 一致的编码标准
    • 使用ESLint、Prettier等工具强制代码风格
    • 通过代码审查确保质量
    • 建立共享的组件库和设计系统
  3. 有效的状态管理
    • 选择合适的状态管理策略
    • 避免状态分散和重复
    • 确保状态变更可追踪
  4. 性能优先考虑
    • 从设计阶段考虑性能问题
    • 定期性能审计和优化
    • 建立性能测试基准
  5. 适应性强的项目结构
    • 选择适合团队和项目的组织结构
    • 允许随项目发展调整
    • 保持一致性但避免过度僵化

# 未来趋势与展望

  1. Web组件与微前端

    • 标准化组件封装和复用
    • 更细粒度的应用拆分
    • 跨框架组件共享
  2. 服务器组件与流式渲染

    • 混合客户端和服务器渲染模式
    • 增量式静态生成(ISR)
    • 流式UI更新
  3. 低代码与可视化开发

    • 组件驱动的可视化构建
    • JSON配置生成UI
    • 业务逻辑与UI分离
  4. AI辅助开发

    • 代码自动补全和生成
    • 智能代码重构
    • 自动文档生成

# 最终建议

  1. 保持简单

    • 避免过度工程化
    • 只有问题明确时才增加抽象层
    • 从简单解决方案开始,按需演进
  2. 以用户为中心

    • 技术选择基于用户体验需求
    • 性能优化关注用户感知
    • 可访问性作为基本要求
  3. 持续改进

    • 定期重构和技术债务清理
    • 保持对新技术的了解
    • 从错误中学习并调整架构
  4. 团队协作

    • 架构决策需要团队共识
    • 投资于知识共享和培训
    • 建立明确的技术决策流程

通过遵循这些原则和实践,团队可以构建出高质量、可维护且灵活的前端架构,从而支持业务长期发展并提供卓越的用户体验。

Last Updated: 2025-03-11T08:38:33.000Z