-
entry
-
ouput
-
loader
-
plugins
-
mode
const { resolve } = require('path'); module.exports = { // 入口文件 entry: './index.js', // 输出文件 output: { filename: 'bundle.js', path: resolve(__dirname, 'dist') }, module: { rules: [ ] }, plugins: [], mode: 'development' };
npm i style-loader css-loader -D
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
npm i less less-loader -D
module: {
rules: [
{
test: /\.less$/,
use: [ 'style-loader', 'css-loader', 'less-loader' ]
}
]
}
npm i url-loader file-loader -D
module: {
rules: [
{
test: /\.(jpg|png|gif)$/,
// url-loader 依赖于 file-loader
loader: 'url-loader',
options: {
// 小于 8kb的图片会被base64处理
// 优点:减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// url-loader 默认使用es6模块化解析,而 html-loader 引入图片是commonjs
// 解析时会出问题: [object Module]
// 解决: 关闭 url-loader的es6模块化,使用commonjs解析
esModule: false,
// [hash:10]取哈希值的前10位
// [ext]取原文件拓展名
name: '[hash:10].[ext]',
outputPath: 'imgs'
}
}
]
}
npm i file-loader -D
module: {
rules: [
{
// 将不需要打包的资源排除在外
exclude: /\.(html|js|css|less|jpg|png|gif)$/,
loader: 'file-loader',
options: {
// [hash:10]取哈希值的前10位
// [ext]取原文件拓展名
name: '[hash:10].[ext]',
// 输出到 media 文件下
outputPath: 'media'
}
}
]
}
npm i html-loader -D
module: {
rules: [
{
test: /\.html$/,
loader: 'html-loader'
}
]
}
npm i postcss-loader postcss-preset-env -D
module: {
rules: [
{
test: /\.css$/,
use: [
// MiniCssExtractPlugin将取代style-loader。作用:将js中的css样式提取出来
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]
}
}
]
}
]
}
"browserslist": {
// 开发环境 --> 设置 node 的环境变量: process.env.NODE_ENV = 'development'
"development": [
// 兼容最近的一个 chorome 版本
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境 --> 设置 node 的环境变量: process.env.NODE_ENV = 'production'
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
process.env.NODE_ENV = 'development'
npm i babel-loader @babel/core @babel/preset-env -D
module: {
rules: [
{
/*
方法2 与方法3 只能二选一
1、基本js兼容性处理 --> @babel/preset-env
问题:只能转换基本语法,如promise等高级语法不能转换
2、全部js兼容性处理 --> @babel/polyfill --- (无需配置)
(1)安装: `npm i @babel/polyfill -D`
(2)引入: 原js代码中引入 import '@babel/polyfill'
问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积过大。
3、需要做兼容性处理的就做:按需加载 --> core.js
(1)安装: `npm i core-js -D`
(2)配置: 将presets改成
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定 core-js 版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
target: {
chrome: '',
firefox: '',
ie: '',
safari: '',
edge: ''
}
}
]
]
*/
// 将不需要打包的资源排除在外
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设: 指示 babel 做怎么样的兼容性处理
presets: '@babel/preset-env'
}
}
]
}
npm i html-webpack-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin(
{
// 复制一个模板 html 文件, 并且自动引入打包输出的所有资源
template: './index.html',
/*
压缩html代码 production环境使用
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComment: true
}
*/
}
)
]
npm i mini-css-extract-plugin -D
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{
test: /\.css$/,
use: [
// MiniCssExtractPlugin将取代style-loader。作用:将js中的css样式提取出来
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin(
// 对输出的 css 文件进行重命名
filename: 'css/bundle.css'
)
]
npm i optimize-css-assets-webpack-plugin -D
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
plugins: [
new OptimizeCssAssetsWebpackPlugin()
]
npx webpack-dev-server
npm i webpack-dev-server -D
devServer: {
contentBase: resolve(__dirname, 'dist'),
// 启动gzip压缩
compress: true,
// 端口号
port: 3000,
// 是否自动打开浏览器
open: true,
/*
是否打开HMR(Hot Module Replacement)热模块替换 / 模块热替换
作用:一个模块发生变化只会重新打包这个模块(而不是所有的模块)提升构建速度
样式文件:可以使用HMR功能,因为style-loader内部实现了
js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码
注意:HMR功能对js的处理,只能处理非入口文件的其他文件。
html文件:默认不能使用HMR功能,同时会导致问题,html文件不能热更新了。
解决:修改entry入口,将html文件引入
entry: ['./index.js', './index.html']
*/
hot: true
}
npx webpack-dev-server
devtool: 'source-map'
/*
source-map: 一种提供源代码到构建后代码的映射技术
[inline-|hidden-|eval][nosources-][cheap-[module-]]source-map
source-map: 外部
提示错误代码的准确信息 和 源代码的错误位置
inline-source-map:内联
只生成一个内联source-map
提示错误代码的准确信息 和 源代码的错误位置
hidden-source-map:外部
提示代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置
eval-source-map:内联
每一个文件都生成对应的source-map,都在eval函数中
提示错误代码的准确信息 和 源代码的错误位置
nosources-source-map:外部
提示错误代码的准确信息,但是没有任何源代码信息
cheap-source-map:外部
提示错误代码的准确信息 和 源代码的错误位置
只能精确到源代码的行
cheap-module-source-map:外部
提示错误代码的准确信息 和 源代码的错误位置
内联 和 外部 的区别: 1.外部生成了文件,内联是没有的。2.内联构建速度更快
开发环境:速度快,调试更友好
速度快(eval>inline>cheap>...)
eval-cheap-source-map
eval-source-map --- (脚手架默认选择)
调试更友好
source-map
cheap-module-source-map
cheap-source-map
--> 建议选择eval-source-map / eval-cheap-module-source-map
生产环境:源代码是否隐藏?调试要不要更友好
内联会让代码体积更大,所以生产环境不用内联
nosources-source-map --- 全部隐藏
hidden-source-map --- 只隐藏源代码,会提示构建后代码错误信息
--> 建议 source-map / cheap-module-source-map
*/
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
]
module: {
rules: [
{
test: /\.css$/,
use: [...commonCssLoader]
}
]
}
-
通常来说每一种类型的文件要被所有的 loader 过一遍,oneOf能让文件只匹配一个loader。
-
注意:一种文件被两种loader处理的时候要将其中一个loader到外面去。
module: { rules: [ { oneOf: [ { test:/.css$/, use: [ 'style-loader', 'css-loader' ] } ] } ] }
- 类似于开发环境中的HMR。
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
}
-
hash:每次webpack构建时会生成一个唯一的hash值。
- 问题:因为js和css同时使用一个hash值。
-
如果重新打包会导致所有缓存失效。(可能我只改动一个文件导致所有缓存失效)
output: { filename: 'bundle.[hash:10].js', }, plugins: [ new MiniCssExtractPlugin( filename: 'css/bundle.[hash:10].css' ) ]
-
- 问题:因为js和css同时使用一个hash值。
-
chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样。
- 问题:js和css的hash值还是一样
-
因为css是在js中被引入的,所以同属于一个chunk
output: { filename: 'bundle.[chunkhash:10].js', }, plugins: [ new MiniCssExtractPlugin( filename: 'css/bundle.[chunkhash:10].css' ) ]
-
- 问题:js和css的hash值还是一样
-
contenthash(推荐):根据文件的内容生成hash值。不同文件hash值一定不一样
output: { filename: 'bundle.[contenthash:10].js', }, plugins: [ new MiniCssExtractPlugin( filename: 'css/bundle.[contenthash:10].css' ) ]
- 使用ES6模块化
- 开启production环境
-
在package.json中配置
// 所有代码都没有副作用(都可以进行tree shaking) // 问题:可能会把css / @babel/polyfill (副作用)文件干掉 "sideEffects": false // 推荐以下写法,将 样式文件 或者 @babel/polyfill 排除,以免被tree shaking被干掉 "sideEffects": ["*.css", "*.less"]
// 入口文件
entry: {
index: './index.js',
test: './test.js'
},
// 输出文件
output: {
[name]:提取文件名
filename: '[name].[contenthash:10].js',
path: resolve(__dirname, 'dist')
}
// 入口文件
entry: './index.js',
// 输出文件
output: {
filename: '[name].[contenthash:10].js',
path: resolve(__dirname, 'dist')
},
/*
1、可以将node_modules中代码单独打包一个chunk最终输出。
2、自动分析多入口chunk中,有没有公共的文件(有大小要求,至少要有几十kb)。如果有会打包成单独的一个chunk。
*/
// 入口文件
entry: {
index: './index.js',
test: './test.js'
},
// 输出文件
output: {
filename: '[name].[contenthash:10].js',
path: resolve(__dirname, 'dist')
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
###做法三(路由懒加载等用法): /* 通过js代码,让某个文件被单独打包成一个chunk import动态导入语法:能将某个文件单独打包 / / webpackChunkName: 'test' /将打包的名字设定为固定的名字 import(/ webpackChunkName: 'test' */'./test') .then() => { // Do something }
npm i thread-loader -D
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
/*
开启多进程打包。
进程启动时间大概600ms,进程通信也有开销。
只有工作消耗时间比较长,才需要多进程打包
*/
{
loader: 'thread-loader',
options: {
workers: 2 // 2个进程
}
},
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定 core-js 版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
target: {
chrome: '',
firefox: '',
ie: '',
safari: '',
edge: ''
}
}
]
]
}
}
]
}
]
}
###配置 mode: 'production', externals: { // 忽略库名 -- npm包名 jquery: 'jQuery' }
打包完成后再html文件内引入CDN资源
与externals类似,指示webpack哪些文件是不参与打包的。
webpack --config webpack.dll.js
/*
使用dll技术,对某些库(第三方库:jQuery、react、Vue...)进行单独打包
当你运行webpack时,默认查找 webpack.config.js 配置文件
需求:需要运行 webpack.dll.js 文件
-->webpack --config webpack.dll.js
*/
const { resolve } = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
// 最终打包生成的[name] --> jQuery
// ['jQuery'] --> 要打包的库是jquery
jquery: ['jQuery']
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
library: '[name]_[hash]', // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins: [
// 打包生成一个 manifest.json --> 提供和jquery映射
new webpack.DllPlugin({
name: '[name]_[hash]', // 映射库的暴露的内容名称
path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
})
],
mode: 'production'
}
const webpack = require('webpack');
plugins: [
// 告诉webpack那些库不参与打包,同时使用时的名称也得变
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
})
]
npm i add-asset-html-webpack-plugin -D
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
plugins: [
// 将某个文件打包输出去,并在html中自动引入资源
new AddAssetHtmlWebpackPlugin({
filePath: resolve(__dirname, 'dll/jquery.js');
})
]
- 优化打包构建速度
- HMR
- 优化代码调试
- source-map
- 优化打包构建速度
- oneOf
- babel缓存
- 多进程打包
- externals
- dll
- 优化代码运行的性能
- 缓存(hash-chunkhash-contenthash)
- tree shaking(mode: 'production'时默认开启)
- code split(异步执行就会懒加载)
- 懒加载 / 预加载(兼容性差)
- PWA(兼容性差)
const { resolve } = require('path');
const TerSerWebpackPlugin = require('terser-webpack-plugin');
module.exports = {
entry: './index.js',
output: {
// 文件名称(指定名称+目录)
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'dist'),
// 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> 'imgs/a.jpg'
publicPath: '/',
// 非入口chunk名称
chunkFilename: 'js/[name]_chunk.js',
library: '[name]', // 整个库向外暴露的变量名(一般不用)
//libraryTarget: 'window', // 变量名称添加到哪个上 browser
//libraryTarget: 'global', // 变量名添加到哪个上 node
libraryTarget: 'commonjs',
chunkFilename: 'js/[name]_[contenthash:10]_chunk.js'
},
module: {},
plugins: [],
mode: 'production',
resolve: {
// 配置解析模块路径别名:优点简写路径 缺点路径没提示
alias: {
$css: resolve(__dirname, 'src/css')
}
},
// 配置省略文件路径的后缀名
extensions: ['.js', '.jsx', 'json'],
// 告诉 webpack 解析模块是去哪个目录下寻找
modules: [resolve(__dirname, './node_modules'), 'node_modules'],
devServer: {
// 运行代码的目录
contentBase: resolve(__dirname, 'build'),
// 监视 contentBase 目录下的所有文件,一旦文件变化就会reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动 gzip 压缩
compress: true,
// 端口号
port: 3000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本启动信息意外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示
overlay: false,
// 服务器代理 --> 解决开发环境跨域问题
proxy: {
// 一旦devServer(5000)服务器接收到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将/api/xxx --> /xxx(去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
},
// optimization 生产环境才有意义
optimization: {
splitChunks: {
chunks: 'all',
/*
下面都是默认值,可以不改
minSize: 30 * 1024, // 分割的 chunk 最小为30kb
maxSize: 0, // 最大没有限制
minChunks: 1, // 要提取的chunks最少被引用1次
maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
maxInitialRequests: 3, // 入口js文件最大并行请求数量
automaticNameDelimiter: '~', // 名称连接符
name: true, //可以使用命名规则
cacheGroups: { // 分割chunk的组
vendors: {
// node_modules 文件会被打包到 vendors 组的 chunk 中。 --> vendors~xxx.js
// 满足上面的公共规则,如大小超过30kb, 至少被引用一次。
test: /[\\/]node_modules[\\/]/,
// 优先级
priority: -10
},
default: {
// 要提取的 chunk 最少被引用2次
minChunks: 2,
// 优先级
priority: -20,
// 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
reuseExistingChunk: true
}
}
*/
},
// 将当前模块保存的其他模块的 hash 值记录单独打包为一个文件 runtime
// 问题:修改a文件导致b文件的contenthash变化导致两个文件都重新打包了。
// 解决:将contenthash值提取出来保存在 runtime 里
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: {
/* 安装npm i terser-webpack-plugin -D
配置生产环境的压缩方案: js和css
*/
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启动 source-map
source-map: true
})
}
}
}