3

我有一种情况,我没有正确配置 Terser,这导致我的页面的压缩版本中断。我的问题是我的 index.js 中有一些功能声明。这些声明需要可以在以后加载的 Bootstrap 模式窗口中访问。

例如,在我index.js有一个这样声明的函数:

function doThis() { }

然后,假设用户在模式窗口中打开一个地址表单,并加载了一个名为“address-form.js”的不同 JavaScript 文件。在这种形式中,有一个带有 onclick 处理程序的按钮,它调用doThis(). onclick 处理程序位于“address-form.js”中,但能够doThis()在父 index.js 中访问。当 index.js 未压缩时,该按钮可以正常工作。但是在压缩之后,我得到一个错误doThis(),说不存在。

我认为这是一个范围界定问题,因为我确实在 中看到了doThis()声明index.js,但它似乎被包裹在一堆括号中。我不确定如何使 doThis() 的范围成为顶级窗口。中的数百个函数声明和变量存在相同的范围界定问题index.js,因此我正在寻找一种解决方案,我不必对庞大的文件进行太多修改。

请注意,如果我将函数声明更改为表达式,它似乎确实有效:

window.doThis = function() {
}

但是,由于文件中有数百个varsandconstlet变量(除了几十个函数声明),因此更改所有变量的范围以使压缩工作对我来说是不切实际的。

这是我的 webpack.config.js:

const TerserPlugin = require("terser-webpack-plugin")
const glob = require('glob')
const path = require('path')
const webpack = require('webpack')

module.exports = {
  entry: glob.sync('./js/Pages/*.js').reduce((object, element) => {
    object[path.parse(element).name] = element
    return object
  }, {}),
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, './js/Pages/minified')
  },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
        test: /\.js(\?.*)?$/i,
        terserOptions: {
          mangle: false,
          compress: true,
          keep_fnames: true,
          keep_classnames: true,
          ie8: false,
          safari10: false,
          toplevel: false
        }
      })
    ]
  },
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    })
  ]
}

我正在运行的命令是:

webpack --mode=production --config webpack.config.js

解决方案

下面接受的解决方案是直接使用 terser-cli。如果有人觉得它有帮助,我编写了一个小脚本,它在目录中的每个文件上运行 terser:

const fs = require('fs')
const path = require('path')
const exec = require('child_process').exec;
const Terser = require('terser')
const srcDir = '../js/Pages'
const destDir = '../js/Pages/minified'


function minifyPagesDir() {
  let srcFileNames = fs.readdirSync(srcDir).filter(path => path.match(/\.js$/)) || []
  let srcFilePaths = []
  
  let destFilePaths = srcFileNames.map((item, i) => {
    srcFilePaths[i] = `${srcDir}/${srcFileNames[i]}`
    return `${destDir}/${item}`
  })

  if (!fs.existsSync(destDir))
    fs.mkdirSync(destDir)
  
  srcFileNames.forEach((item, i) => {
    exec(
      `terser ${srcFilePaths[i]} -c -o ${destFilePaths[i]} --ecma 2021`,
      (error, stdout, stderr) => {
        if (error !== null)
            console.log(`RUHROH: ${error}`, ' stderr: ', stderr);
      }
    )

    console.log(`Minified '${srcFilePaths[i]}' to '${destFilePaths[i]}'!`)
  })
  console.log('Minification complete!')
}

minifyPagesDir()
4

1 回答 1

1

我使用您的配置创建了一个 repro 项目,并验证您的问题不是由 terser 引起的,而是由 webpack 引起的。

我使用了以下测试文件:

function doThis() { }

minimize 禁用且未设置,这是使用minimizerwebpack 构建测试文件的结果:

/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
function doThis() { }
/******/ })()
;

如果我们重新格式化并删除注释,很明显文件中的所有内容都是死代码。Webpack 是一个捆绑器,将所有内容包装在一个IIFE中。这意味着函数不会分配给全局范围。

(() => {
  var __webpack_exports__ = {};
  function doThis() { }
})();

由于您没有导出任何内容或导致任何副作用,例如分配给window,因此 terser 将删除内容。这是正确的,即使不删除它,您仍然会遇到相同的错误,因为 webpack 输出中的函数未分配给全局范围,并且您的处理程序无法访问它。使用 terser cli 压缩 webpack 的导出会导致一个空文件。

直接在测试输入文件上运行 terser cli,会产生所需的压缩输出:

function doThis(){}

您说您已经从命令行运行了 terser,并且遇到了与通过 webpack 捆绑和压缩时相同的问题。你是通过 terser 还是 webpack 的输出运行源文件?因为通过 terser 运行源文件应该会产生所需的输出。请尝试运行terser foo/bar.js -c -o foo/bar.min.js并测试它是否可以解决您的问题。

如果你想使用 webpack,你需要分配window或完全不使用全局范围。

于 2021-06-25T08:03:26.830 回答