构建现代化前端工程体系,提升开发效率与代码质量
引言
前端工程化是现代Web开发的核心基础设施,涵盖了从代码编写、构建打包、质量保证到部署发布的完整流程。随着前端应用复杂度的不断提升,建立完善的工程化体系变得至关重要。本文将深入探讨前端工程化的各个方面,提供从构建优化到CI/CD的完整解决方案。
1. 现代化构建系统设计
1.1 构建工具链管理器
javascript
// 构建工具链管理器
class BuildToolchainManager {
constructor(options = {}) {
this.options = {
mode: process.env.NODE_ENV || 'development',
enableHMR: true,
enableSourceMap: true,
enableTreeShaking: true,
enableCodeSplitting: true,
enableBundleAnalysis: false,
outputPath: 'dist',
publicPath: '/',
...options
};
this.plugins = new Map();
this.loaders = new Map();
this.optimizations = new Map();
this.devServer = null;
this.init();
}
async init() {
try {
// 初始化构建配置
this.initBuildConfig();
// 设置加载器
this.setupLoaders();
// 设置插件
this.setupPlugins();
// 设置优化选项
this.setupOptimizations();
// 设置开发服务器
if (this.options.mode === 'development') {
this.setupDevServer();
}
console.log('构建工具链管理器初始化完成');
} catch (error) {
console.error('构建工具链管理器初始化失败:', error);
}
}
// 初始化构建配置
initBuildConfig() {
this.buildConfig = {
entry: {
main: './src/index.js',
vendor: ['react', 'react-dom', 'lodash']
},
output: {
path: path.resolve(process.cwd(), this.options.outputPath),
filename: this.options.mode === 'production'
? '[name].[contenthash:8].js'
: '[name].js',
chunkFilename: this.options.mode === 'production'
? '[name].[contenthash:8].chunk.js'
: '[name].chunk.js',
publicPath: this.options.publicPath,
clean: true
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue', '.json'],
alias: {
'@': path.resolve(process.cwd(), 'src'),
'@components': path.resolve(process.cwd(), 'src/components'),
'@utils': path.resolve(process.cwd(), 'src/utils'),
'@assets': path.resolve(process.cwd(), 'src/assets')
},
fallback: {
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"buffer": require.resolve("buffer")
}
},
module: {
rules: []
},
plugins: [],
optimization: {},
devtool: this.getSourceMapType(),
target: 'web',
mode: this.options.mode
};
}
// 设置加载器
setupLoaders() {
// JavaScript/TypeScript 加载器
this.addLoader('javascript', {
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
useBuiltIns: 'usage',
corejs: 3,
targets: {
browsers: ['> 1%', 'last 2 versions']
}
}],
'@babel/preset-react',
'@babel/preset-typescript'
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
this.options.enableHMR && 'react-hot-loader/babel'
].filter(Boolean),
cacheDirectory: true
}
}
});
// CSS 加载器
this.addLoader('css', {
test: /\.css$/,
use: this.getCSSLoaders()
});
// SCSS/SASS 加载器
this.addLoader('scss', {
test: /\.(scss|sass)$/,
use: [
...this.getCSSLoaders(),
{
loader: 'sass-loader',
options: {
implementation: require('sass'),
sassOptions: {
fiber: require('fibers')
}
}
}
]
});
// Less 加载器
this.addLoader('less', {
test: /\.less$/,
use: [
...this.getCSSLoaders(),
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true
}
}
}
]
});
// 图片加载器
this.addLoader('images', {
test: /\.(png|jpe?g|gif|svg|webp|avif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8KB
}
},
generator: {
filename: 'images/[name].[hash:8][ext]'
},
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 80
},
optipng: {
enabled: false
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false
},
webp: {
quality: 80
}
}
}
]
});
// 字体加载器
this.addLoader('fonts', {
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[hash:8][ext]'
}
});
// Vue 加载器
this.addLoader('vue', {
test: /\.vue$/,
use: 'vue-loader'
});
// 应用所有加载器
this.loaders.forEach((loader, name) => {
this.buildConfig.module.rules.push(loader);
});
}
// 设置插件
setupPlugins() {
// HTML 插件
this.addPlugin('html', new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
inject: true,
minify: this.options.mode === 'production' ? {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
} : false
}));
// 环境变量插件
this.addPlugin('define', new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(this.options.mode),
'process.env.PUBLIC_URL': JSON.stringify(this.options.publicPath),
'__DEV__': this.options.mode === 'development',
'__PROD__': this.options.mode === 'production'
}));
// CSS 提取插件
if (this.options.mode === 'production') {
this.addPlugin('extractCSS', new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].chunk.css'
}));
}
// 复制静态资源插件
this.addPlugin('copy', new CopyWebpackPlugin({
patterns: [
{
from: 'public',
to: '.',
globOptions: {
ignore: ['**/index.html']
}
}
]
}));
// 进度条插件
this.addPlugin('progress', new webpack.ProgressPlugin({
activeModules: true,
entries: true,
modules: true,
modulesCount: 5000,
profile: false,
dependencies: true,
dependenciesCount: 10000,
percentBy: null
}));
// 热更新插件
if (this.options.mode === 'development' && this.options.enableHMR) {
this.addPlugin('hmr', new webpack.HotModuleReplacementPlugin());
}
// Bundle 分析插件
if (this.options.enableBundleAnalysis) {
this.addPlugin('bundleAnalyzer', new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html'
}));
}
// Vue 插件
if (this.hasVueFiles()) {
this.addPlugin('vue', new VueLoaderPlugin());
}
// 应用所有插件
this.plugins.forEach((plugin, name) => {
this.buildConfig.plugins.push(plugin);
});
}
// 设置优化选项
setupOptimizations() {
this.buildConfig.optimization = {
minimize: this.options.mode === 'production',
minimizer: [],
splitChunks: this.options.enableCodeSplitting ? {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\\/]node_modules[\\\/]/,
name: 'vendors',
chunks: 'all',
priority: 10
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
priority: 5,
reuseExistingChunk: true
}
}
} : false,
runtimeChunk: this.options.enableCodeSplitting ? {
name: 'runtime'
} : false,
usedExports: this.options.enableTreeShaking,
sideEffects: false
};
// 生产环境优化
if (this.options.mode === 'production') {
// JavaScript 压缩
this.buildConfig.optimization.minimizer.push(
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
drop_console: true,
drop_debugger: true
},
mangle: {
safari10: true
},
output: {
ecma: 5,
comments: false,
ascii_only: true
}
},
parallel: true,
extractComments: false
})
);
// CSS 压缩
this.buildConfig.optimization.minimizer.push(
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true }
}
]
}
})
);
}
}
// 设置开发服务器
setupDevServer() {
this.devServer = {
contentBase: path.join(process.cwd(), 'public'),
compress: true,
port: 3000,
hot: this.options.enableHMR,
open: true,
overlay: {
warnings: false,
errors: true
},
historyApiFallback: {
disableDotRule: true
},
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
before: (app, server, compiler) => {
// 自定义中间件
app.use('/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
}
};
this.buildConfig.devServer = this.devServer;
}
// 获取CSS加载器
getCSSLoaders() {
const loaders = [];
if (this.options.mode === 'production') {
loaders.push(MiniCssExtractPlugin.loader);
} else {
loaders.push('style-loader');
}
loaders.push(
{
loader: 'css-loader',
options: {
importLoaders: 1,
sourceMap: this.options.enableSourceMap
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default'
})
]
},
sourceMap: this.options.enableSourceMap
}
}
);
return loaders;
}
// 获取Source Map类型
getSourceMapType() {
if (!this.options.enableSourceMap) {
return false;
}
if (this.options.mode === 'production') {
return 'source-map';
} else {
return 'eval-cheap-module-source-map';
}
}
// 添加加载器
addLoader(name, loader) {
this.loaders.set(name, loader);
}
// 添加插件
addPlugin(name, plugin) {
this.plugins.set(name, plugin);
}
// 检查是否有Vue文件
hasVueFiles() {
const fs = require('fs');
const path = require('path');
try {
const srcPath = path.join(process.cwd(), 'src');
const files = this.getAllFiles(srcPath);
return files.some(file => file.endsWith('.vue'));
} catch (error) {
return false;
}
}
// 获取所有文件
getAllFiles(dirPath, arrayOfFiles = []) {
const fs = require('fs');
const path = require('path');
const files = fs.readdirSync(dirPath);
files.forEach(file => {
const filePath = path.join(dirPath, file);
if (fs.statSync(filePath).isDirectory()) {
arrayOfFiles = this.getAllFiles(filePath, arrayOfFiles);
} else {
arrayOfFiles.push(filePath);
}
});
return arrayOfFiles;
}
// 构建项目
async build() {
const webpack = require('webpack');
return new Promise((resolve, reject) => {
const compiler = webpack(this.buildConfig);
compiler.run((err, stats) => {
if (err) {
reject(err);
return;
}
if (stats.hasErrors()) {
const errors = stats.toJson().errors;
reject(new Error(errors.join('\n')));
return;
}
console.log(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}));
resolve(stats);
});
});
}
// 启动开发服务器
async serve() {
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const compiler = webpack(this.buildConfig);
const server = new WebpackDevServer(compiler, this.devServer);
return new Promise((resolve, reject) => {
server.listen(this.devServer.port, 'localhost', (err) => {
if (err) {
reject(err);
return;
}
console.log(`开发服务器启动在 http://localhost:${this.devServer.port}`);
resolve(server);
});
});
}
// 获取构建配置
getConfig() {
return this.buildConfig;
}
// 更新配置
updateConfig(updates) {
this.buildConfig = {
...this.buildConfig,
...updates
};
}
// 获取构建统计
getBuildStats() {
return {
loaders: this.loaders.size,
plugins: this.plugins.size,
mode: this.options.mode,
enableHMR: this.options.enableHMR,
enableSourceMap: this.options.enableSourceMap,
enableTreeShaking: this.options.enableTreeShaking,
enableCodeSplitting: this.options.enableCodeSplitting
};
}
}
1.2 构建性能优化器
javascript
// 构建性能优化器
class BuildPerformanceOptimizer {
constructor(options = {}) {
this.options = {
enableCache: true,
enableParallel: true,
enableTreeShaking: true,
enableBundleAnalysis: false,
cacheDirectory: '.cache',
maxWorkers: require('os').cpus().length - 1,
...options
};
this.cacheManager = null;
this.parallelManager = null;
this.bundleAnalyzer = null;
this.performanceMetrics = new Map();
this.init();
}
async init() {
try {
// 初始化缓存管理
if (this.options.enableCache) {
this.initCacheManager();
}
// 初始化并行化管理
if (this.options.enableParallel) {
this.initParallelManager();
}
// 初始化Tree Shaking优化
if (this.options.enableTreeShaking) {
this.initTreeShakingOptimizer();
}
// 初始化Bundle分析
if (this.options.enableBundleAnalysis) {
this.initBundleAnalyzer();
}
console.log('构建性能优化器初始化完成');
} catch (error) {
console.error('构建性能优化器初始化失败:', error);
}
}
// 初始化缓存管理
initCacheManager() {
this.cacheManager = {
// 文件系统缓存
filesystem: {
type: 'filesystem',
cacheDirectory: path.resolve(process.cwd(), this.options.cacheDirectory),
buildDependencies: {
config: [__filename]
},
version: '1.0.0'
},
// 内存缓存
memory: {
type: 'memory',
maxGenerations: 1
},
// Babel缓存
babel: {
cacheDirectory: path.resolve(process.cwd(), this.options.cacheDirectory, 'babel'),
cacheCompression: false,
cacheIdentifier: this.getCacheIdentifier()
},
// ESLint缓存
eslint: {
cache: true,
cacheLocation: path.resolve(process.cwd(), this.options.cacheDirectory, 'eslint')
},
// TypeScript缓存
typescript: {
transpileOnly: true,
experimentalWatchApi: true,
compilerOptions: {
incremental: true,
tsBuildInfoFile: path.resolve(process.cwd(), this.options.cacheDirectory, 'tsconfig.tsbuildinfo')
}
}
};
}
// 初始化并行化管理
initParallelManager() {
this.parallelManager = {
// Thread Loader配置
threadLoader: {
workers: this.options.maxWorkers,
workerParallelJobs: 50,
poolTimeout: 2000,
poolParallelJobs: 200,
name: 'thread-pool'
},
// Terser并行配置
terserParallel: {
parallel: this.options.maxWorkers,
cache: true
},
// CSS优化并行配置
cssParallel: {
parallel: this.options.maxWorkers
},
// 图片优化并行配置
imageParallel: {
workers: Math.min(this.options.maxWorkers, 4)
}
};
}
// 初始化Tree Shaking优化
initTreeShakingOptimizer() {
this.treeShakingOptimizer = {
// Webpack配置
webpack: {
mode: 'production',
optimization: {
usedExports: true,
sideEffects: false,
providedExports: true,
concatenateModules: true
}
},
// Babel配置
babel: {
presets: [
['@babel/preset-env', {
modules: false, // 保持ES模块格式
useBuiltIns: 'usage',
corejs: 3
}]
],
plugins: [
// 移除未使用的导入
'babel-plugin-transform-imports',
// 优化lodash导入
['babel-plugin-lodash', {
id: ['lodash', 'recompose']
}]
]
},
// 第三方库优化
libraryOptimization: {
// Lodash优化
lodash: {
plugins: ['lodash-webpack-plugin']
},
// Moment.js优化
moment: {
plugins: ['moment-locales-webpack-plugin'],
locales: ['zh-cn']
},
// Antd优化
antd: {
plugins: [
['import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css'
}]
]
}
}
};
}
// 初始化Bundle分析器
initBundleAnalyzer() {
this.bundleAnalyzer = {
// Webpack Bundle Analyzer
webpackAnalyzer: {
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html',
generateStatsFile: true,
statsFilename: 'bundle-stats.json'
},
// Bundle Size Analyzer
sizeAnalyzer: {
maxAssetSize: 250000, // 250KB
maxEntrypointSize: 250000, // 250KB
hints: 'warning'
},
// Duplicate Package Checker
duplicateChecker: {
verbose: true,
emitError: false,
showHelp: true,
strict: false
}
};
}
// 获取缓存标识符
getCacheIdentifier() {
const crypto = require('crypto');
const packageJson = require(path.resolve(process.cwd(), 'package.json'));
const identifier = {
'babel-loader': packageJson.dependencies['babel-loader'] || '0.0.0',
'@babel/core': packageJson.dependencies['@babel/core'] || '0.0.0',
'@babel/preset-env': packageJson.dependencies['@babel/preset-env'] || '0.0.0',
'babel-preset-react-app': packageJson.dependencies['babel-preset-react-app'] || '0.0.0',
NODE_ENV: process.env.NODE_ENV || 'development'
};
return crypto
.createHash('md5')
.update(JSON.stringify(identifier))
.digest('hex');
}
// 分析构建性能
async analyzeBuildPerformance(stats) {
const analysis = {
buildTime: stats.endTime - stats.startTime,
assetSizes: {},
chunkSizes: {},
moduleCount: 0,
warnings: stats.compilation.warnings.length,
errors: stats.compilation.errors.length,
cacheHitRate: 0,
parallelEfficiency: 0
};
// 分析资源大小
stats.compilation.assets.forEach((asset, name) => {
analysis.assetSizes[name] = asset.size();
});
// 分析代码块大小
stats.compilation.chunks.forEach(chunk => {
analysis.chunkSizes[chunk.name || chunk.id] = chunk.size();
});
// 统计模块数量
analysis.moduleCount = stats.compilation.modules.size;
// 计算缓存命中率
if (this.options.enableCache) {
analysis.cacheHitRate = this.calculateCacheHitRate(stats);
}
// 计算并行化效率
if (this.options.enableParallel) {
analysis.parallelEfficiency = this.calculateParallelEfficiency(stats);
}
this.performanceMetrics.set('latest', analysis);
return analysis;
}
// 计算缓存命中率
calculateCacheHitRate(stats) {
let cacheHits = 0;
let totalModules = 0;
stats.compilation.modules.forEach(module => {
totalModules++;
if (module.buildInfo && module.buildInfo.cacheable) {
cacheHits++;
}
});
return totalModules > 0 ? (cacheHits / totalModules) * 100 : 0;
}
// 计算并行化效率
calculateParallelEfficiency(stats) {
const buildTime = stats.endTime - stats.startTime;
const expectedParallelTime = buildTime / this.options.maxWorkers;
const actualParallelTime = buildTime;
return expectedParallelTime > 0 ? (expectedParallelTime / actualParallelTime) * 100 : 0;
}
// 生成性能报告
generatePerformanceReport() {
const analysis = this.performanceMetrics.get('latest');
if (!analysis) {
console.warn('没有可用的性能分析数据');
return null;
}
const report = {
summary: {
buildTime: `${(analysis.buildTime / 1000).toFixed(2)}s`,
moduleCount: analysis.moduleCount,
assetCount: Object.keys(analysis.assetSizes).length,
chunkCount: Object.keys(analysis.chunkSizes).length,
warnings: analysis.warnings,
errors: analysis.errors
},
performance: {
cacheHitRate: `${analysis.cacheHitRate.toFixed(2)}%`,
parallelEfficiency: `${analysis.parallelEfficiency.toFixed(2)}%`
},
assets: {
largest: this.getLargestAssets(analysis.assetSizes, 5),
total: Object.values(analysis.assetSizes).reduce((sum, size) => sum + size, 0)
},
chunks: {
largest: this.getLargestAssets(analysis.chunkSizes, 5),
total: Object.values(analysis.chunkSizes).reduce((sum, size) => sum + size, 0)
},
recommendations: this.generateRecommendations(analysis)
};
return report;
}
// 获取最大的资源
getLargestAssets(assets, count = 5) {
return Object.entries(assets)
.sort(([, a], [, b]) => b - a)
.slice(0, count)
.map(([name, size]) => ({ name, size: this.formatSize(size) }));
}
// 格式化文件大小
formatSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
if (bytes === 0) return '0 Bytes';
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
// 生成优化建议
generateRecommendations(analysis) {
const recommendations = [];
// 构建时间建议
if (analysis.buildTime > 60000) { // 超过1分钟
recommendations.push({
type: 'performance',
priority: 'high',
message: '构建时间过长,建议启用缓存和并行化构建'
});
}
// 资源大小建议
const largeAssets = Object.entries(analysis.assetSizes)
.filter(([, size]) => size > 250000); // 超过250KB
if (largeAssets.length > 0) {
recommendations.push({
type: 'bundle-size',
priority: 'medium',
message: `发现${largeAssets.length}个大型资源文件,建议进行代码分割和压缩优化`
});
}
// 缓存命中率建议
if (analysis.cacheHitRate < 50) {
recommendations.push({
type: 'cache',
priority: 'medium',
message: '缓存命中率较低,建议检查缓存配置'
});
}
// 并行化效率建议
if (analysis.parallelEfficiency < 30) {
recommendations.push({
type: 'parallel',
priority: 'low',
message: '并行化效率较低,建议调整worker数量或优化任务分配'
});
}
return recommendations;
}
// 清理缓存
async clearCache() {
const fs = require('fs-extra');
const cachePath = path.resolve(process.cwd(), this.options.cacheDirectory);
try {
await fs.remove(cachePath);
console.log('缓存清理完成');
} catch (error) {
console.error('缓存清理失败:', error);
}
}
// 获取优化配置
getOptimizationConfig() {
return {
cache: this.cacheManager,
parallel: this.parallelManager,
treeShaking: this.treeShakingOptimizer,
bundleAnalysis: this.bundleAnalyzer
};
}
// 应用优化到Webpack配置
applyOptimizations(webpackConfig) {
// 应用缓存优化
if (this.options.enableCache && this.cacheManager) {
webpackConfig.cache = this.cacheManager.filesystem;
// 应用Babel缓存
const babelRule = this.findRule(webpackConfig, /\.(js|jsx|ts|tsx)$/);
if (babelRule && babelRule.use && babelRule.use.options) {
Object.assign(babelRule.use.options, this.cacheManager.babel);
}
}
// 应用并行化优化
if (this.options.enableParallel && this.parallelManager) {
// 添加Thread Loader
const jsRule = this.findRule(webpackConfig, /\.(js|jsx|ts|tsx)$/);
if (jsRule && jsRule.use) {
jsRule.use.unshift({
loader: 'thread-loader',
options: this.parallelManager.threadLoader
});
}
}
// 应用Tree Shaking优化
if (this.options.enableTreeShaking && this.treeShakingOptimizer) {
Object.assign(webpackConfig.optimization, this.treeShakingOptimizer.webpack.optimization);
}
return webpackConfig;
}
// 查找Webpack规则
findRule(config, test) {
return config.module.rules.find(rule =>
rule.test && rule.test.toString() === test.toString()
);
}
}
2. CI/CD 流水线设计
2.1 持续集成管理器
javascript
// 持续集成管理器
class ContinuousIntegrationManager {
constructor(options = {}) {
this.options = {
platform: 'github', // github, gitlab, jenkins
enableLinting: true,
enableTesting: true,
enableSecurity: true,
enablePerformance: true,
enableNotification: true,
...options
};
this.pipeline = new Map();
this.stages = new Map();
this.notifications = new Map();
this.init();
}
async init() {
try {
// 初始化流水线阶段
this.initPipelineStages();
// 初始化质量检查
this.initQualityChecks();
// 初始化安全检查
this.initSecurityChecks();
// 初始化性能检查
this.initPerformanceChecks();
// 初始化通知系统
this.initNotificationSystem();
console.log('持续集成管理器初始化完成');
} catch (error) {
console.error('持续集成管理器初始化失败:', error);
}
}
// 初始化流水线阶段
initPipelineStages() {
// 代码检出阶段
this.addStage('checkout', {
name: '代码检出',
description: '从版本控制系统检出代码',
commands: [
'git checkout ${{ github.ref }}',
'git submodule update --init --recursive'
],
timeout: 300, // 5分钟
retries: 3
});
// 依赖安装阶段
this.addStage('install', {
name: '依赖安装',
description: '安装项目依赖',
commands: [
'npm ci --prefer-offline --no-audit',
'npm run postinstall'
],
cache: {
key: 'npm-${{ hashFiles("**/package-lock.json") }}',
paths: ['node_modules', '.npm']
},
timeout: 600, // 10分钟
retries: 2
});
// 代码质量检查阶段
this.addStage('quality', {
name: '代码质量检查',
description: '执行代码质量检查',
commands: [
'npm run lint',
'npm run type-check',
'npm run format:check'
],
parallel: true,
timeout: 300,
retries: 1
});
// 单元测试阶段
this.addStage('test', {
name: '单元测试',
description: '执行单元测试',
commands: [
'npm run test:unit -- --coverage',
'npm run test:integration'
],
artifacts: {
reports: ['coverage/**/*', 'test-results/**/*'],
expire: '30 days'
},
timeout: 900, // 15分钟
retries: 2
});
// 安全检查阶段
this.addStage('security', {
name: '安全检查',
description: '执行安全漏洞检查',
commands: [
'npm audit --audit-level moderate',
'npm run security:scan'
],
timeout: 300,
retries: 1
});
// 构建阶段
this.addStage('build', {
name: '项目构建',
description: '构建生产版本',
commands: [
'npm run build',
'npm run build:analyze'
],
artifacts: {
build: ['dist/**/*', 'build/**/*'],
reports: ['bundle-report.html', 'bundle-stats.json'],
expire: '7 days'
},
timeout: 1200, // 20分钟
retries: 1
});
// 端到端测试阶段
this.addStage('e2e', {
name: '端到端测试',
description: '执行端到端测试',
commands: [
'npm run test:e2e'
],
services: [
{
name: 'selenium',
image: 'selenium/standalone-chrome:latest',
ports: ['4444:4444']
}
],
timeout: 1800, // 30分钟
retries: 2
});
// 部署阶段
this.addStage('deploy', {
name: '应用部署',
description: '部署到目标环境',
commands: [
'npm run deploy:staging',
'npm run deploy:production'
],
environment: {
staging: {
url: 'https://staging.example.com',
variables: {
NODE_ENV: 'staging',
API_URL: 'https://api-staging.example.com'
}
},
production: {
url: 'https://example.com',
variables: {
NODE_ENV: 'production',
API_URL: 'https://api.example.com'
},
protection: {
required_reviewers: 2,
dismiss_stale_reviews: true
}
}
},
timeout: 600,
retries: 1
});
}
// 初始化质量检查
initQualityChecks() {
this.qualityChecks = {
// ESLint配置
eslint: {
configFile: '.eslintrc.js',
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
cache: true,
cacheLocation: '.eslintcache',
fix: false,
maxWarnings: 0,
outputFile: 'eslint-report.json',
format: 'json'
},
// Prettier配置
prettier: {
configFile: '.prettierrc',
ignorePath: '.prettierignore',
check: true,
write: false,
listDifferent: true
},
// TypeScript配置
typescript: {
configFile: 'tsconfig.json',
noEmit: true,
skipLibCheck: true,
strict: true
},
// Stylelint配置
stylelint: {
configFile: '.stylelintrc.js',
syntax: 'scss',
cache: true,
cacheLocation: '.stylelintcache',
fix: false,
outputFile: 'stylelint-report.json',
formatter: 'json'
},
// SonarQube配置
sonarqube: {
projectKey: 'frontend-project',
sources: 'src',
tests: 'src',
testInclusions: '**/*.test.js,**/*.test.ts,**/*.spec.js,**/*.spec.ts',
coverageReportPaths: 'coverage/lcov.info',
eslintReportPaths: 'eslint-report.json',
exclusions: '**/node_modules/**,**/dist/**,**/build/**'
}
};
}
// 初始化安全检查
initSecurityChecks() {
this.securityChecks = {
// npm audit配置
npmAudit: {
level: 'moderate',
production: true,
json: true,
outputFile: 'npm-audit-report.json'
},
// Snyk配置
snyk: {
test: true,
monitor: true,
severity: 'medium',
json: true,
outputFile: 'snyk-report.json'
},
// OWASP Dependency Check
owaspDependencyCheck: {
project: 'frontend-project',
scan: 'package-lock.json',
format: 'JSON',
outputFile: 'owasp-dependency-check-report.json'
},
// 敏感信息检查
secretsCheck: {
patterns: [
/(?i)(password|passwd|pwd)\s*[=:]\s*["'][^"']+["']/,
/(?i)(api[_-]?key|apikey)\s*[=:]\s*["'][^"']+["']/,
/(?i)(secret|token)\s*[=:]\s*["'][^"']+["']/,
/(?i)(access[_-]?token)\s*[=:]\s*["'][^"']+["']/
],
exclude: [
'node_modules/**',
'dist/**',
'build/**',
'**/*.test.js',
'**/*.spec.js'
]
}
};
}
// 初始化性能检查
initPerformanceChecks() {
this.performanceChecks = {
// Lighthouse配置
lighthouse: {
url: 'http://localhost:3000',
config: {
extends: 'lighthouse:default',
settings: {
onlyCategories: ['performance', 'accessibility', 'best-practices', 'seo'],
skipAudits: ['uses-http2']
}
},
thresholds: {
performance: 90,
accessibility: 90,
'best-practices': 90,
seo: 90
},
outputFile: 'lighthouse-report.json'
},
// Bundle分析配置
bundleAnalysis: {
maxAssetSize: 250000, // 250KB
maxEntrypointSize: 250000, // 250KB
outputFile: 'bundle-analysis-report.json'
},
// 加载时间检查
loadTimeCheck: {
url: 'http://localhost:3000',
thresholds: {
firstContentfulPaint: 2000, // 2秒
largestContentfulPaint: 4000, // 4秒
firstInputDelay: 100, // 100毫秒
cumulativeLayoutShift: 0.1
},
runs: 5,
outputFile: 'load-time-report.json'
}
};
}
// 初始化通知系统
initNotificationSystem() {
this.notifications = {
// Slack通知
slack: {
webhook: process.env.SLACK_WEBHOOK_URL,
channel: '#frontend-ci',
username: 'CI Bot',
iconEmoji: ':robot_face:',
templates: {
success: {
color: 'good',
title: '✅ 构建成功',
message: '项目构建和部署成功完成'
},
failure: {
color: 'danger',
title: '❌ 构建失败',
message: '项目构建过程中发生错误'
},
warning: {
color: 'warning',
title: '⚠️ 构建警告',
message: '项目构建完成但存在警告'
}
}
},
// 邮件通知
email: {
smtp: {
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
},
from: 'ci@example.com',
to: ['team@example.com'],
templates: {
success: {
subject: '[CI] 构建成功 - {{project}} #{{buildNumber}}',
html: '<h2>构建成功</h2><p>项目 {{project}} 的构建 #{{buildNumber}} 已成功完成。</p>'
},
failure: {
subject: '[CI] 构建失败 - {{project}} #{{buildNumber}}',
html: '<h2>构建失败</h2><p>项目 {{project}} 的构建 #{{buildNumber}} 失败。</p><p>错误信息:{{error}}</p>'
}
}
},
// 企业微信通知
wechat: {
webhook: process.env.WECHAT_WEBHOOK_URL,
templates: {
success: {
msgtype: 'markdown',
markdown: {
content: '## ✅ 构建成功\n\n项目:{{project}}\n构建号:#{{buildNumber}}\n分支:{{branch}}\n提交:{{commit}}\n\n[查看详情]({{buildUrl}})'
}
},
failure: {
msgtype: 'markdown',
markdown: {
content: '## ❌ 构建失败\n\n项目:{{project}}\n构建号:#{{buildNumber}}\n分支:{{branch}}\n提交:{{commit}}\n错误:{{error}}\n\n[查看详情]({{buildUrl}})'
}
}
}
}
};
}
// 添加流水线阶段
addStage(name, config) {
this.stages.set(name, {
id: name,
...config,
status: 'pending',
startTime: null,
endTime: null,
duration: 0,
logs: []
});
}
// 执行流水线
async executePipeline(context = {}) {
const pipelineId = this.generatePipelineId();
const pipeline = {
id: pipelineId,
status: 'running',
startTime: Date.now(),
endTime: null,
duration: 0,
context,
stages: Array.from(this.stages.values()),
artifacts: new Map(),
reports: new Map()
};
this.pipeline.set(pipelineId, pipeline);
try {
console.log(`开始执行流水线: ${pipelineId}`);
// 按顺序执行各个阶段
for (const stage of pipeline.stages) {
await this.executeStage(pipelineId, stage);
// 如果阶段失败且不允许失败,则停止流水线
if (stage.status === 'failed' && !stage.allowFailure) {
pipeline.status = 'failed';
break;
}
}
// 设置流水线最终状态
if (pipeline.status === 'running') {
const hasFailures = pipeline.stages.some(stage => stage.status === 'failed');
const hasWarnings = pipeline.stages.some(stage => stage.status === 'warning');
if (hasFailures) {
pipeline.status = 'failed';
} else if (hasWarnings) {
pipeline.status = 'warning';
} else {
pipeline.status = 'success';
}
}
pipeline.endTime = Date.now();
pipeline.duration = pipeline.endTime - pipeline.startTime;
// 发送通知
await this.sendNotification(pipeline);
console.log(`流水线执行完成: ${pipelineId}, 状态: ${pipeline.status}`);
return pipeline;
} catch (error) {
pipeline.status = 'error';
pipeline.endTime = Date.now();
pipeline.duration = pipeline.endTime - pipeline.startTime;
pipeline.error = error.message;
await this.sendNotification(pipeline);
console.error(`流水线执行失败: ${pipelineId}`, error);
throw error;
}
}
// 执行阶段
async executeStage(pipelineId, stage) {
stage.status = 'running';
stage.startTime = Date.now();
console.log(`开始执行阶段: ${stage.name}`);
try {
// 执行阶段命令
for (const command of stage.commands) {
const result = await this.executeCommand(command, stage);
stage.logs.push({
command,
output: result.stdout,
error: result.stderr,
exitCode: result.exitCode,
timestamp: Date.now()
});
if (result.exitCode !== 0) {
throw new Error(`命令执行失败: ${command}\n${result.stderr}`);
}
}
// 收集构件
if (stage.artifacts) {
await this.collectArtifacts(pipelineId, stage);
}
stage.status = 'success';
console.log(`阶段执行成功: ${stage.name}`);
} catch (error) {
stage.status = 'failed';
stage.error = error.message;
console.error(`阶段执行失败: ${stage.name}`, error);
// 重试机制
if (stage.retries > 0) {
stage.retries--;
console.log(`重试阶段: ${stage.name}, 剩余重试次数: ${stage.retries}`);
await new Promise(resolve => setTimeout(resolve, 5000)); // 等待5秒后重试
return this.executeStage(pipelineId, stage);
}
} finally {
stage.endTime = Date.now();
stage.duration = stage.endTime - stage.startTime;
}
}
// 执行命令
async executeCommand(command, stage) {
const { spawn } = require('child_process');
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
child.kill('SIGKILL');
reject(new Error(`命令执行超时: ${command}`));
}, stage.timeout * 1000);
const child = spawn('sh', ['-c', command], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, ...stage.environment }
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (exitCode) => {
clearTimeout(timeout);
resolve({ stdout, stderr, exitCode });
});
child.on('error', (error) => {
clearTimeout(timeout);
reject(error);
});
});
}
// 收集构件
async collectArtifacts(pipelineId, stage) {
const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');
const pipeline = this.pipeline.get(pipelineId);
const artifactsDir = path.join(process.cwd(), '.artifacts', pipelineId, stage.id);
await fs.ensureDir(artifactsDir);
for (const [type, patterns] of Object.entries(stage.artifacts)) {
if (type === 'expire') continue;
const files = [];
for (const pattern of patterns) {
const matches = glob.sync(pattern, { cwd: process.cwd() });
files.push(...matches);
}
for (const file of files) {
const srcPath = path.join(process.cwd(), file);
const destPath = path.join(artifactsDir, type, file);
await fs.ensureDir(path.dirname(destPath));
await fs.copy(srcPath, destPath);
}
pipeline.artifacts.set(`${stage.id}-${type}`, {
type,
files,
path: path.join(artifactsDir, type),
expire: stage.artifacts.expire || '7 days'
});
}
}
// 发送通知
async sendNotification(pipeline) {
if (!this.options.enableNotification) {
return;
}
const context = {
project: pipeline.context.project || 'Unknown Project',
buildNumber: pipeline.id,
branch: pipeline.context.branch || 'Unknown Branch',
commit: pipeline.context.commit || 'Unknown Commit',
buildUrl: pipeline.context.buildUrl || '#',
status: pipeline.status,
duration: this.formatDuration(pipeline.duration),
error: pipeline.error || ''
};
// 发送Slack通知
if (this.notifications.slack && this.notifications.slack.webhook) {
await this.sendSlackNotification(context);
}
// 发送邮件通知
if (this.notifications.email && this.notifications.email.smtp.host) {
await this.sendEmailNotification(context);
}
// 发送企业微信通知
if (this.notifications.wechat && this.notifications.wechat.webhook) {
await this.sendWechatNotification(context);
}
}
// 发送Slack通知
async sendSlackNotification(context) {
const axios = require('axios');
const template = this.notifications.slack.templates[context.status] ||
this.notifications.slack.templates.success;
const payload = {
channel: this.notifications.slack.channel,
username: this.notifications.slack.username,
icon_emoji: this.notifications.slack.iconEmoji,
attachments: [{
color: template.color,
title: this.interpolateTemplate(template.title, context),
text: this.interpolateTemplate(template.message, context),
fields: [
{ title: '项目', value: context.project, short: true },
{ title: '分支', value: context.branch, short: true },
{ title: '构建号', value: context.buildNumber, short: true },
{ title: '耗时', value: context.duration, short: true }
],
actions: [{
type: 'button',
text: '查看详情',
url: context.buildUrl
}]
}]
};
try {
await axios.post(this.notifications.slack.webhook, payload);
console.log('Slack通知发送成功');
} catch (error) {
console.error('Slack通知发送失败:', error);
}
}
// 发送邮件通知
async sendEmailNotification(context) {
const nodemailer = require('nodemailer');
const template = this.notifications.email.templates[context.status] ||
this.notifications.email.templates.success;
const transporter = nodemailer.createTransporter(this.notifications.email.smtp);
const mailOptions = {
from: this.notifications.email.from,
to: this.notifications.email.to,
subject: this.interpolateTemplate(template.subject, context),
html: this.interpolateTemplate(template.html, context)
};
try {
await transporter.sendMail(mailOptions);
console.log('邮件通知发送成功');
} catch (error) {
console.error('邮件通知发送失败:', error);
}
}
// 发送企业微信通知
async sendWechatNotification(context) {
const axios = require('axios');
const template = this.notifications.wechat.templates[context.status] ||
this.notifications.wechat.templates.success;
const payload = {
msgtype: template.msgtype,
[template.msgtype]: {
content: this.interpolateTemplate(template[template.msgtype].content, context)
}
};
try {
await axios.post(this.notifications.wechat.webhook, payload);
console.log('企业微信通知发送成功');
} catch (error) {
console.error('企业微信通知发送失败:', error);
}
}
// 模板插值
interpolateTemplate(template, context) {
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return context[key] || match;
});
}
// 格式化持续时间
formatDuration(ms) {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
if (hours > 0) {
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
} else if (minutes > 0) {
return `${minutes}m ${seconds % 60}s`;
} else {
return `${seconds}s`;
}
}
// 生成流水线ID
generatePipelineId() {
return `pipeline-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
// 获取流水线状态
getPipelineStatus(pipelineId) {
return this.pipeline.get(pipelineId);
}
// 获取所有流水线
getAllPipelines() {
return Array.from(this.pipeline.values());
}
// 清理过期构件
async cleanupArtifacts() {
const fs = require('fs-extra');
const path = require('path');
const artifactsDir = path.join(process.cwd(), '.artifacts');
if (!await fs.pathExists(artifactsDir)) {
return;
}
const pipelines = await fs.readdir(artifactsDir);
for (const pipelineId of pipelines) {
const pipelinePath = path.join(artifactsDir, pipelineId);
const stats = await fs.stat(pipelinePath);
const age = Date.now() - stats.mtime.getTime();
// 删除7天前的构件
if (age > 7 * 24 * 60 * 60 * 1000) {
await fs.remove(pipelinePath);
console.log(`清理过期构件: ${pipelineId}`);
}
}
}
}
2.2 部署管理器
javascript
// 部署管理器
class DeploymentManager {
constructor(options = {}) {
this.options = {
strategy: 'rolling', // rolling, blue-green, canary
environments: ['staging', 'production'],
enableRollback: true,
enableHealthCheck: true,
enableMonitoring: true,
...options
};
this.deployments = new Map();
this.environments = new Map();
this.strategies = new Map();
this.init();
}
async init() {
try {
// 初始化环境配置
this.initEnvironments();
// 初始化部署策略
this.initDeploymentStrategies();
// 初始化健康检查
this.initHealthChecks();
// 初始化监控
this.initMonitoring();
console.log('部署管理器初始化完成');
} catch (error) {
console.error('部署管理器初始化失败:', error);
}
}
// 初始化环境配置
initEnvironments() {
// 开发环境
this.addEnvironment('development', {
name: '开发环境',
url: 'http://localhost:3000',
variables: {
NODE_ENV: 'development',
API_URL: 'http://localhost:8080/api',
DEBUG: 'true'
},
deployment: {
type: 'local',
command: 'npm run dev'
}
});
// 测试环境
this.addEnvironment('testing', {
name: '测试环境',
url: 'https://test.example.com',
variables: {
NODE_ENV: 'testing',
API_URL: 'https://api-test.example.com',
DEBUG: 'false'
},
deployment: {
type: 'docker',
image: 'frontend-app:test',
registry: 'registry.example.com',
replicas: 1
}
});
// 预发布环境
this.addEnvironment('staging', {
name: '预发布环境',
url: 'https://staging.example.com',
variables: {
NODE_ENV: 'staging',
API_URL: 'https://api-staging.example.com',
DEBUG: 'false'
},
deployment: {
type: 'kubernetes',
namespace: 'staging',
replicas: 2,
resources: {
requests: { cpu: '100m', memory: '128Mi' },
limits: { cpu: '500m', memory: '512Mi' }
}
},
protection: {
required_reviewers: 1
}
});
// 生产环境
this.addEnvironment('production', {
name: '生产环境',
url: 'https://example.com',
variables: {
NODE_ENV: 'production',
API_URL: 'https://api.example.com',
DEBUG: 'false'
},
deployment: {
type: 'kubernetes',
namespace: 'production',
replicas: 5,
resources: {
requests: { cpu: '200m', memory: '256Mi' },
limits: { cpu: '1000m', memory: '1Gi' }
}
},
protection: {
required_reviewers: 2,
dismiss_stale_reviews: true,
require_code_owner_reviews: true
}
});
}
// 初始化部署策略
initDeploymentStrategies() {
// 滚动更新策略
this.addStrategy('rolling', {
name: '滚动更新',
description: '逐步替换旧版本实例',
config: {
maxUnavailable: '25%',
maxSurge: '25%',
progressDeadlineSeconds: 600
},
steps: [
{ name: '准备新版本', action: 'prepare' },
{ name: '逐步替换实例', action: 'rolling_update' },
{ name: '健康检查', action: 'health_check' },
{ name: '完成部署', action: 'finalize' }
]
});
// 蓝绿部署策略
this.addStrategy('blue-green', {
name: '蓝绿部署',
description: '并行运行两个版本,快速切换',
config: {
autoPromote: false,
scaleDownDelaySeconds: 30,
prePromotionAnalysis: true
},
steps: [
{ name: '部署绿色版本', action: 'deploy_green' },
{ name: '健康检查', action: 'health_check' },
{ name: '流量切换', action: 'switch_traffic' },
{ name: '清理蓝色版本', action: 'cleanup_blue' }
]
});
// 金丝雀部署策略
this.addStrategy('canary', {
name: '金丝雀部署',
description: '逐步增加新版本流量比例',
config: {
steps: [
{ setWeight: 10, pause: { duration: '5m' } },
{ setWeight: 20, pause: { duration: '5m' } },
{ setWeight: 50, pause: { duration: '10m' } },
{ setWeight: 100 }
],
analysis: {
templates: [
{
templateName: 'success-rate',
args: [{ name: 'service-name', value: 'frontend-app' }]
}
],
startingStep: 2,
interval: '5m',
count: 3,
successCondition: 'result[0] >= 0.95'
}
},
steps: [
{ name: '部署金丝雀版本', action: 'deploy_canary' },
{ name: '逐步增加流量', action: 'increase_traffic' },
{ name: '监控指标', action: 'monitor_metrics' },
{ name: '完成部署', action: 'promote_canary' }
]
});
}
// 初始化健康检查
initHealthChecks() {
this.healthChecks = {
// HTTP健康检查
http: {
path: '/health',
port: 3000,
scheme: 'HTTP',
initialDelaySeconds: 30,
periodSeconds: 10,
timeoutSeconds: 5,
successThreshold: 1,
failureThreshold: 3
},
// TCP健康检查
tcp: {
port: 3000,
initialDelaySeconds: 15,
periodSeconds: 20,
timeoutSeconds: 3,
successThreshold: 1,
failureThreshold: 3
},
// 自定义健康检查
custom: {
command: ['curl', '-f', 'http://localhost:3000/health'],
initialDelaySeconds: 30,
periodSeconds: 10,
timeoutSeconds: 5,
successThreshold: 1,
failureThreshold: 3
}
};
}
// 初始化监控
initMonitoring() {
this.monitoring = {
// Prometheus指标
prometheus: {
endpoint: '/metrics',
port: 9090,
metrics: [
'http_requests_total',
'http_request_duration_seconds',
'http_request_size_bytes',
'http_response_size_bytes',
'nodejs_heap_size_total_bytes',
'nodejs_heap_size_used_bytes'
]
},
// 日志收集
logging: {
driver: 'json-file',
options: {
'max-size': '10m',
'max-file': '3'
},
labels: {
service: 'frontend-app',
environment: '{{.Environment}}',
version: '{{.Version}}'
}
},
// 链路追踪
tracing: {
jaeger: {
endpoint: 'http://jaeger-collector:14268/api/traces',
sampler: {
type: 'probabilistic',
param: 0.1
}
}
}
};
}
// 添加环境
addEnvironment(name, config) {
this.environments.set(name, {
name,
...config,
status: 'inactive',
lastDeployment: null,
deploymentHistory: []
});
}
// 添加策略
addStrategy(name, config) {
this.strategies.set(name, config);
}
// 执行部署
async deploy(environment, version, options = {}) {
const deploymentId = this.generateDeploymentId();
const env = this.environments.get(environment);
if (!env) {
throw new Error(`环境不存在: ${environment}`);
}
const strategy = this.strategies.get(options.strategy || this.options.strategy);
if (!strategy) {
throw new Error(`部署策略不存在: ${options.strategy || this.options.strategy}`);
}
const deployment = {
id: deploymentId,
environment,
version,
strategy: strategy.name,
status: 'pending',
startTime: Date.now(),
endTime: null,
duration: 0,
steps: [],
logs: [],
rollbackVersion: env.lastDeployment?.version || null
};
this.deployments.set(deploymentId, deployment);
try {
console.log(`开始部署: ${deploymentId} (${environment} - ${version})`);
deployment.status = 'running';
// 执行部署步骤
for (const step of strategy.steps) {
await this.executeDeploymentStep(deploymentId, step);
}
deployment.status = 'success';
deployment.endTime = Date.now();
deployment.duration = deployment.endTime - deployment.startTime;
// 更新环境状态
env.status = 'active';
env.lastDeployment = {
id: deploymentId,
version,
timestamp: deployment.endTime
};
env.deploymentHistory.unshift(deployment);
// 保留最近10次部署记录
if (env.deploymentHistory.length > 10) {
env.deploymentHistory = env.deploymentHistory.slice(0, 10);
}
console.log(`部署成功: ${deploymentId}`);
return deployment;
} catch (error) {
deployment.status = 'failed';
deployment.endTime = Date.now();
deployment.duration = deployment.endTime - deployment.startTime;
deployment.error = error.message;
console.error(`部署失败: ${deploymentId}`, error);
// 自动回滚
if (this.options.enableRollback && deployment.rollbackVersion) {
console.log(`开始自动回滚到版本: ${deployment.rollbackVersion}`);
await this.rollback(environment, deployment.rollbackVersion);
}
throw error;
}
}
// 执行部署步骤
async executeDeploymentStep(deploymentId, step) {
const deployment = this.deployments.get(deploymentId);
const stepExecution = {
name: step.name,
action: step.action,
status: 'running',
startTime: Date.now(),
endTime: null,
duration: 0,
logs: []
};
deployment.steps.push(stepExecution);
try {
console.log(`执行部署步骤: ${step.name}`);
switch (step.action) {
case 'prepare':
await this.prepareDeployment(deployment);
break;
case 'rolling_update':
await this.executeRollingUpdate(deployment);
break;
case 'deploy_green':
await this.deployGreenVersion(deployment);
break;
case 'switch_traffic':
await this.switchTraffic(deployment);
break;
case 'deploy_canary':
await this.deployCanaryVersion(deployment);
break;
case 'increase_traffic':
await this.increaseCanaryTraffic(deployment);
break;
case 'health_check':
await this.performHealthCheck(deployment);
break;
case 'monitor_metrics':
await this.monitorMetrics(deployment);
break;
case 'finalize':
await this.finalizeDeployment(deployment);
break;
default:
throw new Error(`未知的部署动作: ${step.action}`);
}
stepExecution.status = 'success';
console.log(`部署步骤完成: ${step.name}`);
} catch (error) {
stepExecution.status = 'failed';
stepExecution.error = error.message;
console.error(`部署步骤失败: ${step.name}`, error);
throw error;
} finally {
stepExecution.endTime = Date.now();
stepExecution.duration = stepExecution.endTime - stepExecution.startTime;
}
}
// 准备部署
async prepareDeployment(deployment) {
// 验证版本
await this.validateVersion(deployment.version);
// 准备部署环境
await this.prepareEnvironment(deployment.environment);
// 下载构建产物
await this.downloadArtifacts(deployment.version);
}
// 执行滚动更新
async executeRollingUpdate(deployment) {
const env = this.environments.get(deployment.environment);
// 更新部署配置
const deploymentConfig = {
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: {
name: 'frontend-app',
namespace: env.deployment.namespace
},
spec: {
replicas: env.deployment.replicas,
strategy: {
type: 'RollingUpdate',
rollingUpdate: {
maxUnavailable: '25%',
maxSurge: '25%'
}
},
selector: {
matchLabels: {
app: 'frontend-app'
}
},
template: {
metadata: {
labels: {
app: 'frontend-app',
version: deployment.version
}
},
spec: {
containers: [{
name: 'frontend-app',
image: `frontend-app:${deployment.version}`,
ports: [{ containerPort: 3000 }],
env: Object.entries(env.variables).map(([key, value]) => ({
name: key,
value: value
})),
resources: env.deployment.resources,
livenessProbe: this.healthChecks.http,
readinessProbe: this.healthChecks.http
}]
}
}
}
};
// 应用部署配置
await this.applyKubernetesConfig(deploymentConfig);
// 等待部署完成
await this.waitForDeployment(env.deployment.namespace, 'frontend-app');
}
// 执行健康检查
async performHealthCheck(deployment) {
const env = this.environments.get(deployment.environment);
const maxRetries = 30;
const retryInterval = 10000; // 10秒
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(`${env.url}/health`);
if (response.ok) {
const healthData = await response.json();
console.log('健康检查通过:', healthData);
return;
}
} catch (error) {
console.log(`健康检查失败 (${i + 1}/${maxRetries}):`, error.message);
}
if (i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, retryInterval));
}
}
throw new Error('健康检查失败,部署可能存在问题');
}
// 回滚部署
async rollback(environment, version) {
console.log(`开始回滚环境 ${environment} 到版本 ${version}`);
return this.deploy(environment, version, {
strategy: 'rolling',
isRollback: true
});
}
// 生成部署ID
generateDeploymentId() {
return `deploy-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
// 获取部署状态
getDeploymentStatus(deploymentId) {
return this.deployments.get(deploymentId);
}
// 获取环境状态
getEnvironmentStatus(environment) {
return this.environments.get(environment);
}
// 获取所有部署
getAllDeployments() {
return Array.from(this.deployments.values());
}
}
3. 最佳实践与总结
3.1 分阶段实施策略
第一阶段:基础设施建设
- 建立现代化构建系统
- 配置基础的CI/CD流水线
- 实施代码质量检查
- 建立基本的部署流程
第二阶段:优化与增强
- 实施构建性能优化
- 增加安全检查和性能监控
- 完善部署策略(蓝绿、金丝雀)
- 建立完整的监控体系
第三阶段:高级特性
- 实施智能化部署决策
- 建立自动化回滚机制
- 集成高级安全扫描
- 实现全链路监控
3.2 组织与流程建议
团队协作
- 建立DevOps文化,促进开发与运维协作
- 制定清晰的代码审查流程
- 建立知识分享机制
- 定期进行技术回顾和改进
流程规范
- 制定Git工作流规范(如GitFlow)
- 建立分支保护策略
- 实施自动化测试要求
- 制定部署审批流程
3.3 核心价值与收益
开发效率提升
- 自动化构建减少手动操作
- 快速反馈机制提高开发速度
- 标准化流程降低学习成本
- 并行化处理提升构建速度
质量保障
- 自动化测试确保代码质量
- 代码审查机制防止问题引入
- 安全扫描识别潜在风险
- 性能监控保障用户体验
运维效率
- 自动化部署减少人为错误
- 标准化环境降低维护成本
- 监控告警及时发现问题
- 快速回滚机制降低故障影响
3.4 未来发展趋势
智能化运维
- AI驱动的异常检测
- 智能化容量规划
- 自动化故障恢复
- 预测性维护
云原生技术
- Serverless架构
- 容器化部署
- 微服务治理
- 服务网格
安全左移
- 开发阶段安全检查
- 供应链安全
- 零信任架构
- 合规自动化
结语
前端工程化是现代Web开发的重要基础,通过建立完善的构建系统、CI/CD流水线和部署策略,我们可以显著提升开发效率、保障代码质量、降低运维成本。本文提供的解决方案涵盖了从构建优化到部署管理的完整流程,希望能为你的前端工程化实践提供有价值的参考。
在实施过程中,建议采用渐进式的方法,从基础功能开始,逐步完善和优化整个工程化体系。同时,要注重团队协作和知识分享,确保工程化实践能够真正落地并发挥价值。
相关文章推荐:
- 前端微前端架构深度实践:从qiankun到Module Federation的技术演进
- 前端性能优化深度实践:从Core Web Vitals到用户体验提升的完整解决方案
- 前端Sentry监控体系总结:构建企业级可观测性平台