terser-webpack-plugin 选项的范围问题
Scoping issue with terser-webpack-plugin options
我有一种情况,我没有正确配置 Terser,这导致我的页面的压缩版本中断。我的问题是我的 index.js 中有一些功能声明。这些声明需要可以从 Bootstrap 模式 windows 访问,可以在以后加载。
例如,在我的 index.js
中有一个这样声明的函数:
function doThis() { }
然后,比方说,用户在模式 window 中打开一个地址表单,并加载另一个名为 'address-form.js' 的 javascript 文件。在此表单中,有一个带有调用 doThis()
的 onclick 处理程序的按钮。 onclick 处理程序位于 'address-form.js' 中,但能够访问父 index.js 中的 doThis()
。当 index.js 未压缩时,该按钮工作正常。但是压缩后,我收到一条错误消息,提示 doThis()
不存在。
我认为这是一个范围问题,因为我确实在 index.js
中看到 doThis()
声明,但它似乎包含在一堆括号中。我不确定如何使 doThis() 的范围成为顶级 window。 index.js
中的数百个函数声明和变量都存在相同的范围问题,因此我正在寻找一种解决方案,我不必对这个庞大的文件进行太多修改。
请注意,如果我将函数声明更改为表达式,它似乎确实有效:
window.doThis = function() {
}
但是,由于文件中有数百个vars
和const
和let
变量(此外还有数十个函数声明),所以对我来说不太实用更改所有这些的范围,以便压缩工作。
这是我的 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()
我用你的配置创建了一个 repro 项目,并验证了你的问题不是由 terser 引起的,而是 webpack 引起的。
我使用了以下测试文件:
function doThis() { }
With minimize
disabled and no minimizer
set, this is the result of building the test file with webpack:
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
function doThis() { }
/******/ })()
;
如果我们重新格式化它并删除注释,很明显文件中的所有内容都是死代码。 Webpack 是一个捆绑器,将所有内容包装在 IIFE 中。这意味着函数不会分配给全局范围。
(() => {
var __webpack_exports__ = {};
function doThis() { }
})();
由于您没有导出任何内容或导致任何副作用,例如分配给 window
,terser 将删除内容。这是正确的,即使不删除它,你仍然会得到同样的错误,因为 webpack 输出中的函数没有分配给全局范围,你的处理程序无法访问它。使用 terser cli 压缩 webpack 的导出结果是一个空文件。
运行 直接在测试输入文件上的 terser cli,产生所需的压缩输出:
function doThis(){}
你说你从命令行 运行 更简洁并且遇到了与通过 webpack 捆绑和压缩时相同的问题。你 运行 源文件是通过 terser 还是 webpack 的输出?因为 运行 通过 terser 编译源文件应该会产生所需的输出。请尝试 运行ning terser foo/bar.js -c -o foo/bar.min.js
并测试它是否能解决您的问题。
如果你想使用 webpack,你需要分配给 window
或者完全不使用全局范围。
我有一种情况,我没有正确配置 Terser,这导致我的页面的压缩版本中断。我的问题是我的 index.js 中有一些功能声明。这些声明需要可以从 Bootstrap 模式 windows 访问,可以在以后加载。
例如,在我的 index.js
中有一个这样声明的函数:
function doThis() { }
然后,比方说,用户在模式 window 中打开一个地址表单,并加载另一个名为 'address-form.js' 的 javascript 文件。在此表单中,有一个带有调用 doThis()
的 onclick 处理程序的按钮。 onclick 处理程序位于 'address-form.js' 中,但能够访问父 index.js 中的 doThis()
。当 index.js 未压缩时,该按钮工作正常。但是压缩后,我收到一条错误消息,提示 doThis()
不存在。
我认为这是一个范围问题,因为我确实在 index.js
中看到 doThis()
声明,但它似乎包含在一堆括号中。我不确定如何使 doThis() 的范围成为顶级 window。 index.js
中的数百个函数声明和变量都存在相同的范围问题,因此我正在寻找一种解决方案,我不必对这个庞大的文件进行太多修改。
请注意,如果我将函数声明更改为表达式,它似乎确实有效:
window.doThis = function() {
}
但是,由于文件中有数百个vars
和const
和let
变量(此外还有数十个函数声明),所以对我来说不太实用更改所有这些的范围,以便压缩工作。
这是我的 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()
我用你的配置创建了一个 repro 项目,并验证了你的问题不是由 terser 引起的,而是 webpack 引起的。
我使用了以下测试文件:
function doThis() { }
With minimize
disabled and no minimizer
set, this is the result of building the test file with webpack:
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
function doThis() { }
/******/ })()
;
如果我们重新格式化它并删除注释,很明显文件中的所有内容都是死代码。 Webpack 是一个捆绑器,将所有内容包装在 IIFE 中。这意味着函数不会分配给全局范围。
(() => {
var __webpack_exports__ = {};
function doThis() { }
})();
由于您没有导出任何内容或导致任何副作用,例如分配给 window
,terser 将删除内容。这是正确的,即使不删除它,你仍然会得到同样的错误,因为 webpack 输出中的函数没有分配给全局范围,你的处理程序无法访问它。使用 terser cli 压缩 webpack 的导出结果是一个空文件。
运行 直接在测试输入文件上的 terser cli,产生所需的压缩输出:
function doThis(){}
你说你从命令行 运行 更简洁并且遇到了与通过 webpack 捆绑和压缩时相同的问题。你 运行 源文件是通过 terser 还是 webpack 的输出?因为 运行 通过 terser 编译源文件应该会产生所需的输出。请尝试 运行ning terser foo/bar.js -c -o foo/bar.min.js
并测试它是否能解决您的问题。
如果你想使用 webpack,你需要分配给 window
或者完全不使用全局范围。