大型前端项目架构设计
随着前端应用复杂度的不断提升,如何设计一个可扩展、可维护的大型前端项目架构成为了关键挑战。本文将深入探讨前端架构设计的核心原则和实践方法。
架构设计原则
1. 单一职责原则 (SRP)
每个模块、组件或函数都应该有且仅有一个改变的理由。
javascript
// ❌ 违反单一职责原则
class UserManager {
constructor() {
this.users = [];
}
addUser(user) {
this.users.push(user);
}
saveToDatabase(user) {
// 数据库操作
}
sendEmail(user) {
// 邮件发送
}
validateUser(user) {
// 用户验证
}
}
// ✅ 遵循单一职责原则
class UserRepository {
saveUser(user) {
// 数据库操作
}
findUser(id) {
// 查询操作
}
}
class EmailService {
sendWelcomeEmail(user) {
// 邮件发送
}
}
class UserValidator {
validate(user) {
// 用户验证
}
}
class UserService {
constructor(repository, emailService, validator) {
this.repository = repository;
this.emailService = emailService;
this.validator = validator;
}
async createUser(userData) {
const user = this.validator.validate(userData);
await this.repository.saveUser(user);
await this.emailService.sendWelcomeEmail(user);
return user;
}
}
2. 开闭原则 (OCP)
软件实体应该对扩展开放,对修改关闭。
javascript
// 策略模式实现开闭原则
class PaymentProcessor {
constructor(strategy) {
this.strategy = strategy;
}
process(amount) {
return this.strategy.pay(amount);
}
}
class CreditCardPayment {
pay(amount) {
console.log(`Processing $${amount} via Credit Card`);
}
}
class PayPalPayment {
pay(amount) {
console.log(`Processing $${amount} via PayPal`);
}
}
class AlipayPayment {
pay(amount) {
console.log(`Processing $${amount} via Alipay`);
}
}
// 使用
const processor = new PaymentProcessor(new CreditCardPayment());
processor.process(100);
3. 依赖倒置原则 (DIP)
高层模块不应该依赖低层模块,两者都应该依赖抽象。
javascript
// 依赖注入容器
class DIContainer {
constructor() {
this.services = new Map();
}
register(name, factory) {
this.services.set(name, factory);
}
resolve(name) {
const factory = this.services.get(name);
if (!factory) {
throw new Error(`Service ${name} not found`);
}
return factory();
}
}
// 注册服务
const container = new DIContainer();
container.register('apiClient', () => new ApiClient());
container.register('userRepository', () =>
new UserRepository(container.resolve('apiClient'))
);
container.register('userService', () =>
new UserService(container.resolve('userRepository'))
);
项目结构设计
1. 分层架构
src/
├── presentation/ # 表现层
│ ├── components/ # 通用组件
│ ├── pages/ # 页面组件
│ ├── layouts/ # 布局组件
│ └── hooks/ # 自定义 Hooks
├── application/ # 应用层
│ ├── services/ # 业务服务
│ ├── stores/ # 状态管理
│ └── use-cases/ # 用例
├── domain/ # 领域层
│ ├── entities/ # 实体
│ ├── repositories/ # 仓储接口
│ └── value-objects/ # 值对象
├── infrastructure/ # 基础设施层
│ ├── api/ # API 客户端
│ ├── storage/ # 存储实现
│ └── external/ # 外部服务
└── shared/ # 共享模块
├── utils/ # 工具函数
├── constants/ # 常量
└── types/ # 类型定义
2. 功能模块化
src/
├── modules/
│ ├── auth/
│ │ ├── components/
│ │ ├── services/
│ │ ├── stores/
│ │ ├── types/
│ │ └── index.ts
│ ├── user/
│ │ ├── components/
│ │ ├── services/
│ │ ├── stores/
│ │ ├── types/
│ │ └── index.ts
│ └── product/
│ ├── components/
│ ├── services/
│ ├── stores/
│ ├── types/
│ └── index.ts
└── core/
├── api/
├── router/
├── store/
└── utils/
组件架构设计
1. 原子设计方法论
components/
├── atoms/ # 原子组件
│ ├── Button/
│ ├── Input/
│ ├── Label/
│ └── Icon/
├── molecules/ # 分子组件
│ ├── SearchBox/
│ ├── FormField/
│ └── Card/
├── organisms/ # 有机体组件
│ ├── Header/
│ ├── ProductList/
│ └── ContactForm/
├── templates/ # 模板
│ ├── PageLayout/
│ └── ArticleLayout/
└── pages/ # 页面
├── HomePage/
├── ProductPage/
└── ContactPage/
2. 组件设计模式
容器组件与展示组件分离:
jsx
// 展示组件 - 只负责 UI 渲染
const UserList = ({ users, onUserClick, loading }) => {
if (loading) {
return <LoadingSpinner />;
}
return (
<div className="user-list">
{users.map(user => (
<UserCard
key={user.id}
user={user}
onClick={() => onUserClick(user.id)}
/>
))}
</div>
);
};
// 容器组件 - 负责数据获取和状态管理
const UserListContainer = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
const handleUserClick = (userId) => {
navigate(`/users/${userId}`);
};
return (
<UserList
users={users}
loading={loading}
onUserClick={handleUserClick}
/>
);
};
高阶组件 (HOC) 模式:
jsx
// 权限控制 HOC
const withAuth = (WrappedComponent, requiredRole) => {
return (props) => {
const { user, loading } = useAuth();
if (loading) {
return <LoadingSpinner />;
}
if (!user || !user.roles.includes(requiredRole)) {
return <AccessDenied />;
}
return <WrappedComponent {...props} />;
};
};
// 使用
const AdminPanel = withAuth(AdminPanelComponent, 'admin');
Render Props 模式:
jsx
const DataFetcher = ({ url, children }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(error => {
setError(error);
setLoading(false);
});
}, [url]);
return children({ data, loading, error });
};
// 使用
const UserProfile = ({ userId }) => (
<DataFetcher url={`/api/users/${userId}`}>
{({ data: user, loading, error }) => {
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return <UserCard user={user} />;
}}
</DataFetcher>
);
状态管理架构
1. 状态分层管理
javascript
// 全局状态 - 使用 Redux/Zustand
const useGlobalStore = create((set, get) => ({
user: null,
theme: 'light',
language: 'zh-CN',
setUser: (user) => set({ user }),
setTheme: (theme) => set({ theme }),
setLanguage: (language) => set({ language }),
}));
// 页面级状态 - 使用 Context
const PageContext = createContext();
const PageProvider = ({ children }) => {
const [pageData, setPageData] = useState(null);
const [filters, setFilters] = useState({});
return (
<PageContext.Provider value={{
pageData,
filters,
setPageData,
setFilters
}}>
{children}
</PageContext.Provider>
);
};
// 组件级状态 - 使用 useState/useReducer
const ComponentWithLocalState = () => {
const [localState, setLocalState] = useState(initialState);
return (
<div>
{/* 组件内容 */}
</div>
);
};
2. 状态管理模式
Flux 架构模式:
javascript
// Action Types
const ActionTypes = {
FETCH_USERS_REQUEST: 'FETCH_USERS_REQUEST',
FETCH_USERS_SUCCESS: 'FETCH_USERS_SUCCESS',
FETCH_USERS_FAILURE: 'FETCH_USERS_FAILURE',
};
// Action Creators
const fetchUsersRequest = () => ({
type: ActionTypes.FETCH_USERS_REQUEST
});
const fetchUsersSuccess = (users) => ({
type: ActionTypes.FETCH_USERS_SUCCESS,
payload: users
});
const fetchUsersFailure = (error) => ({
type: ActionTypes.FETCH_USERS_FAILURE,
payload: error
});
// Async Action Creator (Thunk)
const fetchUsers = () => async (dispatch) => {
dispatch(fetchUsersRequest());
try {
const users = await userService.getUsers();
dispatch(fetchUsersSuccess(users));
} catch (error) {
dispatch(fetchUsersFailure(error.message));
}
};
// Reducer
const usersReducer = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.FETCH_USERS_REQUEST:
return {
...state,
loading: true,
error: null
};
case ActionTypes.FETCH_USERS_SUCCESS:
return {
...state,
loading: false,
users: action.payload
};
case ActionTypes.FETCH_USERS_FAILURE:
return {
...state,
loading: false,
error: action.payload
};
default:
return state;
}
};
API 层架构
1. API 客户端设计
javascript
class ApiClient {
constructor(baseURL, options = {}) {
this.baseURL = baseURL;
this.options = options;
this.interceptors = {
request: [],
response: []
};
}
// 请求拦截器
addRequestInterceptor(interceptor) {
this.interceptors.request.push(interceptor);
}
// 响应拦截器
addResponseInterceptor(interceptor) {
this.interceptors.response.push(interceptor);
}
async request(config) {
// 应用请求拦截器
let requestConfig = { ...config };
for (const interceptor of this.interceptors.request) {
requestConfig = await interceptor(requestConfig);
}
try {
let response = await fetch(`${this.baseURL}${requestConfig.url}`, {
method: requestConfig.method || 'GET',
headers: {
'Content-Type': 'application/json',
...this.options.headers,
...requestConfig.headers
},
body: requestConfig.data ? JSON.stringify(requestConfig.data) : undefined
});
// 应用响应拦截器
for (const interceptor of this.interceptors.response) {
response = await interceptor(response);
}
return response;
} catch (error) {
throw new ApiError(error.message, error.status);
}
}
get(url, config = {}) {
return this.request({ ...config, method: 'GET', url });
}
post(url, data, config = {}) {
return this.request({ ...config, method: 'POST', url, data });
}
put(url, data, config = {}) {
return this.request({ ...config, method: 'PUT', url, data });
}
delete(url, config = {}) {
return this.request({ ...config, method: 'DELETE', url });
}
}
// API 错误类
class ApiError extends Error {
constructor(message, status, data) {
super(message);
this.name = 'ApiError';
this.status = status;
this.data = data;
}
}
2. 资源层抽象
javascript
// 基础资源类
class BaseResource {
constructor(apiClient, endpoint) {
this.apiClient = apiClient;
this.endpoint = endpoint;
}
async findAll(params = {}) {
const response = await this.apiClient.get(this.endpoint, { params });
return response.data;
}
async findById(id) {
const response = await this.apiClient.get(`${this.endpoint}/${id}`);
return response.data;
}
async create(data) {
const response = await this.apiClient.post(this.endpoint, data);
return response.data;
}
async update(id, data) {
const response = await this.apiClient.put(`${this.endpoint}/${id}`, data);
return response.data;
}
async delete(id) {
await this.apiClient.delete(`${this.endpoint}/${id}`);
}
}
// 具体资源类
class UserResource extends BaseResource {
constructor(apiClient) {
super(apiClient, '/users');
}
async findByEmail(email) {
const response = await this.apiClient.get(`${this.endpoint}/by-email`, {
params: { email }
});
return response.data;
}
async updateProfile(id, profileData) {
const response = await this.apiClient.put(
`${this.endpoint}/${id}/profile`,
profileData
);
return response.data;
}
}
// API 服务工厂
class ApiService {
constructor(baseURL) {
this.apiClient = new ApiClient(baseURL);
// 添加认证拦截器
this.apiClient.addRequestInterceptor(async (config) => {
const token = localStorage.getItem('authToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 添加错误处理拦截器
this.apiClient.addResponseInterceptor(async (response) => {
if (!response.ok) {
const error = await response.json();
throw new ApiError(error.message, response.status, error);
}
return response.json();
});
// 初始化资源
this.users = new UserResource(this.apiClient);
this.products = new BaseResource(this.apiClient, '/products');
this.orders = new BaseResource(this.apiClient, '/orders');
}
}
路由架构
1. 路由配置
javascript
// 路由配置
const routes = [
{
path: '/',
component: HomeLayout,
children: [
{
path: '',
component: HomePage,
meta: { title: '首页' }
},
{
path: 'about',
component: AboutPage,
meta: { title: '关于我们' }
}
]
},
{
path: '/auth',
component: AuthLayout,
children: [
{
path: 'login',
component: LoginPage,
meta: { title: '登录', requiresGuest: true }
},
{
path: 'register',
component: RegisterPage,
meta: { title: '注册', requiresGuest: true }
}
]
},
{
path: '/dashboard',
component: DashboardLayout,
meta: { requiresAuth: true },
children: [
{
path: '',
component: DashboardHome,
meta: { title: '控制台' }
},
{
path: 'users',
component: UserManagement,
meta: { title: '用户管理', requiredRole: 'admin' }
}
]
}
];
// 路由守卫
const router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach(async (to, from, next) => {
// 设置页面标题
if (to.meta.title) {
document.title = `${to.meta.title} - 应用名称`;
}
// 认证检查
if (to.meta.requiresAuth) {
const isAuthenticated = await checkAuth();
if (!isAuthenticated) {
return next('/auth/login');
}
}
// 访客页面检查
if (to.meta.requiresGuest) {
const isAuthenticated = await checkAuth();
if (isAuthenticated) {
return next('/dashboard');
}
}
// 角色权限检查
if (to.meta.requiredRole) {
const user = await getCurrentUser();
if (!user || !user.roles.includes(to.meta.requiredRole)) {
return next('/403');
}
}
next();
});
2. 代码分割与懒加载
javascript
// 路由级代码分割
const routes = [
{
path: '/',
component: () => import('../pages/HomePage.vue')
},
{
path: '/dashboard',
component: () => import('../pages/DashboardPage.vue'),
children: [
{
path: 'users',
component: () => import('../pages/UserManagement.vue')
},
{
path: 'products',
component: () => import('../pages/ProductManagement.vue')
}
]
}
];
// 预加载策略
const preloadRoute = (routeName) => {
const route = routes.find(r => r.name === routeName);
if (route && typeof route.component === 'function') {
route.component();
}
};
// 在用户交互时预加载
const handleMouseEnter = () => {
preloadRoute('dashboard');
};
性能优化架构
1. 缓存策略
javascript
// 内存缓存
class MemoryCache {
constructor(maxSize = 100, ttl = 5 * 60 * 1000) {
this.cache = new Map();
this.maxSize = maxSize;
this.ttl = ttl;
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, {
value,
timestamp: Date.now()
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() - item.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return item.value;
}
clear() {
this.cache.clear();
}
}
// HTTP 缓存
class HttpCache {
constructor() {
this.cache = new MemoryCache();
}
generateKey(url, params) {
return `${url}?${new URLSearchParams(params).toString()}`;
}
async get(url, params = {}) {
const key = this.generateKey(url, params);
const cached = this.cache.get(key);
if (cached) {
return cached;
}
const response = await fetch(url + '?' + new URLSearchParams(params));
const data = await response.json();
this.cache.set(key, data);
return data;
}
}
2. 虚拟化列表
jsx
// 虚拟化列表组件
const VirtualList = ({
items,
itemHeight,
containerHeight,
renderItem
}) => {
const [scrollTop, setScrollTop] = useState(0);
const visibleStart = Math.floor(scrollTop / itemHeight);
const visibleEnd = Math.min(
visibleStart + Math.ceil(containerHeight / itemHeight) + 1,
items.length
);
const visibleItems = items.slice(visibleStart, visibleEnd);
const handleScroll = (e) => {
setScrollTop(e.target.scrollTop);
};
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={handleScroll}
>
<div style={{ height: items.length * itemHeight, position: 'relative' }}>
{visibleItems.map((item, index) => (
<div
key={visibleStart + index}
style={{
position: 'absolute',
top: (visibleStart + index) * itemHeight,
height: itemHeight,
width: '100%'
}}
>
{renderItem(item, visibleStart + index)}
</div>
))}
</div>
</div>
);
};
错误处理架构
1. 全局错误处理
javascript
// 错误边界组件
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// 记录错误到监控服务
errorReportingService.captureException(error, {
extra: errorInfo,
tags: {
component: 'ErrorBoundary'
}
});
}
render() {
if (this.state.hasError) {
return this.props.fallback || <ErrorFallback error={this.state.error} />;
}
return this.props.children;
}
}
// 错误报告服务
class ErrorReportingService {
constructor(config) {
this.config = config;
this.queue = [];
this.isOnline = navigator.onLine;
window.addEventListener('online', () => {
this.isOnline = true;
this.flushQueue();
});
window.addEventListener('offline', () => {
this.isOnline = false;
});
}
captureException(error, context = {}) {
const errorReport = {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
url: window.location.href,
userAgent: navigator.userAgent,
...context
};
if (this.isOnline) {
this.sendReport(errorReport);
} else {
this.queue.push(errorReport);
}
}
async sendReport(report) {
try {
await fetch(this.config.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiKey}`
},
body: JSON.stringify(report)
});
} catch (error) {
console.error('Failed to send error report:', error);
}
}
flushQueue() {
while (this.queue.length > 0) {
const report = this.queue.shift();
this.sendReport(report);
}
}
}
2. 异步错误处理
javascript
// 异步操作包装器
const withErrorHandling = (asyncFn) => {
return async (...args) => {
try {
return await asyncFn(...args);
} catch (error) {
errorReportingService.captureException(error, {
function: asyncFn.name,
arguments: args
});
throw error;
}
};
};
// 使用示例
const fetchUserData = withErrorHandling(async (userId) => {
const response = await apiService.users.findById(userId);
return response;
});
// Promise 错误处理
const handleAsyncError = (promise) => {
return promise.catch(error => {
errorReportingService.captureException(error);
return { error: error.message };
});
};
测试架构
1. 测试金字塔
javascript
// 单元测试
describe('UserService', () => {
let userService;
let mockRepository;
beforeEach(() => {
mockRepository = {
findById: jest.fn(),
save: jest.fn()
};
userService = new UserService(mockRepository);
});
test('should create user successfully', async () => {
const userData = { name: 'John', email: 'john@example.com' };
const expectedUser = { id: 1, ...userData };
mockRepository.save.mockResolvedValue(expectedUser);
const result = await userService.createUser(userData);
expect(mockRepository.save).toHaveBeenCalledWith(userData);
expect(result).toEqual(expectedUser);
});
});
// 集成测试
describe('User API Integration', () => {
test('should create and retrieve user', async () => {
const userData = { name: 'John', email: 'john@example.com' };
// 创建用户
const createResponse = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
const userId = createResponse.body.id;
// 获取用户
const getResponse = await request(app)
.get(`/api/users/${userId}`)
.expect(200);
expect(getResponse.body).toMatchObject(userData);
});
});
// E2E 测试
describe('User Registration Flow', () => {
test('should complete user registration', async () => {
await page.goto('/register');
await page.fill('[data-testid="name-input"]', 'John Doe');
await page.fill('[data-testid="email-input"]', 'john@example.com');
await page.fill('[data-testid="password-input"]', 'password123');
await page.click('[data-testid="register-button"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('[data-testid="welcome-message"]'))
.toContainText('Welcome, John Doe');
});
});
部署架构
1. 构建优化
javascript
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return {
entry: {
main: './src/index.js',
vendor: ['react', 'react-dom', 'lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: isProduction
? '[name].[contenthash].js'
: '[name].js',
chunkFilename: isProduction
? '[name].[contenthash].chunk.js'
: '[name].chunk.js',
clean: true
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true
}
}
}
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
minify: isProduction
}),
new MiniCssExtractPlugin({
filename: isProduction
? '[name].[contenthash].css'
: '[name].css'
}),
...(process.env.ANALYZE ? [new BundleAnalyzerPlugin()] : [])
]
};
};
2. 环境配置
javascript
// 环境配置管理
class ConfigManager {
constructor() {
this.env = process.env.NODE_ENV || 'development';
this.config = this.loadConfig();
}
loadConfig() {
const baseConfig = {
app: {
name: 'MyApp',
version: process.env.npm_package_version
}
};
const envConfig = {
development: {
api: {
baseURL: 'http://localhost:3001/api',
timeout: 10000
},
features: {
debugMode: true,
mockData: true
}
},
staging: {
api: {
baseURL: 'https://staging-api.example.com/api',
timeout: 15000
},
features: {
debugMode: false,
mockData: false
}
},
production: {
api: {
baseURL: 'https://api.example.com/api',
timeout: 15000
},
features: {
debugMode: false,
mockData: false
}
}
};
return {
...baseConfig,
...envConfig[this.env]
};
}
get(path) {
return path.split('.').reduce((obj, key) => obj?.[key], this.config);
}
}
export const config = new ConfigManager();
监控与日志
1. 性能监控
javascript
// 性能监控服务
class PerformanceMonitor {
constructor() {
this.metrics = new Map();
this.observers = [];
this.initObservers();
}
initObservers() {
// 页面加载性能
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
this.recordMetric(entry.name, entry.duration);
}
});
observer.observe({ entryTypes: ['navigation', 'paint', 'largest-contentful-paint'] });
this.observers.push(observer);
}
// 资源加载性能
const resourceObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 1000) { // 超过1秒的资源
this.recordSlowResource(entry);
}
}
});
resourceObserver.observe({ entryTypes: ['resource'] });
this.observers.push(resourceObserver);
}
recordMetric(name, value, tags = {}) {
const metric = {
name,
value,
timestamp: Date.now(),
tags
};
this.metrics.set(`${name}_${Date.now()}`, metric);
this.sendMetric(metric);
}
recordSlowResource(entry) {
this.recordMetric('slow_resource', entry.duration, {
url: entry.name,
type: entry.initiatorType
});
}
async sendMetric(metric) {
try {
await fetch('/api/metrics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(metric)
});
} catch (error) {
console.warn('Failed to send metric:', error);
}
}
// 自定义性能标记
mark(name) {
performance.mark(name);
}
measure(name, startMark, endMark) {
performance.measure(name, startMark, endMark);
const measure = performance.getEntriesByName(name, 'measure')[0];
this.recordMetric(name, measure.duration);
}
}
export const performanceMonitor = new PerformanceMonitor();
2. 日志系统
javascript
// 日志服务
class Logger {
constructor(config = {}) {
this.level = config.level || 'info';
this.transports = config.transports || [new ConsoleTransport()];
this.context = config.context || {};
}
log(level, message, meta = {}) {
if (!this.shouldLog(level)) return;
const logEntry = {
level,
message,
timestamp: new Date().toISOString(),
context: { ...this.context, ...meta }
};
this.transports.forEach(transport => {
transport.log(logEntry);
});
}
debug(message, meta) { this.log('debug', message, meta); }
info(message, meta) { this.log('info', message, meta); }
warn(message, meta) { this.log('warn', message, meta); }
error(message, meta) { this.log('error', message, meta); }
shouldLog(level) {
const levels = ['debug', 'info', 'warn', 'error'];
return levels.indexOf(level) >= levels.indexOf(this.level);
}
child(context) {
return new Logger({
level: this.level,
transports: this.transports,
context: { ...this.context, ...context }
});
}
}
// 控制台传输器
class ConsoleTransport {
log(entry) {
const { level, message, timestamp, context } = entry;
const contextStr = Object.keys(context).length > 0
? JSON.stringify(context)
: '';
console[level](`[${timestamp}] ${level.toUpperCase()}: ${message}`, contextStr);
}
}
// 远程传输器
class RemoteTransport {
constructor(endpoint) {
this.endpoint = endpoint;
this.buffer = [];
this.flushInterval = setInterval(() => this.flush(), 5000);
}
log(entry) {
this.buffer.push(entry);
if (this.buffer.length >= 10) {
this.flush();
}
}
async flush() {
if (this.buffer.length === 0) return;
const logs = [...this.buffer];
this.buffer = [];
try {
await fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ logs })
});
} catch (error) {
console.error('Failed to send logs:', error);
// 重新加入缓冲区
this.buffer.unshift(...logs);
}
}
}
export const logger = new Logger({
level: process.env.NODE_ENV === 'production' ? 'warn' : 'debug',
transports: [
new ConsoleTransport(),
...(process.env.NODE_ENV === 'production'
? [new RemoteTransport('/api/logs')]
: [])
]
});
安全架构
1. 输入验证与清理
javascript
// 输入验证器
class InputValidator {
static email(value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value);
}
static password(value) {
// 至少8位,包含大小写字母、数字和特殊字符
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
return passwordRegex.test(value);
}
static sanitizeHtml(html) {
const div = document.createElement('div');
div.textContent = html;
return div.innerHTML;
}
static validateSchema(data, schema) {
const errors = [];
for (const [field, rules] of Object.entries(schema)) {
const value = data[field];
if (rules.required && (value === undefined || value === null || value === '')) {
errors.push(`${field} is required`);
continue;
}
if (value !== undefined && rules.type && typeof value !== rules.type) {
errors.push(`${field} must be of type ${rules.type}`);
}
if (rules.minLength && value.length < rules.minLength) {
errors.push(`${field} must be at least ${rules.minLength} characters`);
}
if (rules.maxLength && value.length > rules.maxLength) {
errors.push(`${field} must be no more than ${rules.maxLength} characters`);
}
if (rules.pattern && !rules.pattern.test(value)) {
errors.push(`${field} format is invalid`);
}
if (rules.custom && !rules.custom(value)) {
errors.push(`${field} validation failed`);
}
}
return {
isValid: errors.length === 0,
errors
};
}
}
// 使用示例
const userSchema = {
email: {
required: true,
type: 'string',
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
},
password: {
required: true,
type: 'string',
minLength: 8,
custom: InputValidator.password
},
age: {
type: 'number',
min: 18,
max: 120
}
};
const validation = InputValidator.validateSchema(userData, userSchema);
2. 权限控制
javascript
// 权限管理系统
class PermissionManager {
constructor() {
this.permissions = new Map();
this.roles = new Map();
}
definePermission(name, description) {
this.permissions.set(name, { name, description });
}
defineRole(name, permissions) {
this.roles.set(name, { name, permissions });
}
hasPermission(userRoles, requiredPermission) {
return userRoles.some(role => {
const roleData = this.roles.get(role);
return roleData && roleData.permissions.includes(requiredPermission);
});
}
checkAccess(user, resource, action) {
const permission = `${resource}:${action}`;
return this.hasPermission(user.roles, permission);
}
}
// 权限装饰器
const requirePermission = (permission) => {
return (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
const user = this.getCurrentUser();
if (!permissionManager.hasPermission(user.roles, permission)) {
throw new Error('Access denied: insufficient permissions');
}
return originalMethod.apply(this, args);
};
return descriptor;
};
};
// 使用示例
class UserService {
@requirePermission('user:read')
getUsers() {
return this.userRepository.findAll();
}
@requirePermission('user:write')
createUser(userData) {
return this.userRepository.create(userData);
}
@requirePermission('user:delete')
deleteUser(userId) {
return this.userRepository.delete(userId);
}
}
国际化架构
javascript
// 国际化管理器
class I18nManager {
constructor(defaultLocale = 'zh-CN') {
this.currentLocale = defaultLocale;
this.messages = new Map();
this.fallbackLocale = 'en-US';
}
async loadMessages(locale) {
if (this.messages.has(locale)) {
return this.messages.get(locale);
}
try {
const messages = await import(`../locales/${locale}.json`);
this.messages.set(locale, messages.default);
return messages.default;
} catch (error) {
console.warn(`Failed to load messages for locale: ${locale}`);
return {};
}
}
async setLocale(locale) {
await this.loadMessages(locale);
this.currentLocale = locale;
// 更新 HTML lang 属性
document.documentElement.lang = locale;
// 触发语言变更事件
window.dispatchEvent(new CustomEvent('localechange', {
detail: { locale }
}));
}
t(key, params = {}) {
const messages = this.messages.get(this.currentLocale) || {};
const fallbackMessages = this.messages.get(this.fallbackLocale) || {};
let message = this.getNestedValue(messages, key) ||
this.getNestedValue(fallbackMessages, key) ||
key;
// 参数替换
Object.entries(params).forEach(([param, value]) => {
message = message.replace(new RegExp(`{{${param}}}`, 'g'), value);
});
return message;
}
getNestedValue(obj, path) {
return path.split('.').reduce((current, key) => current?.[key], obj);
}
// 数字格式化
formatNumber(number, options = {}) {
return new Intl.NumberFormat(this.currentLocale, options).format(number);
}
// 日期格式化
formatDate(date, options = {}) {
return new Intl.DateTimeFormat(this.currentLocale, options).format(date);
}
// 货币格式化
formatCurrency(amount, currency = 'CNY') {
return new Intl.NumberFormat(this.currentLocale, {
style: 'currency',
currency
}).format(amount);
}
}
export const i18n = new I18nManager();
总结
大型前端项目架构设计需要考虑多个维度:
核心原则
- 模块化设计:清晰的职责分离和依赖管理
- 可扩展性:支持功能和团队规模的增长
- 可维护性:代码结构清晰,易于理解和修改
- 性能优化:从架构层面考虑性能问题
- 安全性:内置安全机制和最佳实践
关键技术
- 分层架构:表现层、应用层、领域层、基础设施层
- 组件化:原子设计方法论和组件复用
- 状态管理:合理的状态分层和管理策略
- 路由设计:代码分割和权限控制
- API 设计:统一的接口抽象和错误处理
工程化实践
- 构建优化:代码分割、缓存策略、性能监控
- 测试策略:单元测试、集成测试、E2E 测试
- 部署流程:环境配置、CI/CD 集成
- 监控体系:性能监控、错误追踪、日志管理
通过合理的架构设计,可以构建出高质量、可维护的大型前端应用。