vue.config.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /**
  2. * 构建配置(真机运行启动即 OOM 的修复)
  3. *
  4. * 现象(2026-10-20):
  5. * 真机运行基座,App 启动 2 秒内必崩,堆栈停在
  6. * io.dcloud.feature.weex.WXViewWrapper.render -> StringBuilder.append,
  7. * 每次都是 Failed to allocate 26943xxxx byte(≈257MB)。
  8. *
  9. * 根因:
  10. * ① dev 编译把 2900 个模块的 sourcemap 连同 sourcesContent(模块源码)
  11. * base64 内联进 app-service.js,让 bundle 从 25.6MB 膨胀到 67.8MB。
  12. * ② webpack 在 development 下 mode=development,完全不压缩,385 个页面
  13. * 的源码 39MB 原样进包。
  14. * native 层把这个 JS 按 UTF-16 读成字符串再拼一次,单次分配 ≈ 体积 × 4:
  15. * 67.8MB -> 269MB -> OOM
  16. * 41.4MB -> ~165MB -> 压着 512MB 堆上限,仍然危险
  17. * 11MB -> ~44MB -> 安全
  18. *
  19. * 为什么只设 devtool 没用:
  20. * @dcloudio/vue-cli-plugin-uni/lib/configure-webpack.js 在 development 下是
  21. * 直接 plugins.push(sourceMap.createEvalSourceMapDevToolPlugin()),
  22. * 绕过了 devtool 字段,必须把插件实例本身从 plugins 里摘掉。
  23. */
  24. const fs = require('fs')
  25. const path = require('path')
  26. // dev 下是否压缩 bundle(关闭后完全还原官方默认行为,会重新 OOM)
  27. const DEV_MINIFY = true
  28. // 官方开关:CLI 读取 process.env.UNI_MINIMIZE 决定 args.minimize,
  29. // 该值会打开 optimization.minimize 并关闭 namedModules。
  30. // 本文件在 Service 构造时加载,早于 args 求值,所以这里赋值有效。
  31. if (DEV_MINIFY && process.env.NODE_ENV === 'development') {
  32. process.env.UNI_MINIMIZE = 'true'
  33. }
  34. const DROP_PLUGINS = ['EvalSourceMapDevToolPlugin', 'SourceMapDevToolPlugin']
  35. function pluginName(p) {
  36. return (p && p.constructor && p.constructor.name) || ''
  37. }
  38. function isSourceMapPlugin(p) {
  39. return DROP_PLUGINS.indexOf(pluginName(p)) >= 0
  40. }
  41. // 产物体积报告:编译结束后把 app-service.js / app-view.js 大小追加进
  42. // .workbuddy/.bundle-size.log,同时打到 HBuilderX 控制台,便于确认改动生效。
  43. function createSizeReporter() {
  44. return {
  45. apply(compiler) {
  46. compiler.hooks.done.tap('BundleSizeReporter', () => {
  47. try {
  48. const outDir = (compiler.options.output && compiler.options.output.path) || ''
  49. if (!outDir) return
  50. const lines = []
  51. ;['app-service.js', 'app-view.js'].forEach((f) => {
  52. const p = path.join(outDir, f)
  53. if (fs.existsSync(p)) {
  54. lines.push(`${f}: ${(fs.statSync(p).size / 1048576).toFixed(2)} MB`)
  55. }
  56. })
  57. if (!lines.length) return
  58. const text = `[${new Date().toLocaleString()}] ${lines.join(' | ')}`
  59. fs.mkdirSync(path.join(__dirname, '.workbuddy'), { recursive: true })
  60. fs.appendFileSync(path.join(__dirname, '.workbuddy', '.bundle-size.log'), text + '\n')
  61. console.log('\n[bundle-size] ' + lines.join(' | ') + '\n')
  62. } catch (e) {
  63. // 报告失败不影响构建
  64. }
  65. })
  66. },
  67. }
  68. }
  69. module.exports = {
  70. // chainWebpack 按构造器名删除插件(vue-cli 的 plugins 集合按名字索引)
  71. chainWebpack(config) {
  72. if (process.env.NODE_ENV !== 'development') return
  73. DROP_PLUGINS.forEach((name) => {
  74. try {
  75. config.plugins.delete(name)
  76. } catch (e) {
  77. // 插件不存在时忽略
  78. }
  79. })
  80. },
  81. // configureWebpack 再做一层兜底:有些编译流程里插件是直接 push 进数组的,
  82. // 而 optimization 走链式 API 容易被框架后续覆盖,这里直接写最终对象。
  83. configureWebpack(config) {
  84. if (process.env.NODE_ENV !== 'development') return
  85. if (!config || !Array.isArray(config.plugins)) return
  86. config.plugins = config.plugins.filter((p) => !isSourceMapPlugin(p))
  87. if (DEV_MINIFY) {
  88. // webpack 4 在 minimize=true 且未指定 minimizer 时默认使用
  89. // terser-webpack-plugin(uniapp-cli 自带),与正式包同一套压缩。
  90. config.optimization = config.optimization || {}
  91. config.optimization.minimize = true
  92. config.optimization.namedModules = false
  93. }
  94. config.plugins.push(createSizeReporter())
  95. },
  96. }