工程化与框架系列(8)--持续集成实践
持续集成实践 🔄
持续集成(Continuous Integration,简称CI)是现代前端开发流程中的重要环节,它通过自动化构建、测试和部署,帮助团队更快速、更可靠地交付高质量代码。本文将详细介绍前端持续集成的实践方法和最佳实践。
持续集成概述 🌟
💡 小知识:持续集成的核心理念是团队成员频繁地将代码集成到主干分支,通过自动化测试和构建来验证代码的正确性,从而尽早发现并解决问题。
为什么需要持续集成
在现代前端开发中,持续集成带来以下好处:
-
提早发现问题
- 及时发现代码冲突
- 快速定位构建错误
- 自动化测试验证
-
提高代码质量
- 统一的代码规范检查
- 自动化测试覆盖
- 性能指标监控
-
加速开发流程
- 自动化构建部署
- 减少人工操作
- 快速迭代反馈
-
降低发布风险
- 环境一致性保证
- 部署流程标准化
- 回滚机制保障
CI/CD 工具链 🛠️
主流CI工具对比
| 特性 | Jenkins | GitHub Actions | GitLab CI | Circle CI |
|---|---|---|---|---|
| 部署方式 | 自托管 | 云服务 | 自托管/云服务 | 云服务 |
| 配置难度 | 较复杂 | 简单 | 中等 | 简单 |
| 扩展性 | 极强 | 良好 | 良好 | 良好 |
| 生态系统 | 丰富 | 快速成长 | 完整 | 完整 |
| 价格 | 开源免费 | 免费额度 | 免费/付费 | 免费/付费 |
GitHub Actions 配置示例
name: Frontend CIon:push:branches: [ main ]pull_request:branches: [ main ]jobs:build:runs-on: ubuntu-lateststrategy:matrix:node-version: [14.x, 16.x, 18.x]steps:- uses: actions/checkout@v2- name: Use Node.js ${{ matrix.node-version }}uses: actions/setup-node@v2with:node-version: ${{ matrix.node-version }}- name: Install dependenciesrun: npm ci- name: Run lintingrun: npm run lint- name: Run testsrun: npm test- name: Build projectrun: npm run build- name: Upload build artifactsuses: actions/upload-artifact@v2with:name: build-filespath: build/deploy:needs: buildruns-on: ubuntu-latestif: github.ref == 'refs/heads/main'steps:- name: Download build artifactsuses: actions/download-artifact@v2with:name: build-filespath: build/- name: Deploy to productionrun: |# 部署脚本echo "Deploying to production..."
GitLab CI 配置示例
image: node:16stages:- install- test- build- deploycache:paths:- node_modules/install:stage: installscript:- npm ciartifacts:paths:- node_modules/test:stage: testscript:- npm run lint- npm run test:coveragecoverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'build:stage: buildscript:- npm run buildartifacts:paths:- dist/deploy:stage: deployscript:- npm run deployonly:- main
自动化测试集成 🧪
测试策略
// Jest 测试配置
// jest.config.js
module.exports = {preset: 'ts-jest',testEnvironment: 'jsdom',setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}','!src/**/*.d.ts','!src/index.tsx',],coverageThreshold: {global: {branches: 80,functions: 80,lines: 80,statements: 80,},},
};// 组件测试示例
// Button.test.tsx
import { render, fireEvent } from '@testing-library/react';
import Button from './Button';describe('Button Component', () => {it('renders correctly', () => {const { getByText } = render(<Button>Click me</Button>);expect(getByText('Click me')).toBeInTheDocument();});it('handles click events', () => {const handleClick = jest.fn();const { getByText } = render(<Button onClick={handleClick}>Click me</Button>);fireEvent.click(getByText('Click me'));expect(handleClick).toHaveBeenCalledTimes(1);});
});
端到端测试集成
// Cypress 测试配置
// cypress.config.ts
import { defineConfig } from 'cypress';export default defineConfig({e2e: {baseUrl: 'http://localhost:3000',supportFile: 'cypress/support/e2e.ts',specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',video: false,},
});// 登录流程测试
// login.cy.ts
describe('Login Flow', () => {beforeEach(() => {cy.visit('/login');});it('should login successfully with valid credentials', () => {cy.get('[data-testid=email]').type('user@example.com');cy.get('[data-testid=password]').type('password123');cy.get('[data-testid=login-button]').click();cy.url().should('include', '/dashboard');cy.get('[data-testid=welcome-message]').should('contain', 'Welcome back');});it('should show error with invalid credentials', () => {cy.get('[data-testid=email]').type('invalid@example.com');cy.get('[data-testid=password]').type('wrongpass');cy.get('[data-testid=login-button]').click();cy.get('[data-testid=error-message]').should('be.visible').and('contain', 'Invalid credentials');});
});
自动化部署流程 🚀
部署配置
// deploy.config.js
module.exports = {apps: [{name: 'frontend-app',script: 'serve',env: {PM2_SERVE_PATH: './build',PM2_SERVE_PORT: 3000,PM2_SERVE_SPA: 'true',NODE_ENV: 'production'}}],deploy: {production: {user: 'deploy',host: ['prod-server'],ref: 'origin/main',repo: 'git@github.com:username/repo.git',path: '/var/www/production','post-deploy': 'npm ci && npm run build && pm2 reload deploy.config.js'},staging: {user: 'deploy',host: ['staging-server'],ref: 'origin/develop',repo: 'git@github.com:username/repo.git',path: '/var/www/staging','post-deploy': 'npm ci && npm run build && pm2 reload deploy.config.js'}}
};
环境配置管理
// 环境变量配置
// .env.production
REACT_APP_API_URL=https://api.production.com
REACT_APP_SENTRY_DSN=https://sentry.production.com
REACT_APP_GA_ID=UA-XXXXXXXXX-1// .env.staging
REACT_APP_API_URL=https://api.staging.com
REACT_APP_SENTRY_DSN=https://sentry.staging.com
REACT_APP_GA_ID=UA-XXXXXXXXX-2// 配置加载
// config.ts
interface Config {apiUrl: string;sentryDsn: string;gaId: string;
}export const config: Config = {apiUrl: process.env.REACT_APP_API_URL!,sentryDsn: process.env.REACT_APP_SENTRY_DSN!,gaId: process.env.REACT_APP_GA_ID!
};
质量控制与监控 📊
代码质量检查
// .eslintrc.js
module.exports = {extends: ['react-app','react-app/jest','plugin:@typescript-eslint/recommended','plugin:prettier/recommended'],rules: {'@typescript-eslint/explicit-module-boundary-types': 'error','@typescript-eslint/no-explicit-any': 'error','react-hooks/rules-of-hooks': 'error','react-hooks/exhaustive-deps': 'warn'}
};// package.json
{"scripts": {"lint": "eslint src --ext .ts,.tsx","lint:fix": "eslint src --ext .ts,.tsx --fix","format": "prettier --write \"src/**/*.{ts,tsx,scss}\""},"husky": {"hooks": {"pre-commit": "lint-staged"}},"lint-staged": {"src/**/*.{ts,tsx}": ["eslint --fix","prettier --write"]}
}
性能监控
// performance-monitoring.ts
import * as Sentry from '@sentry/react';export const initializePerformanceMonitoring = (): void => {// 初始化Sentry性能监控Sentry.init({dsn: process.env.REACT_APP_SENTRY_DSN,tracesSampleRate: 0.2,integrations: [new Sentry.BrowserTracing({tracingOrigins: ['localhost', 'your-site.com']})]});// 监控关键性能指标if ('performance' in window) {const observer = new PerformanceObserver((list) => {list.getEntries().forEach((entry) => {if (entry.entryType === 'largest-contentful-paint') {Sentry.captureMessage(`LCP: ${entry.startTime}`,'info');}});});observer.observe({entryTypes: ['largest-contentful-paint']});}
};
最佳实践与建议 ⭐
CI/CD 最佳实践
-
分支策略
- 使用Git Flow或Trunk Based Development
- 保护主干分支
- 强制代码审查
-
构建优化
- 缓存依赖和构建产物
- 并行执行任务
- 按需构建和测试
-
测试策略
- 单元测试必须通过
- 集成测试覆盖关键流程
- 性能测试基准线
-
部署安全
- 环境变量加密
- 访问权限控制
- 部署审计日志
开发流程建议
- 提交规范
# 使用conventional commits
feat: add new feature
fix: resolve bug
docs: update documentation
style: format code
refactor: refactor code
test: add tests
chore: update build tasks
- 版本管理
{"scripts": {"release": "standard-version"},"standard-version": {"types": [{"type": "feat", "section": "Features"},{"type": "fix", "section": "Bug Fixes"},{"type": "docs", "section": "Documentation"},{"type": "style", "section": "Styles"},{"type": "refactor", "section": "Code Refactoring"},{"type": "perf", "section": "Performance Improvements"},{"type": "test", "section": "Tests"},{"type": "build", "section": "Build System"},{"type": "ci", "section": "Continuous Integration"},{"type": "chore", "section": "Chores"},{"type": "revert", "section": "Reverts"}]}
}
- 文档维护
- 更新README.md
- 维护CHANGELOG.md
- 编写部署文档
- 记录问题解决方案
结语 📝
持续集成是现代前端开发不可或缺的一部分,它不仅能提高团队的开发效率,还能保证代码质量和部署可靠性。通过本文,我们学习了:
- 持续集成的基本概念和重要性
- 主流CI/CD工具的使用方法
- 自动化测试和部署的实践
- 代码质量控制和性能监控
- CI/CD最佳实践和建议
💡 学习建议:
- 从简单的CI流程开始,逐步添加更多自动化步骤
- 重视测试覆盖率,编写高质量的测试用例
- 关注部署安全性,做好环境隔离
- 持续优化构建速度和部署效率
- 建立团队的CI/CD文化
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻
相关文章:
工程化与框架系列(8)--持续集成实践
持续集成实践 🔄 持续集成(Continuous Integration,简称CI)是现代前端开发流程中的重要环节,它通过自动化构建、测试和部署,帮助团队更快速、更可靠地交付高质量代码。本文将详细介绍前端持续集成的实践方…...
Python核心技术,Django学习基础入门教程(附环境安装包)
文章目录 前言1. 环境准备1.1Python安装1.2选择Python开发环境1.3 创建虚拟环境1.4 安装 Django 2. 创建 Django 项目3. Django项目结构介绍4. 启动开发服务器5. 创建 Django 应用6. 应用结构介绍7. 编写视图函数8. 配置 URL 映射9. 运行项目并访问视图10. 数据库配置与模型创建…...
【Qt-信号与槽】connect函数的用法
🏠个人主页:Yui_ 🍑操作环境:Qt Creator 🚀所属专栏:Qt 文章目录 1.信号和槽的概念1.1 信号的本质1.2 槽的本质1.3 补充说明2. 信号和槽的使用2.1 connect函数介绍2.2 connect函数的简单使用2.2.1 图形化方…...
计算机毕业设计SpringBoot+Vue.js景区民宿预约系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
服务流程设计和服务或端口重定向及其websocket等应用示例
服务流程设计和服务或端口重定向及其websocket等应用示例 目录 服务或端口重定向的服务设计和websocket等应用示例 一、通用请求控制流程 1.1、入口 1.2、所有GET请求首先预检控制单元 1.3、http请求会分别自动307重定向 1.4、所有请求首先执行跨源控制单元 1.5、然后…...
16. LangChain实战项目2——易速鲜花内部问答系统
需求简介 易束鲜花企业内部知识库如下: 本实战项目设计一个内部问答系统,基于这些内部知识,回答内部员工的提问。 在前面课程的基础上,需要安装的依赖包如下: pip install docx2txt pip install qdrant-client pip i…...
一文了解Conda使用
一、Conda库频道 conda的软件频道是存储软件包的远程位置,当在Conda中安装软件包时,它会从指定的频道中下载和提取软件包。频道包含了各种软件包,不同的频道可能提供不同版本的软件包,用户可以根据需要选择适合的版本。 常见 Co…...
AI辅助学习vue第十四章
第十四章:技术引领与未来展望 在第十五章,你已经在Vue技术领域深耕许久,积累了丰富的经验与卓越的影响力。此时,你将站在行业前沿,引领技术走向,为Vue技术的未来发展开辟新道路。 1. 引领Vue技术发展方向…...
chromadb向量数据库使用 (1)
目录 完整代码代码解释 完整代码 import chromadb chroma_client chromadb.Client()collection chroma_client.create_collection(name"my_collection")collection.add(documents["This is a document about pineapple","This is a document about…...
CSS—text文本、font字体、列表list、表格table、表单input、下拉菜单select
目录 1.文本 2.字体 3.列表list a.无序列表 b.有序列表 c.定义列表 4.表格table a.内容 b.合并单元格 3.表单input a.input标签 b.单选框 c.上传文件 4.下拉菜单 1.文本 属性描述color设置文本颜色。direction指定文本的方向 / 书写方向。letter-spacing设置字符…...
关于大型语言模型的结构修剪
本文介绍了一种名为 **LLM-Pruner** 的方法,用于对大型语言模型(LLMs)进行结构化剪枝,以减少模型大小和计算需求,同时保留其多任务解决和语言生成能力。LLM-Pruner 通过依赖检测和重要性估计实现高效剪枝,并…...
PostgreSQL 生产环境升级指南:pg_upgrade 快速完成版本升级!
前言 PostgreSQL 的版本号由主要版本号和次要版本号组成。例如,在 10.1 中,10 是主要版本,1 是次要版本。关于更多版本的规划,请参考 PostgreSQL 版本路线图。 版本号规则: PostgreSQL 10 及以后:版本号…...
Ubuntu2204下使用NVIDIA GeForce RTX 4090进行DeepSeek-R1-Distill-Llama-8B模型微调
Ubuntu2204下使用NVIDIA GeForce RTX 4090进行DeepSeek-R1-Distill-Llama-8B模型微调 环境准备创建Python微调环境准备数据集准备模型文件 模型微调模型预测原始模型预测微调模型预测 使用unsloth,可以方便地对大模型进行微调。以微调DeepSeek-R1-Distill-Llama-8B为…...
JAVA面试常见题_基础部分_mybatis面试题
1、什么是 MyBatis? 答:MyBatis 是一个可以自定义 SQL、存储过程和高级映射的持久层框架。 2、讲下 MyBatis 的缓存答 :MyBatis 的缓存分为一级缓存和二级缓存,一级缓存放在 session 里面,默认就有,二级缓存放在它的命名空间里,默认是不打…...
RISC-V汇编学习(一)—— 基础认识
最近这三年的工作时间大部分的工作,都是基于riscv的cpu和接口ip开发适配驱动,时不时的就要debug测试代码,面对很多都是汇编,所以也是整理下积累的一点点笔记,系列博客将总结下riscv相关的内容,一是给有需要…...
【Delphi】如何解决使用webView2时主界面置顶,而导致网页选择文件对话框被覆盖问题
一、问题描述: 在Delphi 中使用WebView2控件,如果预先把主界面置顶(Self.FormStyle : fsStayOnTop;),此时,如果在Web页面中有使用(<input type"file" id"fileInput" acc…...
基于POI的Excel下拉框自动搜索,包括数据验证的单列删除
目录 目标 例子 1.搜索下拉框页 2.数据源页 3.效果 代码以及注意事项 1.代码 2.注意事项 1.基于Excel的话,相当于加入了一个【数据验证】 2.代码中的一些方法说明 目标 期望在Excel利用代码创建具备自动搜索功能的下拉框 例子 1.搜索下拉框页 2.数据源…...
基金 word-->pdf图片模糊的解决方法
1. 首先需要Adobe或福昕等pdf阅读器。 2. word中 [文件]--[打印],其中打印机选择pdf阅读器,例如此处我选择福昕阅读器。 3. 选择 [打印机属性]--[编辑]--[图像],将所有的采样、压缩均设置为 关闭。点击[另存为],保存为 基金报告…...
React底层原理详解
React中Element&Fiber对象、WorkInProgress双缓存、Reconcile&Render&Commit、第一次挂载过程详解 在面试中介绍React底层原理时,需遵循逻辑清晰、层次分明、重点突出的原则,结合技术深度与实际应用场景。以下是结构化回答模板:…...
Word 插入图片会到文字底下解决方案
一、现象描述 正常情况下,我们插入图片都是这样的。 但有时突然会这样,插入的图片陷于文字底部。 二、网上解决方案 网上有教程说,修改图片布局选项,从嵌入型改成上下型环绕。改完之后确实有用,但是需要手动拖动图片…...
Pixel Couplet Gen效果展示:基于用户画像(年龄/地域)的像素春联风格个性化推荐
Pixel Couplet Gen效果展示:基于用户画像(年龄/地域)的像素春联风格个性化推荐 1. 项目概览 Pixel Couplet Gen是一款融合传统春节文化与现代像素艺术风格的AI春联生成器。通过ModelScope大模型的强大生成能力,结合精心设计的8-…...
Anaconda环境管理:为Phi-4-mini-reasoning 3.8B创建独立的Python开发环境
Anaconda环境管理:为Phi-4-mini-reasoning 3.8B创建独立的Python开发环境 1. 为什么需要独立环境? 在数据科学和机器学习项目中,环境隔离是个经常被忽视但极其重要的问题。想象一下这样的场景:你花了两周时间调试一个模型&#…...
Java程序员终于有自己的AI Agent框架了:Spring AI Alibaba上手实录
Java程序员终于有自己的AI Agent框架了:Spring AI Alibaba上手实录 说实话,作为一个写了多年Java的人,看着Python那边各种AI框架、Agent工具层出不穷,心里是有点酸的。LangChain、AutoGPT、CrewAI…全是Python的天下。Java开发者想…...
RK Android14 开机自启APP分析与使用
文章目录 前言 一、功能补丁 二、如何使用 1. 应用补丁 2. 设置自启动应用 3. 获取应用包名和Activity 4. 验证 总结 前言 根据客户需要,有时需要设置第三方的apk进行开机自启动。 一、功能补丁 功能分析: 系统启动完成后,自动启动系统属性 persist.sys.start.app 中配置的…...
杰理之关机DAC未进入高阻【篇】
memset(JL_ADDA, 0x0, sizeof(JL_ADDA_TypeDef)); SFR(JL_ADDA->DAA_CON2, 15, 1, 1); SFR(JL_ADDA->DAA_CON2, 5, 1, 1);...
【仅限头部车企工控厂商内部流通】C++27静态反射安全合规包:覆盖MISRA C++:2023 Annex A.12及AUTOSAR C++14兼容性桥接层
第一章:C27 静态反射工业应用案例C27 将正式引入标准化的静态反射(Static Reflection)核心特性,基于 std::reflexpr 和反射元对象模型(ROM),使编译期类型结构可被直接查询与遍历,无需…...
OpenClaw环境迁移:gemma-3-12b-it配置备份与恢复指南
OpenClaw环境迁移:gemma-3-12b-it配置备份与恢复指南 1. 为什么需要环境迁移方案 上周我的主力开发机突然硬盘故障,导致所有数据丢失。最让我头疼的不是代码仓库——它们都有远程备份,而是那套精心调校的OpenClawgemma-3-12b-it环境。花了整…...
轻量级嵌入式软传感库:用双BME280实现太阳辐射实时反演
1. 项目概述FiaPhy 是一个面向嵌入式环境的轻量级软传感(Soft-Sensing)库,核心实现差分时间导数软传感(Differential Temporal Derivative Soft-Sensing, DTDSS)算法。该库不依赖专用辐射计硬件,而是通过部…...
STM32自动循迹小车设计与实现
1. 项目概述2016年TI杯电子设计竞赛中,我们团队设计了一款基于STM32的自动循迹小车系统。这个项目获得了省级一等奖,也是我职业生涯的重要转折点。作为控制类题目,系统需要实现沿预定轨迹自动行驶,并能检测轨迹旁的金属硬币。核心…...
如何通过哈氏训练提升孩子的学习能力以应对多动症表现和作业拖延症?
如何运用哈氏训练助力孩子克服多动症表现与作业拖延 哈氏训练是一种有效的应对策略,尤其对有多动症表现和作业拖延症的孩子。首先,这种训练方法可以帮助孩子建立稳定的日常作息,提高他们的注意力和自我控制能力。通过结构化的活动和渐进式的任…...
