Files
vf_react/craco.config.js
2025-11-11 19:00:02 +08:00

292 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const path = require('path');
const webpack = require('webpack');
const { BundleAnalyzerPlugin } = process.env.ANALYZE ? require('webpack-bundle-analyzer') : { BundleAnalyzerPlugin: null };
// 检查是否为 Mock 模式(与 src/utils/apiConfig.js 保持一致)
const isMockMode = () => process.env.REACT_APP_ENABLE_MOCK === 'true';
module.exports = {
webpack: {
configure: (webpackConfig, { env, paths }) => {
// ============== 持久化缓存配置 ==============
// 大幅提升二次构建速度(可提升 50-80%
webpackConfig.cache = {
type: 'filesystem',
cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack'),
buildDependencies: {
config: [__filename],
},
// 增加缓存有效性检查
name: env === 'production' ? 'production' : 'development',
compression: env === 'production' ? 'gzip' : false,
};
// ============== 生产环境优化 ==============
if (env === 'production') {
// 高级代码分割策略
webpackConfig.optimization = {
...webpackConfig.optimization,
splitChunks: {
chunks: 'all',
maxInitialRequests: 30,
minSize: 20000,
maxSize: 512000, // 限制单个 chunk 最大大小512KB与 performance.maxAssetSize 一致)
cacheGroups: {
// React 核心库单独分离
react: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
name: 'react-vendor',
priority: 30,
reuseExistingChunk: true,
},
// 大型图表库分离echarts, d3, apexcharts 等)
charts: {
test: /[\\/]node_modules[\\/](echarts|echarts-for-react|apexcharts|react-apexcharts|recharts|d3|d3-.*)[\\/]/,
name: 'charts-lib',
priority: 25,
reuseExistingChunk: true,
},
// Chakra UI 框架
chakraUI: {
test: /[\\/]node_modules[\\/](@chakra-ui|@emotion)[\\/]/,
name: 'chakra-ui',
priority: 23, // 从 22 改为 23避免与 antd 优先级冲突
reuseExistingChunk: true,
},
// Ant Design
antd: {
test: /[\\/]node_modules[\\/](antd|@ant-design)[\\/]/,
name: 'antd-lib',
priority: 22,
reuseExistingChunk: true,
},
// 3D 库three.js
three: {
test: /[\\/]node_modules[\\/](three|@react-three)[\\/]/,
name: 'three-lib',
priority: 20,
reuseExistingChunk: true,
},
// 日期/日历库
calendar: {
test: /[\\/]node_modules[\\/](moment|date-fns|@fullcalendar|react-big-calendar)[\\/]/,
name: 'calendar-lib',
priority: 18,
reuseExistingChunk: true,
},
// 其他第三方库
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
reuseExistingChunk: true,
},
// 公共代码
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true,
name: 'common',
},
},
},
// 优化运行时代码
runtimeChunk: 'single',
// 使用确定性的模块 ID
moduleIds: 'deterministic',
// 最小化配置
minimize: true,
};
// 生产环境禁用 source map 以加快构建(可节省 40-60% 时间)
webpackConfig.devtool = false;
} else {
// 开发环境使用更快的 source map
webpackConfig.devtool = 'eval-cheap-module-source-map';
}
// ============== 模块解析优化 ==============
webpackConfig.resolve = {
...webpackConfig.resolve,
alias: {
...webpackConfig.resolve.alias,
// 根目录别名
'@': path.resolve(__dirname, 'src'),
// 功能模块别名(按字母顺序)
'@assets': path.resolve(__dirname, 'src/assets'),
'@components': path.resolve(__dirname, 'src/components'),
'@constants': path.resolve(__dirname, 'src/constants'),
'@contexts': path.resolve(__dirname, 'src/contexts'),
'@data': path.resolve(__dirname, 'src/data'),
'@hooks': path.resolve(__dirname, 'src/hooks'),
'@layouts': path.resolve(__dirname, 'src/layouts'),
'@lib': path.resolve(__dirname, 'src/lib'),
'@mocks': path.resolve(__dirname, 'src/mocks'),
'@providers': path.resolve(__dirname, 'src/providers'),
'@routes': path.resolve(__dirname, 'src/routes'),
'@services': path.resolve(__dirname, 'src/services'),
'@store': path.resolve(__dirname, 'src/store'),
'@styles': path.resolve(__dirname, 'src/styles'),
'@theme': path.resolve(__dirname, 'src/theme'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@variables': path.resolve(__dirname, 'src/variables'),
'@views': path.resolve(__dirname, 'src/views'),
},
// 减少文件扩展名搜索
extensions: ['.js', '.jsx', '.json'],
// 优化模块查找路径
modules: [
path.resolve(__dirname, 'src'),
'node_modules'
],
// 优化符号链接解析
symlinks: false,
};
// ============== 插件优化 ==============
// 移除 ESLint 插件以提升构建速度(可提升 20-30%
webpackConfig.plugins = webpackConfig.plugins.filter(
plugin => plugin.constructor.name !== 'ESLintWebpackPlugin'
);
// 添加打包分析工具(通过 ANALYZE=true 启用)
if (env === 'production' && process.env.ANALYZE && BundleAnalyzerPlugin) {
webpackConfig.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: true,
reportFilename: 'bundle-report.html',
})
);
}
// 忽略 moment 的语言包(如果项目使用了 moment
webpackConfig.plugins.push(
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
})
);
// ============== Loader 优化 ==============
const babelLoaderRule = webpackConfig.module.rules.find(
rule => rule.oneOf
);
if (babelLoaderRule && babelLoaderRule.oneOf) {
babelLoaderRule.oneOf.forEach(rule => {
// 优化 Babel Loader
if (rule.loader && rule.loader.includes('babel-loader')) {
rule.options = {
...rule.options,
cacheDirectory: true,
cacheCompression: false,
compact: env === 'production',
};
// 限制 Babel 处理范围
rule.include = path.resolve(__dirname, 'src');
rule.exclude = /node_modules/;
}
// 优化 CSS Loader
if (rule.use && Array.isArray(rule.use)) {
rule.use.forEach(loader => {
if (loader.loader && loader.loader.includes('css-loader') && loader.options) {
loader.options.sourceMap = env !== 'production';
}
});
}
});
}
// ============== 性能提示配置 ==============
webpackConfig.performance = {
hints: env === 'production' ? 'warning' : false,
maxEntrypointSize: 512000, // 512KB
maxAssetSize: 512000,
};
return webpackConfig;
},
},
// ============== Babel 配置优化 ==============
babel: {
plugins: [
// 运行时辅助函数复用
['@babel/plugin-transform-runtime', {
regenerator: true,
useESModules: true,
}],
],
loaderOptions: {
cacheDirectory: true,
cacheCompression: false,
},
},
// ============== 开发服务器配置 ==============
devServer: {
hot: true,
port: 3000,
compress: true,
client: {
overlay: false,
progress: true,
},
// 优化开发服务器性能
devMiddleware: {
writeToDisk: false,
},
// 调试日志
onListening: (devServer) => {
console.log(`[CRACO] Mock Mode: ${isMockMode() ? 'Enabled ✅' : 'Disabled ❌'}`);
console.log(`[CRACO] Proxy: ${isMockMode() ? 'Disabled (MSW intercepts)' : 'Enabled (forwarding to backend)'}`);
},
// 代理配置:将 /api 请求代理到后端服务器
// 注意Mock 模式下禁用 proxy让 MSW 拦截请求
...(isMockMode() ? {} : {
proxy: {
'/api': {
target: 'http://49.232.185.254:5001',
changeOrigin: true,
secure: false,
logLevel: 'debug',
},
'/concept-api': {
target: 'http://49.232.185.254:6801',
changeOrigin: true,
secure: false,
logLevel: 'debug',
pathRewrite: { '^/concept-api': '' },
},
'/bytedesk-api': {
target: 'https://valuefrontier.cn', // 统一使用生产环境 Nginx 代理
changeOrigin: true,
secure: false, // 开发环境禁用 HTTPS 严格验证
logLevel: 'debug',
ws: true, // 支持 WebSocket
// 不使用 pathRewrite保留 /bytedesk-api 前缀,让生产 Nginx 处理
},
'/chat': {
target: 'https://valuefrontier.cn/bytedesk-api', // Bytedesk iframe 内部的资源SDK、CSS等
changeOrigin: true,
secure: false, // 开发环境禁用 HTTPS 严格验证
logLevel: 'debug',
// 保留 /chat 路径
},
'/config': {
target: 'https://valuefrontier.cn/bytedesk-api', // Bytedesk 配置接口
changeOrigin: true,
secure: false, // 开发环境禁用 HTTPS 严格验证
logLevel: 'debug',
// 保留 /config 路径
},
},
}),
},
};