1 webpack的作用
webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序服务
打包器bundler:webpack可以将前端相互具有依赖性的资源、代码打包在一起,最后输出静态资源。
其中:webpack默认支持各种模块化的开发,ES Module、CommonJS、AMD等。
2 安装webpack
2.1 全局安装
npm install webpack webpack-cli –g
如果只有全局的 webpack,那么打包的时候,用了全局的 webpack,不同电脑的 webpack 版本不同会导致包的安装版本不同。
2.2 局部安装
npm install webpack webpack-cli --save-dev
每一个项目都有自己的 webpack 的版本, –save-dev 是开发时依赖(等同于-D),定义了统一的 webpack 版本,打包的时候不会出现包的版本问题。
说明:webpack从4.0版本开始,在安装时,就需要安装webpack和webpack-cli这两个东西。
- webpack是打包代码时的核心内容。
- webpack-cli是一个用来在命令行中运行webpack的工具。
3 webpack-cli介绍
以npm run build命令来解析webpack-cli在代码打包中的作用。
步骤一:找到webpack-cli文件
当在命令行中执行npm run build时,会执行node_modules/.bin下的webpack可执行文件。这里有三个webpack可执行文件,分别对应不同的平台。
以webpack可执行文件为例,从代码中可以看到,会执行
"%dp0%\..\webpack\bin\webpack.js"
该文件的主要代码是
const CLIs =[]
该对象是为了执行webpack-cli包中bin目录下的cli.js文件,也就是说在此之前的步骤只是为了找到cli.js文件,在此之后,webpack-cli才发挥作用。
步骤二:webpack-cli的运行
打开webpack-cli/bin/cli.js。该文件的主要函数是runCLI。
const runCLI = require("../lib/bootstrap");
而runCLI来自bootstrap.js文件,打开该文件。
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const WebpackCLI = require("./webpack-cli");
const runCLI = async (args) => {
// Create a new instance of the CLI object
const cli = new WebpackCLI();
try {
await cli.run(args);//重要语句
}
catch (error) {
cli.logger.error(error);
process.exit(2);
}
};
module.exports = runCLI;
到了这里,才用到webpack-cli暴露出的接口,cli-run(args)用来处理命令行参数,此时命令行参数为
[
'E:\nodejs/nodejs/node.exe',
'D:\webpack-wx\node_modules\webpack\bin\webpack.js',
'--config',
'wk.config.js'
]
过程总结
npm run build -> 执行node_modules/webpack文件 -> 执行webpack-cli文件 -> 执行cli.run() -> 处理命令行参数。
功能总结
webpack-cli用来处理命令行参数,并通过参数构建compiler对象,最后才是对代码打包的过程。
因此,webpack-cli的功能只是为了处理命令行参数,那我们同样可以构建自己的cli来处理参数,比如 lyx-cli。在第三方框架中,React 和 Vue(未使用Vite的版本)也没有使用 webpack-cli。
4 压缩混淆
像软件加密与解密一样,javascript的混淆与解混淆同属于同一个范畴。道高一尺,魔高一丈。没有永恒的黑,也没有永恒的白。一切都是资本市场驱动行为,现在都流行你能为人解决什么问题,这个概念。那么市场究竟能容纳多少个能解决这种问题的利益者。JS没有秘密。
javascript进行hash混淆处理,会拖慢运行时速度,且体积增大。JS代码前端可获取,天生赋予“开源”属性,都可以在chrome devTools下查看。JS非压缩性混淆完全违法前端优化准则。但如果源码保密性较高,可选择使用。
目前网络上可以搜索的JS混淆工具不外乎以下几种:
三种混淆方式
4.1 方式一:uglify
压缩类型:是目前前端性能优化的常用工具,以uglify为代表。
npm i -D webpack-parallel-uglify-plugin
使用方法:
const path = require('path');
const ParallelUglifyPlugin = require("webpack-parallel-uglify-plugin");
module.exports = merge(common, {
mode: 'production',
output: {
path: path.resolve(__dirname, "../dist"),
filename: `[name].min.js`
},
optimization: {
minimize:true,
minimizer: [
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS: {
output: {
comments: false,
beautify: false,
},
compress: {
drop_console: true,
collapse_vars: true,
reduce_vars: true
}
}
})
],
splitChunks: {
chunks: 'all',
cacheGroups: {
libs: {
name: "chunk-libs",
test: /[/]node_modules[/]/,
priority: 10,
chunks: "initial" // 只打包初始时依赖的第三方
}
}
}
}
});
4.2 方式二:eval混淆
也是最早JS出现的混淆加密,据说第一天就被破解,修改一下代码,alert一下就可以破解了。这种方法从出生的那天就失去了意义。其实JS加密(混淆)是相对于可读性而言的,其实真正有意义的就是压缩型混淆uglify这一类,即可减少体重,也可减少可读性。
4.3 方式三:obfuscator
也不能排除部分商业源代码使用hash类型混淆源代码,比如 miniui 使用的JSA加密, fundebug使用的javascript-obfuscator。
npm install --save-dev webpack-obfuscator
4.3.1 使用方式
const path = require('path');
const { merge } = require('webpack-merge');
const TerserWebpackPlugin = require("terser-webpack-plugin");
const ParallelUglifyPlugin = require("webpack-parallel-uglify-plugin");
const common = require('./webpack.common');
const JavaScriptObfuscator = require('webpack-obfuscator');
module.exports = merge(common, {
mode: 'production',
output: {
path: path.resolve(__dirname, "../dist"),
filename: `[name].min.js`
},
optimization: {
minimize:true,
minimizer: [
// new TerserWebpackPlugin()
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS: {
output: {
comments: false,
beautify: false,
},
compress: {
drop_console: true,
collapse_vars: true,
reduce_vars: true
}
}
})
],
splitChunks: {
chunks: 'all',
cacheGroups: {
libs: {
name: "chunk-libs",
test: /[/]node_modules[/]/,
priority: 10,
chunks: "initial" // 只打包初始时依赖的第三方
}
}
}
},
plugins:[
new JavaScriptObfuscator({
rotateUnicodeArray: true
})
]
});
4.3.2 构建文件对比
(1)webpack-obfuscator无参数时
new JavaScriptObfuscator({ }, [])
(2)webpack-obfuscator
高度混淆
低性能:性能比没有模糊处理慢 50-100%
new JavaScriptObfuscator({
// 压缩代码
compact: true,
// 是否启用控制流扁平化(降低1.5倍的运行速度)
controlFlowFlattening: true,
// 应用概率;在较大的代码库中,建议降低此值,因为大量的控制流转换可能会增加代码的大小并降低代码的速度。
controlFlowFlatteningThreshold: 1,
// 随机的死代码块(增加了混淆代码的大小)
deadCodeInjection: true,
// 死代码块的影响概率
deadCodeInjectionThreshold: 1,
// 此选项几乎不可能使用开发者工具的控制台选项卡
debugProtection: true,
// 如果选中,则会在“控制台”选项卡上使用间隔强制调试模式,从而更难使用“开发人员工具”的其他功能。
debugProtectionInterval: true,
// 通过用空函数替换它们来禁用console.log,console.info,console.error和console.warn。这使得调试器的使用更加困难。
disableConsoleOutput: true,
// 标识符的混淆方式 hexadecimal(十六进制) mangled(短标识符)
identifierNamesGenerator: 'hexadecimal',
log: false,
// 是否启用全局变量和函数名称的混淆
renameGlobals: false,
// 通过固定和随机(在代码混淆时生成)的位置移动数组。这使得将删除的字符串的顺序与其原始位置相匹配变得更加困难。如果原始源代码不小,建议使用此选项,因为辅助函数可以引起注意。
rotateStringArray: true,
// 混淆后的代码,不能使用代码美化,同时需要配置 cpmpat:true;
selfDefending: true,
// 删除字符串文字并将它们放在一个特殊的数组中
stringArray: true,
stringArrayEncoding: 'rc4',
stringArrayThreshold: 1,
// 允许启用/禁用字符串转换为unicode转义序列。Unicode转义序列大大增加了代码大小,并且可以轻松地将字符串恢复为原始视图。建议仅对小型源代码启用此选项。
transformObjectKeys: true,
unicodeEscapeSequence: false
}, []),
(3)webpack-obfuscator
中等混淆
最佳性能:性能比没有模糊处理慢 30-35%
new JavaScriptObfuscator({
// 压缩代码
compact: true,
// 是否启用控制流扁平化(降低1.5倍的运行速度)
controlFlowFlattening: true,
// 应用概率;在较大的代码库中,建议降低此值,因为大量的控制流转换可能会增加代码的大小并降低代码的速度。
controlFlowFlatteningThreshold: 0.75,
// 随机的死代码块(增加了混淆代码的大小)
deadCodeInjection: true,
// 死代码块的影响概率
deadCodeInjectionThreshold: 0.4,
// 此选项几乎不可能使用开发者工具的控制台选项卡
debugProtection: false,
// 如果选中,则会在“控制台”选项卡上使用间隔强制调试模式,从而更难使用“开发人员工具”的其他功能。
debugProtectionInterval: false,
// 通过用空函数替换它们来禁用console.log,console.info,console.error和console.warn。这使得调试器的使用更加困难。
disableConsoleOutput: true,
// 标识符的混淆方式 hexadecimal(十六进制) mangled(短标识符)
identifierNamesGenerator: 'hexadecimal',
log: false,
// 是否启用全局变量和函数名称的混淆
renameGlobals: false,
// 通过固定和随机(在代码混淆时生成)的位置移动数组。这使得将删除的字符串的顺序与其原始位置相匹配变得更加困难。如果原始源代码不小,建议使用此选项,因为辅助函数可以引起注意。
rotateStringArray: true,
// 混淆后的代码,不能使用代码美化,同时需要配置 cpmpat:true;
selfDefending: true,
// 删除字符串文字并将它们放在一个特殊的数组中
stringArray: true,
stringArrayEncoding: 'base64',
stringArrayThreshold: 0.75,
transformObjectKeys: true,
// 允许启用/禁用字符串转换为unicode转义序列。Unicode转义序列大大增加了代码大小,并且可以轻松地将字符串恢复为原始视图。建议仅对小型源代码启用此选项。
unicodeEscapeSequence: false
}, []),
(4)webpack-obfuscator
低混淆
高性能: 性能稍微慢于没有混淆
new JavaScriptObfuscator({
// 压缩代码
compact: true,
// 是否启用控制流扁平化(降低1.5倍的运行速度)
controlFlowFlattening: false,
// 随机的死代码块(增加了混淆代码的大小)
deadCodeInjection: false,
// 此选项几乎不可能使用开发者工具的控制台选项卡
debugProtection: false,
// 如果选中,则会在“控制台”选项卡上使用间隔强制调试模式,从而更难使用“开发人员工具”的其他功能。
debugProtectionInterval: false,
// 通过用空函数替换它们来禁用console.log,console.info,console.error和console.warn。这使得调试器的使用更加困难。
disableConsoleOutput: true,
// 标识符的混淆方式 hexadecimal(十六进制) mangled(短标识符)
identifierNamesGenerator: 'hexadecimal',
log: false,
// 是否启用全局变量和函数名称的混淆
renameGlobals: false,
// 通过固定和随机(在代码混淆时生成)的位置移动数组。这使得将删除的字符串的顺序与其原始位置相匹配变得更加困难。如果原始源代码不小,建议使用此选项,因为辅助函数可以引起注意。
rotateStringArray: true,
// 混淆后的代码,不能使用代码美化,同时需要配置 cpmpat:true;
selfDefending: true,
// 删除字符串文字并将它们放在一个特殊的数组中
stringArray: true,
stringArrayEncoding: false,
stringArrayThreshold: 0.75,
// 允许启用/禁用字符串转换为unicode转义序列。Unicode转义序列大大增加了代码大小,并且可以轻松地将字符串恢复为原始视图。建议仅对小型源代码启用此选项。
unicodeEscapeSequence: false
}, []),
(5)对比表格
文件名称 | 文件大小 | 正常构建 | 无参数 | 低度混淆 | 中度混淆 | 高度混淆 |
test1.js | 117字节 | 117字节 | 363字节 | 2.36KB | 6.90KB | 29.2KB |
jquery.js | 111KB | 85.0KB | 115KB | 117KB | 401KB | 1.24MB |