const path = require('path') const fs = require('fs') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const { VueLoaderPlugin } = require('vue-loader') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // 预加载插件用于资源预加载 const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin') // 资源压缩插件 const CompressionWebpackPlugin = require('compression-webpack-plugin') // 读取环境变量文件 function loadEnvFile(envPath) { if (fs.existsSync(envPath)) { const envContent = fs.readFileSync(envPath, 'utf8') const envVars = {} envContent.split('\n').forEach(line => { // 跳过注释和空行 const trimmedLine = line.trim() if (!trimmedLine || trimmedLine.startsWith('#')) return const equalIndex = trimmedLine.indexOf('=') if (equalIndex > 0) { const key = trimmedLine.substring(0, equalIndex).trim() const value = trimmedLine.substring(equalIndex + 1).trim() // 移除首尾的引号(单引号或双引号) const cleanValue = value.replace(/^["']|["']$/g, '') envVars[key] = cleanValue } }) return envVars } return {} } // 根据环境加载对应的 .env 文件 // 确保 NODE_ENV 有正确的值 const nodeEnv = process.env.NODE_ENV || 'development' const isDevelopment = process.env.NODE_ENV === 'development' const isProduction = process.env.NODE_ENV === 'production' let envVars = {} if (isProduction) { envVars = loadEnvFile('.env.production') } else if (isDevelopment) { envVars = loadEnvFile('.env.development') } // 合并到 process.env Object.assign(process.env, envVars) // 获取 public path const getPublicPath = () => { const routerBase = process.env.VUE_ROUTER_BASE || process.env.VUE_APP_ROUTER_BASE || '/' // 确保路径以斜杠结尾 const normalizedPath = routerBase.endsWith('/') ? routerBase : routerBase + '/' const publicPath = isProduction ? normalizedPath : '/' console.log('==========',process.env.NODE_ENV) console.log(`🔧 构建环境: ${process.env.NODE_ENV || 'development'}`) console.log(`📁 Public Path: ${publicPath}`) if (isProduction && (process.env.VUE_ROUTER_BASE || process.env.VUE_APP_ROUTER_BASE)) { console.log(`📖 从 .env.production 读取路由基础路径: ${routerBase}`) } return publicPath } // 验证并设置正确的 mode 值 const getMode = () => { const validModes = ['development', 'production', 'none'] if (validModes.includes(nodeEnv)) { return nodeEnv } // 如果 NODE_ENV 不是有效值,根据情况设置默认值 console.warn(`⚠️ NODE_ENV "${nodeEnv}" 不是有效的 webpack mode,默认使用 development`) return 'development' } module.exports = { mode: getMode(), // 添加这个配置来抑制特定警告 stats: { warnings: isDevelopment ? false : true, // 开发环境完全关闭警告,生产环境显示 warningsFilter: [ /export .* was not found in/, /Deprecation Warning/, /sass-lang\.com\/d\/import/, /Global built-in functions are deprecated/, /@import rules are deprecated/, /Invalid deprecation/, /Module Warning \(from \.\/node_modules\/sass-loader/, /Sass @import rules are deprecated/, /More info and automated migrator/, /downloadByUrl.*was not found/, /possible exports:/, /WARNING in/, /Module not found/, /Can't resolve/, /\[BABEL\] Note:/, /code generator has deoptimised/, /exceeds the max of/ ] }, // 缓存配置 - 大幅提升重新构建速度 cache: { type: 'filesystem', buildDependencies: { config: [__filename], }, cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack'), compression: 'gzip', profile: false, maxMemoryGenerations: isDevelopment ? 5 : Infinity, maxAge: 1000 * 60 * 60 * 24 * 7, // 7天 allowCollectingMemory: isDevelopment, }, entry: { main: path.resolve(__dirname, 'src/main.js') }, output: { path: path.resolve(__dirname, 'dist'), filename: isDevelopment ? 'js/[name].js' : 'js/[name].[contenthash:8].js', chunkFilename: isDevelopment ? 'js/[name].chunk.js' : 'js/[name].[contenthash:8].chunk.js', publicPath: getPublicPath(), clean: true, assetModuleFilename: 'assets/[name].[hash:8][ext]' }, resolve: { alias: { '@': path.resolve(__dirname, 'src'), '@components': path.resolve(__dirname, 'src/components'), '@views': path.resolve(__dirname, 'src/views'), '@utils': path.resolve(__dirname, 'src/utils'), '@lib': path.resolve(__dirname, 'src/components/lib'), '@assets': path.resolve(__dirname, 'src/assets'), '@api': path.resolve(__dirname, 'src/api'), '#': path.resolve(__dirname, 'public'), 'vue$': 'vue/dist/vue.esm.js', 'element-ui/lib/el-tooltip': 'element-ui/lib/tooltip', // Component Gallery 别名 '@component-gallery/assets': path.join(__dirname, 'src/components/common-assets'), '@component-gallery/utils': path.join(__dirname, 'src/components/common-inner-utils'), '@component-gallery/theme-chalk': path.join(__dirname, 'src/components/theme-chalk'), '~@component-gallery/theme-chalk': path.join(__dirname, 'src/components/theme-chalk'), "@component-gallery/build-event-bus-path": path.join(__dirname, 'src/components/build-event-bus-path/lib/main'), "@component-gallery/base-components": path.join(__dirname, 'src/components/common-base-components'), "@component-gallery/base-art-player": path.join(__dirname, 'src/components/common-base-art-player'), "@component-gallery/base-video-player": path.join(__dirname, 'src/components/common-base-component-video-player'), "@component-gallery/video-map-camera-position": path.join(__dirname, 'src/components/common-comp-video-map-camera-position/src/entry/CommonVideoMapCameraPosition.vue'), "@component-gallery/video-map-view": path.join(__dirname, 'src/components/common-comp-video-map-view/src/entry/CommonVideoMapView.vue'), "@component-gallery/horn-shout": path.join(__dirname, 'src/components/common-comp-horn-shout/src/entry/CommonHornShout.vue'), "@component-gallery/lib": path.join(__dirname, 'src/components/lib'), "@component-gallery/one-key-alarm": path.join(__dirname, 'src/components/common-comp-one-key-alarm/src/entry/CommonOneKeyAlarm.vue'), "@component-gallery/alarm-list": path.join(__dirname, 'src/components/common-comp-alarm-list'), "@component-gallery/emergency-events-inquiry": path.join(__dirname, 'src/components/common-comp-emergency-events-inquiry'), "@component-gallery/alarm-detail": path.join(__dirname, 'src/components/common-comp-alarm-detail/src/entry/CommonAlarmDetail.vue'), "@component-gallery/horn-tree": path.join(__dirname, 'src/components/common-comp-horn-tree/src/entry/CommonHornTree.vue'), "@component-gallery/horn-broadcast": path.join(__dirname, 'src/components/common-comp-horn-broadcast/src/entry/CommonHornBroadcast.vue'), "@component-gallery/map": path.join(__dirname, 'src/components/common-comp-map/src/entry/CommonMap.vue'), "@component-gallery/tool-box": path.join(__dirname, 'src/components/common-comp-tool-box/src/entry/CommonToolBox.vue'), "@component-gallery/kanzheli": path.join(__dirname, 'src/components/common-comp-kanzheli/src/entry/CommonKanzheli.vue'), "@component-gallery/alarm-filte": path.join(__dirname, 'src/components/common-comp-alarm-filte/src/entry/CommonAlarmFilte.vue'), "@component-gallery/camera-choose": path.join(__dirname, 'src/components/common-comp-camera-choose/src/CommonCameraChoose.vue'), "@component-gallery/coordinate-picking": path.join(__dirname, 'src/components/common-comp-coordinate-picking/src/entry/CommonCoordinatePicking.vue'), "@component-gallery/coordinate-positioning": path.join(__dirname, 'src/components/common-comp-coordinate-positioning/src/entry/CommonCoordinatePositioning.vue'), "@component-gallery/layers-control": path.join(__dirname, 'src/components/common-comp-layers-control/src/entry/CommonLayersControl.vue'), "@component-gallery/slope-aspect-analysis": path.join(__dirname, 'src/components/common-comp-slope-aspect-analysis/src/CommonSlopeAspectAnalysis.vue'), "@component-gallery/spot-detail": path.join(__dirname, 'src/components/common-comp-spot-detail/src/entry/SpotDetail.vue'), "@component-gallery/tool-camera-filte": path.join(__dirname, 'src/components/common-comp-tool-camera-filte/src/CommonCameraFilte.vue'), "@component-gallery/tool-config-item": path.join(__dirname, 'src/components/common-comp-tool-config-item/src/entry/CommonToolConfigItem.vue'), "@component-gallery/tools-plot": path.join(__dirname, 'src/components/common-comp-tools-plot/src/CommonToolsPlot.vue'), "@component-gallery/viewshed-analysis": path.join(__dirname, 'src/components/common-comp-viewshed-analysis/src/CommonViewshedAnalysis.vue'), "@component-gallery/tool-measure": path.join(__dirname, 'src/components/common-comp-tool-measure/src/entry/CommonCompToolMeasure.vue'), "@component-gallery/tool-section-analyse": path.join(__dirname, 'src/components/common-comp-tool-section-analyse/src/entry/CommonCompToolSectionAnalyse.vue'), "@component-gallery/layer-style-config": path.join(__dirname, 'src/components/common-comp-layer-style-config/src/entry/CommonLayerStyleConfig.vue'), "@component-gallery/azimuthal-measurement": path.join(__dirname, 'src/components/common-comp-azimuthal-measurement/src/entry/AzimuthalMeasurement.vue'), "@component-gallery/terrain-dig": path.join(__dirname, 'src/components/common-comp-terrain-dig/src/entry/TerrainDig.vue'), "@component-gallery/lnglat-select-point": path.join(__dirname, 'src/components/common-comp-lnglat-select-point/src/entry/CommonLnglatSelectPoint.vue'), "@component-gallery/tool-spot": path.join(__dirname, 'src/components/common-comp-tool-spot/src/entry/SpotSearch.vue'), "@component-gallery/dialog-tool": path.join(__dirname, 'src/components/common-comp-dialog-tool/src/entry/CommonDialogTool.vue'), "@component-gallery/layers-tool": path.join(__dirname, 'src/components/common-comp-layers-tool/src/entry/CommonLayersTool.vue'), "@component-gallery/horn-circle-selection": path.join(__dirname, 'src/components/common-comp-horn-circle-selection/src/entry/CommonHornCircleSelection.vue'), "@component-gallery/around-analysis": path.join(__dirname, 'src/components/common-comp-around-analysis/src/CommonAroundAnalysis.vue'), "@component-gallery/video-inspection-polygon": path.join(__dirname, 'src/components/common-comp-video-inspection-polygon/src/entry/CommonVideoInspectionPolygon.vue'), "@component-gallery/footer": path.join(__dirname, 'src/components/common-comp-footer/src/entry/CommonFooter.vue'), "@component-gallery/multimodal-assistant": path.join(__dirname, 'src/components/common-comp-multimodal-assistant/src/entry/CommonMultimodalAssistant.vue'), "@component-gallery/alarm-detail-large": path.join(__dirname, 'src/components/common-comp-alarm-detail-large/src/entry/AlarmDetailLarge.vue'), "@component-gallery/alarm-transfer-event": path.join(__dirname, 'src/components/common-comp-alarm-transfer-event/src/entry/AlarmTransferEvent.vue'), "@component-gallery/tree": path.join(__dirname, 'src/components/common-comp-tree/src/entry/CommonTree.vue'), "@component-gallery/iot-tree": path.join(__dirname, 'src/components/common-comp-iot-tree/src/entry/CommonIotTree.vue'), "@component-gallery/radar-tree": path.join(__dirname, 'src/components/common-comp-radar-tree/src/entry/CommonRadarTree.vue'), "@component-gallery/uav-tree": path.join(__dirname, 'src/components/common-comp-uav-tree/src/entry/CommonUavTree.vue'), "@component-gallery/source-tree": path.join(__dirname, 'src/components/common-comp-source-tree/src/entry/CommonSourceTree.vue'), "@component-gallery/grid-tree": path.join(__dirname, 'src/components/common-comp-grid-tree/src/entry/CommonGridTree.vue'), "@component-gallery/grid-operator-tree": path.join(__dirname, 'src/components/common-comp-grid-operator-tree/src/entry/CommonGridOperatorTree.vue'), "@component-gallery/track-popup": path.join(__dirname, 'src/components/common-comp-track-popup/src/entry/TrackPopup.vue'), "@component-gallery/tree-recorder": path.join(__dirname, 'src/components/common-comp-tree-recorder/src/entry/CommonTreeRecorder.vue'), "@component-gallery/multiple-device-search": path.join(__dirname, 'src/components/common-comp-multiple-device-search/src/entry/CommonMultipleDeviceSearch.vue'), "@component-gallery/camera-circle-selection": path.join(__dirname, 'src/components/common-comp-camera-circle-selection/src/CommonCameraCircleSelection.vue'), "@component-gallery/algorithm-configuration": path.join(__dirname, 'src/components/common-comp-algorithm-configuration/src/entry/AlgorithmConfiguration.vue'), "@component-gallery/algorithm-configuration-dialog": path.join(__dirname, 'src/components/common-comp-algorithm-configuration-dialog/src/entry/index.vue'), "@component-gallery/satellite-sense": path.join(__dirname, 'src/components/common-comp-tool-satellite-sense/src/entry/CommonSatelliteSense.vue'), "@component-gallery/tool-space": path.join(__dirname, 'src/components/common-comp-tool-space/src/entry/SpotSpace.vue'), "@component-gallery/tool-compound": path.join(__dirname, 'src/components/common-comp-tool-compound/src/entry/SpotCompound.vue'), "@component-gallery/tool-complex": path.join(__dirname, 'src/components/common-comp-tool-complex/src/entry/ToolComplex.vue'), "@component-gallery/tool-swiper": path.join(__dirname, 'src/components/common-comp-tool-swiper/src/entry/ToolSwiper.vue'), "@component-gallery/sense-time-line": path.join(__dirname, 'src/components/common-comp-sense-time-line/src/entry/SenseTimeLine.vue'), "@component-gallery/weather-small": path.join(__dirname, 'src/components/common-comp-weather-small/src/entry/CommonWeatherSmall.vue'), "@component-gallery/search-map": path.join(__dirname, 'src/components/common-comp-search-map/src/entry/CommonSearchMap.vue'), "@component-gallery/inspection-task-list": path.join(__dirname, 'src/components/common-comp-inspection-task-list/src/entry/CommonInspectionTaskList.vue'), "@component-gallery/uav-fly-manage": path.join(__dirname, 'src/components/common-comp-uav-fly-manage/src/entry/UavFlyManage.vue'), }, extensions: ['.js', '.vue', '.json', '.ts', '.tsx'], // 解决 webpack 5 严格模块解析问题 fullySpecified: false, fallback: { path: require.resolve('path-browserify'), crypto: false, stream: false, fs: false, net: false, tls: false, buffer: require.resolve('buffer'), util: require.resolve('util') } }, module: { rules: [ // 处理第三方库的严格模块解析问题 { test: /\.m?js$/, resolve: { fullySpecified: false } }, // 处理 @ct 相关的 ES6 语法问题 { test: /\.m?js$/, include: [ // 指定需要转译的 node_modules 包 /node_modules\/@ct/, /node_modules\/marked/, /node_modules\/@ct\/ais-js-kit/ ], use: { loader: 'babel-loader', options: { presets: [['@babel/preset-env', { modules: false }]], plugins: [ '@babel/plugin-proposal-nullish-coalescing-operator', '@babel/plugin-proposal-optional-chaining', ], }, }, }, // Vue 文件处理 { test: /\.vue$/, loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false } } }, // JavaScript 文件处理 { test: /\.js$/, exclude: /node_modules(?!\/(@ct|marked))/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], plugins: [ "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-class-properties" ], // 启用缓存 cacheDirectory: true, cacheCompression: false // plugins: [ // [ // 'babel-plugin-component', // { // libraryName: 'element-ui', // libraryDirectory: 'lib', // styleLibraryName: 'theme-chalk' // }, // 'element-ui' // ] // ] } } }, // CSS 文件处理 { test: /\.css$/, use: [ isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader', { loader: 'css-loader', options: { sourceMap: true, // 处理CSS中的URL引用 url: { filter: (url, resourcePath) => { // 对于第三方包中的静态资源路径,返回false让webpack跳过处理 // 我们将通过CopyPlugin来复制这些资源 if (resourcePath.includes('node_modules/@ct/component-gallery-theme-chalk') && url.startsWith('./static/')) { return false; } return true; }, }, } }, { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ require('postcss-pxtorem')({ rootValue: 100, propList: ['*'], // exclude: /node_modules/i exclude(file) { if (file.indexOf("component-gallery-theme-chalk") !== -1 || file.indexOf("common-base-component-video-player-listener") !== -1) { return true; } else { return false; } } }) ] } } } ] }, // SCSS 文件处理 { test: /\.scss$/, use: [ isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader', { loader: 'css-loader', options: { sourceMap: true, } }, // { // loader: 'postcss-loader', // options: { // sourceMap: true, // postcssOptions: { // plugins: [ // require('postcss-pxtorem')({ // rootValue: 16, // propList: ['*'], // exclude: /node_modules/i // }) // ] // } // } // }, { loader: 'resolve-url-loader', options: { sourceMap: true, } }, { loader: 'sass-loader', options: { sourceMap: true, sassOptions: { // 抑制 Sass 弃用警告 quietDeps: true, verbose: false, // 使用通配符抑制所有弃用警告 silenceDeprecations: ['*'] } }, }, ] }, // 图片文件处理 { test: /\.(png|jpe?g|gif|svg|webp|tiff?)$/i, type: 'asset', parser: { dataUrlCondition: { maxSize: 4 * 1024 // 4KB以下的图片转为base64,减少小图片请求 } }, generator: { filename: 'img/[name].[hash:8][ext]' } }, // 字体文件处理 { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset', parser: { dataUrlCondition: { maxSize: 8 * 1024 } }, generator: { filename: 'font/[name].[hash:8][ext]' } }, // 媒体文件处理 { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/i, type: 'asset', generator: { filename: 'media/[name].[hash:8][ext]' } } ] }, plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin({ template: path.resolve(__dirname, 'public/index.html'), filename: 'index.html', inject: true, templateParameters: { BASE_URL: getPublicPath(), VUE_APP_ROUTER_BASE: getPublicPath() }, publicPath: getPublicPath(), // 优化资源加载优先级 scriptLoading: 'defer', minify: isProduction ? { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true } : false }), new webpack.DefinePlugin({ // 创建一个干净的环境变量对象,避免冲突 'process.env': JSON.stringify({ NODE_ENV: process.env.NODE_ENV || 'development', VUE_APP_ROUTER_BASE: getPublicPath(), VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE || process.env.VUE_APP_ROUTER_BASE || '/', // 确保VUE_ROUTER_BASE被传递 // 只包含需要的环境变量,避免泄露敏感信息 ...Object.keys(process.env) .filter(key => key.startsWith('VUE_APP_') || key === 'NODE_ENV' || key === 'VUE_ROUTER_BASE') .reduce((env, key) => { env[key] = process.env[key]; return env; }, {}) }), 'process.browser': true, 'process.version': JSON.stringify(process.version), 'process.platform': JSON.stringify(process.platform), 'process.type': JSON.stringify('renderer') }), new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, 'public'), to: path.resolve(__dirname, 'dist'), globOptions: { ignore: ['**/index.html'] } }, // 复制本地assets到static目录,以匹配第三方CSS包中的相对路径引用 { from: path.resolve(__dirname, 'src/components/common-assets/image'), to: path.resolve(__dirname, 'dist/static/image'), globOptions: { ignore: ['**/.DS_Store'] } } ] }) ].concat(isProduction ? [ new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:8].css', chunkFilename: 'css/[name].[contenthash:8].css', ignoreOrder: true // 忽略 CSS 顺序警告 }), // 预加载关键资源 - 只在生产环境使用,排除图片资源 new PreloadWebpackPlugin({ rel: 'preload', as(entry) { if (/\.css$/.test(entry)) return 'style'; if (/\.woff2?$/.test(entry)) return 'font'; // 移除图片的 preload,让它们按需加载 // if (/\.(png|jpg|jpeg|gif|svg)$/.test(entry)) return 'image'; return 'script'; }, include: ['vue-vendor', 'element-ui', 'main'], // 预加载关键chunk fileBlacklist: [ /\.map$/, /hot-update\.js$/, /\.(png|jpg|jpeg|gif|svg|webp|ico)$/ // 排除所有图片文件 ] }), // 预获取其他资源 - 只在生产环境使用,排除图片资源 new PreloadWebpackPlugin({ rel: 'prefetch', include: 'asyncChunks', // 预获取异步chunk fileBlacklist: [ /\.map$/, /hot-update\.js$/, /\.(png|jpg|jpeg|gif|svg|webp|ico)$/ // 排除所有图片文件 ] }), // Gzip压缩 - 提高加载速度,减少带宽消耗 new CompressionWebpackPlugin({ algorithm: 'gzip', test: /\.(js|css|html|svg)$/, threshold: 8192, // 8KB以上的文件才压缩 minRatio: 0.8, deleteOriginalAssets: false // 保留原文件 }), // Brotli压缩 - 比Gzip更高的压缩率 new CompressionWebpackPlugin({ algorithm: 'brotliCompress', test: /\.(js|css|html|svg)$/, compressionOptions: { level: 11, }, threshold: 8192, minRatio: 0.8, filename: '[path][base].br', deleteOriginalAssets: false }) ] : []), optimization: { minimize: isProduction, minimizer: [ new TerserPlugin({ terserOptions: { compress: { drop_console: isProduction, drop_debugger: isProduction } }, parallel: 2 // 减少并行处理数量,降低内存使用 }), // 添加CSS压缩器 new CssMinimizerPlugin({ parallel: 2, minimizerOptions: { preset: [ 'default', { discardComments: { removeAll: true }, }, ], }, }) ], splitChunks: { chunks: 'all', maxInitialRequests: 6, // 减少初始请求数量 maxAsyncRequests: 8, // 减少异步请求数量 minSize: 100000, // 100KB 最小包大小,避免过小的包 maxSize: 8000000, // 8MB 最大包大小,允许更大的包 cacheGroups: { // Vue 生态系统合并打包 vue: { test: /[\\/]node_modules[\\/](vue|vue-router|vuex|@vue)[\\/]/, name: 'vue-vendor', chunks: 'all', priority: 30, enforce: true // 强制打包,不受大小限制 }, // Element UI 完整生态 elementUI: { test: /[\\/]node_modules[\\/](element-ui|element-theme-|@element)[\\/]/, name: 'element-ui', chunks: 'all', priority: 25, enforce: true }, // 大型第三方库合并 largeLibs: { test: /[\\/]node_modules[\\/](echarts|artplayer|hls\.js|mpegts\.js|fabric|@ct\/ct_map_ol|@ct\/icons-v2)[\\/]/, name: 'large-libs', chunks: 'all', priority: 23, enforce: true }, // 工具库合并 utils: { test: /[\\/]node_modules[\\/](lodash|moment|dayjs|axios|crypto-js|uuid|classnames|immer)[\\/]/, name: 'utils', chunks: 'all', priority: 21, enforce: true }, // CT组件库合并 ctComponents: { test: /[\\/]node_modules[\\/]@ct[\\/]/, name: 'ct-components', chunks: 'all', priority: 20, enforce: true }, // 其他第三方库 vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all', priority: 10, minSize: 200000, // 200KB 最小大小 enforce: true }, // 公共代码 - 更宽松的条件 common: { name: 'common', minChunks: 2, chunks: 'all', priority: 5, reuseExistingChunk: true, minSize: 50000, // 50KB maxSize: 3000000 // 3MB } } } }, devServer: { host: '0.0.0.0', historyApiFallback: true, static: [ { directory: path.join(__dirname, 'public'), publicPath: getPublicPath() }, // 添加静态资源服务,支持第三方CSS包的相对路径访问 { directory: path.join(__dirname, 'src/components/common-assets/image'), publicPath: '/static/image' } ], // open: true, compress: true, hot: true, // 只使用HMR,不用liveReload port: 3000, allowedHosts: 'all', // 开发服务器性能优化 devMiddleware: { writeToDisk: false, // 不写入磁盘,提高速度 stats: 'errors-only', // 只显示错误 }, client: { logging: 'error', // 只显示错误级别的日志 overlay: { errors: true, warnings: false, // 不显示警告 runtimeErrors: false // 减少运行时错误覆盖 }, progress: false, // 关闭进度条减少内存 webSocketTransport: 'ws' // 使用更高效的WebSocket传输 }, proxy: [ { context: ['/api'], target: 'http://localhost:3000', changeOrigin: true, pathRewrite: { '^/api': '' } } ] }, // 文件监听优化 - 减少内存使用 watchOptions: { ignored: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/.webpack/**', '**/.cache/**'], // 使用glob模式 aggregateTimeout: 500, // 增加延迟,减少编译频率 poll: false, // 使用原生文件监听,更高效 followSymlinks: false // 不跟随符号链接 }, devtool: isDevelopment ? 'eval-cheap-module-source-map' : false, performance: { hints: false, maxAssetSize: 2000000, maxEntrypointSize: 2000000 }, // 内存优化 cache: { type: 'filesystem', cacheDirectory: path.resolve(__dirname, '.webpack'), compression: 'gzip', // 限制缓存大小,防止内存溢出 maxMemoryGenerations: isDevelopment ? 1 : 0, // 内存管理配置 memoryCacheUnaffected: true, buildDependencies: { config: [__filename] } } }