刚看了官网的webpack例子,跟着做了一遍,把相关的配置用法注释了一遍,另外把我在配置中遇到的问题也分享出来。。。
一.webpack的配置详细注释
const config = {
//入口文件配置
entry:{
mian:'./src/index.js',
// print:'./src/print.js'
vendor:['lodash']
},
//配置出口文件
output:{
//出口文件的名称
filename:'[name].bundle.js',
//出口文件的路径
path:path.resolve(__dirname,'dist'),
//publicPath 也会在服务器脚本用到,以确保文件资源能够在 http://localhost:3000 下正确访问
publicPath:'/'
},
mode:'none',
module:{
//匹配除开js之外其他的文件,让相应的loader将文件解析成js文件
rules:[
{
test:/\.css$/,
//将 loaders 应用于最少数的必要模块中
//使用 include 字段仅将 loader 模块应用在实际需要用其转换的位置中:
include: path.resolve(__dirname, "src"),
use:['style-loader','css-loader']
},
{
test:/\.xml$/,
use:'xml-loader'
}
]
},
//追踪错误来源,若不配置,错误会在bundle.js中,不好找错误
devtool:'inline-source-map',
//配置插件,完成loader无法完成的功能
plugins: [
//离线任务未完成,报错:TypeError: WorkboxPlugin is not a constructor,原因待查
// new WorkboxPlugin({
// // 这些选项帮助 ServiceWorkers 快速启用
// // 不允许遗留任何“旧的” ServiceWorkers
// clientsClaim: true,
// skipWaiting: true
// }),
//RuntimeChunkPlugin 的 'vendor' 实例,必须在 'common' 实例之前引入。
new webpack.optimize.RuntimeChunkPlugin({
name: "vendor"
}),
new webpack.optimize.RuntimeChunkPlugin({
name: "common"
}),
/*
自动生成html,及文件的引用
html-webpack-plugin用来打包入口html文件
entry配置的入口是js文件, webpack以js文件为入口, 遇到import, 用配置的loader加载引入文件
但作为浏览器打开的入口html, 是引用入口js的文件, 它在整个编译过程的外面,
所以, 我们需要html-webpack-plugin来打包作为入口的html文件
*/
new HtmlWebpackPlugin({
title: '柠檬🍋'
}),
//每次打包前会将dist文件夹中的文件清除,防止加载不必要的文件
new CleanWebpackPlugin(['dist']),
// webpack 内置的 HMR 插件
new webpack.HotModuleReplacementPlugin(),
//查看要修补(patch)的依赖
new webpack.NamedModulesPlugin(),
//删除未引用代码(dead code)的压缩工具(minifier)
new UglifyJSPlugin(),
//HashedModuleIdsPlugin,推荐用于生产环境构建:使用这个可以实现缓存,那些没有改变的文件就不会
//随着每次构建而改变了,节约资源
new webpack.HashedModuleIdsPlugin(),
],
//webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载,devServer启动的就是webpack-dev-server
//以下配置告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。
devServer:{
//告诉开发服务器(dev server),在哪里查找文件:
contentBase:'./dist',
port:'3333',
//启动热重载
hot:true
}
}
module.exports = config
二.配置过程中遇到的问题
- 1.error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
查了资料发现,webpack 4.0.0-beta.0删除了 CommonsChunkPlugin,以支持两个新的选项(optimization.splitChunks 和 optimization.runtimeChunk)。
从webpack 4.0.0-beta.0 开始分割 Chunk 将不在使用 CommonsChunkPlugin 插件,而是使用 optimization 配置项,具体的实现原理可以参考 CommonsChunkPlugin。
对于那些需要细粒度控制缓存策略的人,可以通过 optimization.splitChunks和 optimization.runtimeChunk。 现在可以使用 module.rules[].resolve来配置解析。它与全局配置合并。
删掉 new webpack.optimize.CommonsChunkPlugin({
name: 'common' // 指定公共 bundle 的名称。
}),
改用 new webpack.optimize.RuntimeChunkPlugin({
name: "common" // 指定公共 bundle 的名称
}),
optimization的配置介绍如下:
//这里有两种使用方式:
const config= {
//方式一:
optimization:{...}
//方式二:
plugins: [
new webpack.optimize.SplitChunksPlugin({...})
]
}
optimization参数介绍:
optimization: {
runtimeChunk: {
name: "manifest" //指定公共 bundle 的名称
},
splitChunks: {
chunks: "initial", // 必须三选一: "initial" | "all"(默认就是all) | "async"
minSize: 0, // 最小尺寸,默认0
minChunks: 1, // 最小 chunk ,默认1
maxAsyncRequests: 1, // 最大异步请求数, 默认1
maxInitialRequests: 1, // 最大初始化请求书,默认1
name: () => {}, // 名称,此选项可接收 function
cacheGroups: { // 这里开始设置缓存的 chunks
priority: "0", // 缓存组优先级 false | object |
vendor: { // key 为entry中定义的 入口名称
chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk
name: "vendor", // 要缓存的 分隔出来的 chunk 名称
minSize: 0,
minChunks: 1,
enforce: true,
maxAsyncRequests: 1, // 最大异步请求数, 默认1
maxInitialRequests: 1, // 最大初始化请求书,默认1
reuseExistingChunk: true // 可设置是否重用该chunk(查看源码没有发现默认值)
}
}
}
},
2.filename: '[name].[chunkhash].js',
dev环境时候报错:Cannot use [chunkhash] or [contenthash] for chunk in '[name].[chunkhash].js' (use [hash] instead)
prod环境时候都可以用。
这里[chunkhash]与[hash]的区别:
chunkhash会给每个出口文件每次生成不一样的hash名字
Built at: 2018-4-13 16:34:23
Asset Size Chunks Chunk Names
main.3d547e36b08c284ce1a2.js 76.1 KiB 0 [emitted] main
vendor.3e5be7d4aebfd3b7d5dd.js 69.4 KiB 1 [emitted] vendor
common.38541d717f3e409861be.js 1.09 KiB 2 [emitted] common
hash则每次给所有出口文件生成不一样的名字,但是所有出口文件的hash名字一样
Built at: 2018-4-13 16:33:22
Asset Size Chunks Chunk Names
main.5af0edc63954761b5bc3.js 76.1 KiB 0 [emitted] main
vendor.5af0edc63954761b5bc3.js 69.4 KiB 1 [emitted] vendor
common.5af0edc63954761b5bc3.js 1.09 KiB 2 [emitted] common
bundle 的名称是它内容(通过 hash)的映射。如果我们不做修改,然后再次运行构建,我们以为文件名会保持不变。然而,如果我们真的运行,可能会发现情况并非如此:(译注:这里的意思是,如果不做修改,文件名可能会变,也可能不会。)
三.性能优化
避免在生产环境下才会用到的工具,也就是说不要在开发环境下用这些插件。
某些实用工具, plugins 和 loaders 都只能在构建生产环境时才有用。例如,在开发时使用 UglifyJsPlugin 来压缩和修改代码是没有意义的。以下这些工具在开发中通常被排除在外:
- UglifyJsPlugin
- ExtractTextPlugin
- [hash]/[chunkhash]
- AggressiveSplittingPlugin
- AggressiveMergingPlugin
- ModuleConcatenationPlugin
四.在构建项目时设置路径值
在开发模式中,我们通常有一个 assets/ 文件夹,它往往存放在和首页一个级别的目录下。这样是挺方便;但是如果在生产环境下,你想把这些静态文件统一使用CDN加载,那该怎么办?
想要解决这个问题,你可以使用有着悠久历史的环境变量。比如说,我们设置了一个名为 ASSET_PATH 的变量:
import webpack from 'webpack';
// 如果预先定义过环境变量,就将其赋值给`ASSET_PATH`变量,否则赋值为根目录
const ASSET_PATH = process.env.ASSET_PATH || '/';
export default {
output: {
publicPath: ASSET_PATH
},
plugins: [
// 该插件帮助我们安心地使用环境变量
new webpack.DefinePlugin({
'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH)
})
]
};
五. DefinePlugin的用法
DefinePlugin
允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。如果在开发构建中,而不在发布构建中执行日志记录,则可以使用全局常量来决定是否记录日志。这就是 DefinePlugin
的用处,设置它,就可以忘记开发和发布构建的规则。
new webpack.DefinePlugin({
// Definitions...
})
用法
每个传进 DefinePlugin
的键值都是一个标志符或者多个用 .
连接起来的标志符。
- 如果这个值是一个字符串,它会被当作一个代码片段来使用。
- 如果这个值不是字符串,它会被转化为字符串(包括函数)。
- 如果这个值是一个对象,它所有的 key 会被同样的方式定义。
- 如果在一个 key 前面加了
typeof
,它会被定义为 typeof 调用。
这些值会被内联进那些允许传一个代码压缩参数的代码中,从而减少冗余的条件判断。
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
"typeof window": JSON.stringify("object")
})
console.log("Running App version " + VERSION);
if(!BROWSER_SUPPORTS_HTML5) require("html5shiv");
T> 注意,因为这个插件直接执行文本替换,给定的值必须包含字符串本身内的实际引号。通常,有两种方式来达到这个效果,使用 '"production"'
, 或者使用 JSON.stringify('production')
。
index.js
if (!PRODUCTION) {
console.log('Debug info')
}
if (PRODUCTION) {
console.log('Production log')
}
通过没有使用压缩的 webpack 的结果:
if (!true) {
console.log('Debug info')
}
if (true) {
console.log('Production log')
}
通过使用压缩的 webpack 的结果:
console.log('Production log')
功能标记(Feature Flags)
使用功能标记来「启用/禁用」「生产/开发」构建中的功能。
new webpack.DefinePlugin({
'NICE_FEATURE': JSON.stringify(true),
'EXPERIMENTAL_FEATURE': JSON.stringify(false)
})
W> When defining values for process
prefer 'process.env.NODE_ENV': JSON.stringify('production')
over process: { env: { NODE_ENV: JSON.stringify('production') } }
. Using the latter will overwrite the process
object which can break compatibility with some modules that expect other values on the process object to be defined.
服务 URL(Service URLs)
在「生产/开发」构建中使用不同的服务 URL(Service URLs):
new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify("http://dev.example.com")
})
网友评论