生产环境配置
如何让用户更快加载资源,包括:
- 如何压缩资源
- 如何添加环境变量优化打包
- 如何最大限度地利用缓存等
为不同环境设置不同的配置文件
- 在package.json中添加环境变量,在webpack.config.js中判断环境变量去做不同处理
// package.json
{
"script": {
"dev": "ENV=development webpack-dev-server",
"build": "ENV=production webpack"
}
}
// webpack.config.js
const ENV = process.env.ENV // 注意,process.env只能在webpack.config.js中读取到
const isProd = ENV === 'production'
module.exports = {
output: {
filename: isProd ? 'bundle@[chuckhash].js' : 'bundle.js'
},
mode: ENV,
// ...
}
- 或者为不同环境创建各自的配置文件:如webpack.development.config.js、webpack.production.config.js、webpack.common.config.js
// package.json
{
"script": {
"dev": "webpack-dev-server --config=webpack.development.config.js",
"build": "webpack --config=webpack.production.config.js"
}
}
// webpack.common.config.js
module.exports = {
// 一些公共的配置项
}
// webpack.production.config.js
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.config.js')
const prodConfig = {
// 生产环境独有配置项...
}
module.exports = merge.smart(commonConfig, prodConfig)
// webpack.development.config.js
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.config.js')
const devConfig = {
// 开发环境独有配置项...
}
module.exports = merge(commonConfig, devConfig)
DefinePlugin 设置环境变量
如果想在src文件中获取到环境变量,只在script中写上ENV=produciton是获取不到的,会报错
// index.js
console.log(process.env.ENV) // Uncaught ReferenceError: process is not defined
- 修改webpack配置,定义自定义的环境变量
// webpack.config.js
const webapck = require('webpack')
module.exports = {
plugins: [
new webpack.DefinePlugin({
ENV: JSON.stringify('development'),
// 也可以添加其它自定义环境变量
TEST_ENV: JSON.stringify({
TYPES: [11, 'aa']
})
})
]
}
// index.js
console.log(ENV); // development
console.log(TEST_ENV); // {TYPES: Array(2)}
DefinePlugin在替换环境变量时,对于字符串的值是进行完全替换,如果不加JSON.stringify,就会将字符串变成变量,因此对于字符串和包含字符串的对象,都要加上JSON.stringify
- 如果是设置了mode属性,webpack会自动设置好了process.env.NODE_ENV,不需要人为设置当前环境变量,所以不设置DefinePlugin,也可以在src文件中获取当前环境
console.log(process.env.NODE_ENV) // development
source map配置
如果不配置source map,那么在浏览器调试时输出的是打包后的文件,无法与源码对应,找到问题。
配置了source-map后,就可以在chrome的source中,查找到对应的源码。
对于css,scss,less来说,需要添加额外的source map配置项
module.exports = {
devtool: 'source-map',
module: {
rules: [
{
test: /\.scss$/,
use: {
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
}
}
]
}
}
安全问题
配置了source map后,也意味着将源码暴露给了客户,那么怎么才能保持其功能的同时,又防止暴露源码呢?
webpack提供了hidden-source-map和nosources-source-map两种策略来提升安全性。
hidden-source-map
仍然产生完整的map文件,但不会在bundle文件中添加对map文件的引用。想要追溯源码,可以利用一些第三方服务,将map文件上传到上面。目前最流行的解决方案是: sentry
- sentry
是一个错误跟踪平台,开发者接入后可以进行错误的收集和聚类,以便更好发现和解决问题。sentry支持js的source map,可以通过它提供的命令行工具或webpack插件来自动上传map文件。
同时,还要在工程代码中添加sentry对应的工具包,每当js执行出错时,会上报给sentry。sentry在接收错误后,会去对应的map文件进行源码解析,并给出源码中的错误栈。
nosources-source-map
它对于安全性的保护没那么强,但使用方式相对简单。打包后,可以在浏览器中sources看到源码的目录结构,但文件的具体内容会被隐藏。
对于错误来说,仍可以在console控制台中查看源代码的错误栈,或者console日志的准确行数。
使用nginx配置.map文件的白名单
source-map正常开放,但将.map文件只对部分名单开放(比如公司内网)
压缩js
压缩文件,移动多余的空格、挑选及执行不到的代码,缩短变量名,一般正常代码在压缩后整体体积都将显著缩小。压缩后的代码可读性极差,在一定程序上提升了代码的安全性。
压缩js的工具有两个:
uglify, webpack3使用
terser, webpack4之后使用terser的插件terser-webpack-plugin
- 生产环境默认配置
mode: "production",
// 如果设置了 mode: production会默认打开,不需要额外设置,会默认打开这个配置
// optimization: {
// minimize: true
// }
- 同时也支持用户自定义压缩的配置
const TerserWebpackPlugin = require("terser-webpack-plugin")
module.exports = {
mode: "production",
optimization: {
// 自定义配置
minimizer: [
new TerserWebpackPlugin({
test: /\.js$/i,
exclude: /\/excludes/
})
]
},
}
压缩css
bundle体积监控和分析
有两种方法:
- 下载vscode插件,import cost,可以在引用模块的地方,就可以查看压缩后和Gzip后的文件大小
// index.js
import React from 'react' // 7.2K (gzipped: 2.9K)
- 使用webpack-bundle-analyzer插件,进行监控和分析
进行以下配置后,运行npm run analyze,就可以在网页中查看打包后各模块的大小
// webpack.config.js
const WebpackBundleAnalyze = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
mode: 'production',
plugins: [
new WebpackBundleAnalyze()
]
}
{
"scripts": {
"analyze": "NODE_ENV=production npm_config_report=true npm run build"
}
}
资源hash:[chunkhash]
常用的一个方法,就是在每次打包过程中对资源的内容计算一次hash,并作为版本号放在文件名中,如bundle@10ruqirur9w8w.js,即名字+hash值,每当代码发生变化时,相应的hash也会变化
module.exports = {
entry: __dirname + "/src/index.js",
output: {
filename: "bundle@[chunkhash].js"
}
}
动态html:html-webpack-plugin
当使用资源hash时,会发现html中需要引用的script资源路径要改变,但要自己手动改是很困难的(也不可能,哼)。所以怎么根据js的文件名自动更新html中的引用,可以使用html-webpack-plugin来实现,它会将这个js文件动态地在head中使用script标签引入
npm i html-webpack-plugin
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
plugins: [
// 使用自定义模板生成一个动态html
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
<!-- ./index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我是自性化模板</title>
</head>
<body>
我啥也不放
</body>
</html>
<!-- 生成的dist/index.html -->
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>我是自性化模板</title><script defer="defer" src="bundle%4013aa409da0c9f98cce8f.js"></script></head><body>我啥也不放</body></html>
网友评论