一个成功的项目需要一个合理的架构设计,这个架构设计需要考虑到项目的需求、技术栈、团队规模、团队技术水平、项目规模、项目周期、项目预算等多方面的因素。在这篇文章中,我将分享一些关于前端架构设计的思考,希望对您有所帮助。
首先,让我们来看一下一个典型的现代Web应用的架构设计。本文梳理思路时,采用的技术栈是React + Vite + TypeScript,后端假设是使用restful接口的方式,会配置好跨域,前端部署采用Docker容器化,部署到Azure云平台。

架构设计宗旨
前端架构:使用现代化的前端技术栈,如 React 或者 Vue,配合 Vite 或者 Webpack 进行构建,使用 TypeScript 进行开发,使用状态管理库(如 Redux、MobX、Zustand)进行状态管理,使用路由库(如 React Router、Vue Router)进行路由管理,使用UI库(如 Ant Design、Element UI)进行UI设计,网络请求方面使用swr对axios进行封装,使用Jest、Playwright等工具进行测试,使用Lighthouse等工具进行性能测试和优化,使用Storybook进行组件驱动开发。
后端架构:这块我们可能简单的带过一下,假设后端使用restful接口的方式,提供数据服务,采用OAuth 2.0和JWT进行身份验证和授权,使用Swagger生成API文档。
部署架构:使用Docker容器化应用,部署到云平台,使用DevOps进行持续集成/持续部署。
具体的实施的原则
性能优化:应用在各种网络条件下都能提供良好的性能,可以使用Lighthouse等工具进行性能测试和优化。
安全性:应用有足够的安全措施,包括身份验证、数据加密、输入验证等。
可扩展性:应用可以轻松扩展,包括水平和垂直扩展,以应对未来的增长。
可维护性:代码易于维护,包括良好的文档、清晰的代码结构、合理的命名等。
测试覆盖率:应用有足够的测试覆盖率,包括单元测试、集成测试和端到端测试。
监控和日志:应用有足够的监控和日志记录,以便及时发现和解决问题。
文档:应用有足够的文档,包括用户文档、API文档、架构文档等。
团队协作:团队有良好的协作机制,包括代码审查、持续集成、持续部署等。
用户体验:应用提供良好的用户体验,包括响应式设计、无障碍访问、国际化等。
成本优化:应用在云平台上的成本是可控的,包括资源利用率、自动伸缩等。
灾难恢复:应用有灾难恢复计划,包括备份策略、故障转移等。
用户反馈:应用有良好的用户反馈机制,包括用户调查、错误报告等。
一个可能的技术栈
下面是一个可能的技术栈,基于上述架构设计的考虑的实践,
-
react + vite + typescript: 基本的架子
-
zustand: 状态管理
-
react-router: 路由管理
-
arco-design: UI库
-
docker: 容器化部署
-
cos: 静态资源存储
-
jest + playwright: 测试工具
-
lighthouse: 性能工具
-
prometheus + grafana + ELK Stack: 监控和日志
-
storybook: 组件驱动开发
-
oauth2.0/jwt: 身份验证
-
azure devops/github actions: 持续集成/持续部署
-
swagger: 文档工具
-
hotjar: 用户反馈
-
owasp zap: 静态代码扫描
-
eslint + prettier: 代码规范和风格指南
下面是一些关于这些技术的简要说明:
React + Vite + TypeScript:这是一个强大的组合,React 提供了灵活的组件模型,Vite 作为下一代前端工具提供了极快的冷启动和即时模块热更新,TypeScript 提供了静态类型检查,可以提高代码质量和可维护性。
状态管理(Zustand):Zustand 是一个轻量级的状态管理库,它提供了简单直观的API,易于上手,且不需要像 Redux 那样的繁琐配置,适合中小型项目,而且配合 persist 可以实现状态持久化。以下是一个简单的示例:
javascript
import create from 'zustand';
//persist
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'counter-store', // unique name
}
)
);
const Counter = () => {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
const decrement = useStore((state) => state.decrement);
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default Counter;
路由(React Router):React Router 是一个流行的路由库,它提供了强大的路由功能,适合构建单页面应用,最新的版本还支持了路由懒加载和代码分割,可以提高应用的性能,而且还支持loaders,可以实现路由级别的数据预加载,提高用户体验。一下是一个loader的示例:
javascript
const HomeLoader = () => import('home/HomeLoader');
const AboutLoader = () => import('about/AboutLoader');
const routes = [
{
path: '/',
component: HomeLoader,
},
{
path: '/about',
component: AboutLoader,
},
];
const App = () => (
<Router routes={routes} />
);
export default App;
UI库(Arco Design):Arco Design 是一个相对较新的组件库,它提供了一套企业级产品的UI解决方案,相对于 Ant Design 和 Element UI,它更加现代化,更加灵活,更加易于定制,适合构建现代化的Web应用。
Docker部署:使用Docker可以确保环境一致性,简化部署流程,是当前的行业标准。 Azure提供了一套完整的容器服务,可以帮助您轻松部署和管理容器化应用。当然国内可以使用阿里云或者 腾讯云的容器服务。具体的做法是将前端打包后的静态资源放到Nginx容器中,然后部署到云平台。下面是一个简单的Dockerfile示例:
dockerfile
# 使用官方的nginx镜像作为基础镜像
FROM nginx:alpine
# 将打包后的静态资源复制到Nginx的默认目录
COPY build /usr/share/nginx/html
# 暴露80端口
EXPOSE 80
CDN:使用CDN可以加速静态资源的加载,提高应用的性能。Azure提供了一套完整的CDN服务,可以帮助您轻松部署和管理CDN。前端打包后的静态资源可以上传到CDN,加速用户访问。当然还需要哦提供一个发布脚本,将静态资源上传到CDN。下面是一个可能的发布打包后的静态资源到腾讯云cos的 node.js脚本示例:
javascript
const COS = require('cos-nodejs-sdk-v5');
const fs = require('fs');
// 初始化COS客户端
const cos = new COS({
SecretId: 'yourSecretId
SecretKey: 'yourSecretKey', // 注意信息安全,不要将密钥暴露在代码中
Region: 'ap-guangzhou',
Bucket: 'yourBucket',
// 可选参数
FileParallelLimit: 3,
ChunkParallelLimit : 3,
ChunkSize: 1024 * 1024,
ProgressInterval: 1000,
Proxy: '',
Protocol: 'https',
});
// 上传静态资源到COS
cos.putObject({
Bucket: 'yourBucket',
Region: 'ap-guangzhou',
Key: 'yourKey',
Body : fs.createReadStream('yourStaticResource'),
}, function (err, data) { console.log(err || data); });
测试工具(Jest + Playwright):Jest 是一个广泛使用的JavaScript测试框架,适合单元和集成测试。Playwright 是一个强大的端到端测试工具,支持多浏览器测试。这里有一些最佳实践,什么时候使用单元测试,什么时候使用端到端测试,如何编写测试用例,如何集成测试工具到CI/CD流程:
javascript
// 单元测试
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
// 端到端测试
test('should display the correct title', async () => {
await page.goto('https://example.com');
const title = await page.title();
expect(title).toBe('Example Domain');
});
yaml
# GitHub Actions配置文件
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
可以看到,我们在这里使用了Jest进行单元测试,Playwright进行端到端测试,同时使用GitHub Actions进行持续集成。单元测试适合测试单个函数或模块,端到端测试适合测试整个应用的功能,持续集成可以确保每次提交都会自动运行测试,确保代码质量。
性能工具Lighthouse:Lighthouse 是一个开源的自动化工具,用于改进网络应用的质量,非常适合进行性能测试。可以集成到CI/CD流程中,确保每次提交都会自动运行性能测试,以便及时发现和解决性能问题。方式是在CI/CD流程中添加一个Lighthouse测试的步骤,如下所示:
yaml
# GitHub Actions配置文件
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Run Lighthouse test
run: npx lighthouse https://example.com --output json --output-path=./lighthouse-report.json
日志监控(Prometheus + Grafana + ELK Stack):这是一套强大的监控和日志管理组合,可以帮助您及时发现和解决问题。以下是一个简单的配置示例:
yaml
# Prometheus配置文件
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['localhost:9100']
# Grafana配置文件
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://localhost:9090
isDefault: true
# ELK Stack配置文件
input {
file {
path => "/var/log/nginx/access.log"
start_position => "beginning"
}
}
filter {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
}
}
组件驱动开发(Storybook):Storybook 是一个出色的工具,用于构建和测试UI组件,它可以提高开发效率并确保组件质量。关键是可以解决组件的复用性和可维护性问题。引入storybook后,可以在本地开发环境中独立开发和测试组件,然后将组件集成到应用中。以下是一个简单的配置示例:
step1. 安装storybook
bash
npx sb init
step2. 编写组件,假设我们有一个Button组件
javascript
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Button from './Button';
storiesOf('Button', module)
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
.add('with emoji', () => (
<Button onClick={action('clicked')}>
<span role="img" aria-label="so cool">
😀 😎 👍 💯
</span>
</Button>
));
身份验证(OAuth2.0/JWT):OAuth 2.0 和 JWT 是当前Web应用中常用的身份验证和授权机制,它们可以提供安全的用户认证流程。 OAuth 2.0 是一个开放标准,它允许用户授权第三方应用访问其资源,而无需提供用户名和密码。他的原理是通过授权码的方式,获取access token,然后使用access token访问资源。以下是一个简单的 GitHub OAuth 2.0示例:
javascript
const express = require('express');
const axios = require('axios');
const qs = require('querystring');
const app = express();
const CLIENT_ID = 'your-github-client-id';
const CLIENT_SECRET = 'your-github-client-secret';
app.get('/login/github', (req, res) => {
res.redirect(`https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}`);
});
app.get('/github/callback', async (req, res) => {
const { code } = req.query;
const response = await axios({
method: 'post',
url: `https://github.com/login/oauth/access_token?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&code=${code}`,
headers: {
accept: 'application/json'
}
});
const accessToken = response.data.access_token;
console.log(`Access Token: ${accessToken}`);
res.redirect(`/welcome`);
});
JWT 是一种轻量级的身份验证和授权机制,它使用JSON Web Token(JWT)来传递用户信息,可以提供安全的用户认证和授权流程。下面是一个简单的JWT示例:
javascript
// 假设服务端的实现是这个样子,但可能你的后端使用go来实现
const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const SECRET_KEY = 'your-secret-key';
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 这里应该添加验证用户名和密码的逻辑
// 为了简单起见,我们假设任何用户都可以登录
const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
});
app.get('/protected', (req, res) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) return res.sendStatus(403);
res.send(`Hello ${user.username}, you accessed protected route!`);
});
});
app.listen(3000, () => console.log('Server started on port 3000'));
// 假设客户端的实现是这个样子
async function login(e) {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const response = await fetch('http://localhost:3000/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
const data = await response.json();
token = data.token;
// 保存token到本地存储
localStorage.setItem('token', token);
}
async function accessProtected() {
const token = localStorage.getItem('token');
const response = await fetch('http://localhost:3000/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.text();
alert(data);
}
持续集成/持续部署(Azure DevOps/GitHub Actions):Azure DevOps 和 GitHub Actions 都是流行的CI/CD工具,可以帮助您自动化测试和部署流程, 以下是一个简单的配置示例:
yaml
# Azure DevOps配置文件
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '14.x'
displayName: 'Install Node.js'
- script: |
npm install
npm test
displayName: 'Install and test'
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(System.DefaultWorkingDirectory)/dist'
artifact: 'dist'
publishLocation: 'pipeline'
# GitHub Actions配置文件
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
文档工具(Swagger):Swagger 是一个流行的API文档工具,它可以帮助您自动生成API文档,并提供交互式API测试功能。以下是一个简单的配置示例:
用户反馈(Hotjar):Hotjar 是一个流行的用户反馈工具,它可以帮助您了解用户行为和需求,以便改进产品,他的使用方式是在页面中添加一个脚本,然后就可以收集用户行为数据了。
html
<!-- Hotjar Tracking Code for https://your-website.com -->
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:1234567,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
国内可以使用神策分析或者GrowingIO等工具,示例代码如下:
javascript
// 神策分析
sensorsdata.track('button_click', {
button_id: 'your-button-id',
button_text: 'your-button-text'
});
// GrowingIO
gio('track', 'button_click', {
button_id: 'your-button-id',
button_text: 'your-button-text'
});
安全工具(OWASP ZAP):OWASP ZAP 是一个流行的安全测试工具,它可以帮助您发现和解决安全问题,如跨站脚本(XSS)、SQL注入、CSRF等。静态扫描工具可以在CI/CD流程中集成,以便及时发现和解决安全问题。以下是一个简单的配置示例:
yaml
# GitHub Actions配置文件
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Run OWASP ZAP test
run: docker run -t owasp/zap2docker-stable zap-baseline.py -t https://example.com -l Low
一些额外的考虑点
代码规范和风格指南:确保团队遵循统一的编码标准,可以使用ESLint和Prettier等工具自动化代码格式化和质量检查。必要的时候可以使用Husky和Lint-staged等工具在提交代码前进行代码检查。同时也可以自己编写一些eslint的规则,以确保代码质量。
备份和灾难恢复:确保应用有足够的备份策略,以便在发生灾难时能够及时恢复。可以使用Azure的备份服务,或者自己编写一个备份脚本。不过有docker的话,可以使用docker的备份和恢复功能。至于更加强的方式,比如两地三中心,这里就不展开了。
安全性:除了身份验证,还要考虑其他安全措施,如输入验证、CSRF保护、CORS策略等。即便在静态代码扫描环节有些漏掉的,也可以在部署环节使用WAF等工具进行安全防护。
可访问性(Accessibility):应用遵循WCAG指南,使其对所有用户都是可访问的,比如说盲人、色盲、聋哑等。但是这个一般是在产品上线后,再进行优化。
国际化(i18n):如果您的应用需要支持多语言,那么从一开始就考虑国际化是很重要的,因为在后期添加国际化会比较麻烦,需要修改大量的代码。不止是前端,后端也需要考虑国际化。如error message,返回的数据等。
响应式设计(Responsive Design):确保应用在各种设备上都能提供良好的用户体验,可以使用CSS媒体查询和弹性布局等技术实现响应式设计。这部分通常来讲可能不太容易处理,因为涉及到设计和前端的协作。而且移动端的体验和PC端的体验天然是不一样的,举一个例子,PC端的表格是可以横向滚动的,但是移动端的表格是不可以横向滚动的。pc端用户可以做适当的编辑工作,移动端因为屏幕小,所以尽量不要让用户做过重的编辑工作。
下面是一个响应式设计的示例:
css
/* pc上可以是网格布局 */
.container {
display: grid;
grid-template-columns: auto auto auto;
grid-gap: 10px;
padding: 10px;
}
/* 响应式设计,移动端变为flex */
@media (max-width: 600px) {
.container {
display: flex;
flex-direction: column;
}
}
关于前端架构设计的思考就到这里,希朝对您有所帮助。当然,这只是一些思考,实际的项目中可能会有更多的考虑点,比如说项目的特殊需求、团队的特殊情况等。希望您在实际项目中能够根据实际情况进行合理的架构设计,确保项目的成功。
欢迎关注我的微信公众号 :老码沉思录
,和我一起交流!