webpack常用配置
一、安装与运行
1
| npm i webpack webpack-cli -D
|
配置文件:webpack.config.js
运行命令:配置**package.json
**文件
1 2 3 4 5
| { "scripts": { "build": "webpack --config webpack.config.js" } }
|
二、基础配置
1 2 3 4 5 6 7 8 9 10 11 12
| const {resolve} = require("path")
module.exports = { mode: 'development', entry: resolve(__dirname, 'src/index.js'), output: { path: resolve(__dirname, 'dist'), filename: 'js/bundle.js', }, module: { rules: [] }, plugins: [] }
|
三、开发配置
1. 处理CSS/Less/Sass
1 2 3 4 5
| npm i style-loader css-loader -D
npm i style-loader css-loader less less-loader -D
npm i style-loader css-loader sass-loader sass node-sass -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| module.exports = { module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader' ] }, { test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] }, ] } }
|
2. 处理HTML
html-webpack-plugin:指定默认处理的html文件,自动引入打包后的CSS,JS,同时可以压缩打包后的html
1
| npm i html-webpack-plugin -D
|
1 2 3 4 5 6 7 8 9 10
| const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = { plugins: [ new HtmlWebpackPlugin({ template: resolve(__dirname,"public/index.html"), }) ] }
|
3. 图片/字体处理
1
| npm i file-loader url-loader html-loader -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| module.exports = { module: { rules: [ { test: /\.(png|svg|jpe?g|gif)$/i, loader: 'url-loader', options: { limit: 4096, name: 'img/[name].[hash:8].[ext]' } }, { test: /\.html$/, loader: 'html-loader' }, { test: /\.(eot|ttf|woff2?)$/, loader: 'file-loader', options: { name: 'fonts/[name].[hash:8].[ext]' } } ] } }
|
4. 自动编译构建
4.1 watch
自动监听并构建,需要手动刷新浏览器。
原理:轮询判断文件的最后编辑时间是否发生变化,某个文件发生变化,先缓存起来,等aggregateTimeout之后再构建。
1 2 3 4 5 6 7 8
| module.exports = { watch: true, watchOptions: { poll: 1000, aggregateTimeout: 500, ignored: /node_modules/ } }
|
4.2 devServer
开发服务器。自动编译(只在内存中编译打包,不会有任何文件输出)、自动打开浏览器、自动刷新浏览器等功能。
1
| npm i webpack-dev-server -D
|
1 2 3 4 5
| { "script": { "dev": "webpack-dev-server --config webpack.config.js" } }
|
1 2 3 4 5 6 7 8 9
| module.exports = { devServer: { contentBase: resolve(__dirname, 'dist'), host: 'localhost', port: 3000, open: true, compress: true, } }
|
四、生产配置
1. CSS
1.1 提取CSS到单独文件中
1
| npm i mini-css-extract-plugin -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| module.exports = { module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../' } }, 'css-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:8].css', chunkFilename: 'css/[name].[contenthash:8].css', }), ] }
|
1.2 CSS兼容性处理
1
| npm i postcss-loader postcss-preset-env -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
module.exports = { module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../' } }, 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ "postcss-preset-env" ] } } } ] } ] } }
|
修改package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "browserslist": { "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ], "production": [ ">0.2%", "not dead", "not op_mini all" ] } }
|
1.3 CSS压缩
1
| npm i optimize-css-assets-webpack-plugin -D
|
1 2 3 4 5 6 7
| const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = { plugins: [ new OptimizeCssAssetsWebpackPlugin() ] }
|
2. JS兼容性处理
1
| npm i babel-loader @babel/core @babel/preset-env -D
|
2.1 兼容基础语法
两种配置方式写法相同。
(1) 配置到babel.config.js单独文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
module.exports = { presets: [ "@babel/preset-env", ] }
module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', } ] } }
|
(2) 配置到webpack.config.js中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ '@babel/preset-env', ] } } ] } }
|
2.2 兼容ES6,ES7等
(1) 全局兼容处理(polyfill)
1
| npm i @babel/polyfill -D
|
相当于把所有不兼容的JS方法等都重新定义(打包后,体积大)。
1 2 3 4 5
|
import "@babel/polyfill"
|
(2) 按需加载(core-js)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
module.exports = { presets: [ [ "@babel/preset-env", { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17' } } ] ] }
|
3. JS压缩
production模式下,自动压缩
4. HTML压缩
使用html-webpack-plugin提供的压缩功能
1 2 3 4 5 6 7 8 9 10 11 12 13
| module.exports = { plugins: [ new HtmlWebpackPlugin({ template: resolve(__dirname, 'public/index.html'), minify: { removeComments: true, collapseWhitespace: true, minifyCSS: true, minifyJS: true } }) ] }
|
5. 辅助插件清空构建目录
1
| npm i clean-webpack-plugin -D
|
1 2 3 4 5 6 7
| const { CleanWebpackPlugin } = require("clean-webpack-plugin")
module.exports = { plugins: [ new CleanWebpackPlugin() ] }
|
五、性能优化
1. 开发优化
1.1 HMR
HMR:一个模块发生了变化,只会重新构建这个模块,而不是所有模块,提高构建速度。
1 2 3 4 5 6 7 8
| module.exports = { devServer: { hot: true }, plugins: [ new HotModuleReplacementPlugin() ] }
|
-
样式文件:可以通过 style-loader 使用HMR
-
js文件:默认不能使用HMR, 可以通过module.hot 监听JS文件HMR
1 2 3 4 5 6 7 8 9
|
if (module.hot) { module.hot.accept('./js/common.js', function () { test() }) }
|
-
html文件:默认不能使用HMR,html文件只有一个,所以不需要使用HMR
1.2 source-map
一个信息文件,存储着位置信息,可以定位到源代码,用于代码调试。
[inline- | hidden- | eval-][nosources-][cheap- [module-]]source-map
- inline:内联,保存在打包后的js文件中,只生成一个最终的source-map信息,构建速度快。能定位到源代码的行列。
- hidden:外部map文件。不能定位到源代码。
- eval:内联,保存在打包后的js文件中,每个模块对应一个eval包裹的source-map信息。能定位到源代码的行列。
- nosources:外部map文件。不能定位到源代码。
- cheap:外部map文件。只能定位到源代码的行。
- module:外部map文件,包含loader。只能定位到源代码的行。
- source-map:外部map文件。能定位到源代码的行列。
开发环境:eval-source-map(速度快,调试友好)
生产环境:source-map / nosources-source-map(全部隐藏) / hidden-source-map(只隐藏源代码)
2. 生产优化
2.1 oneOf
只会匹配一个
1 2 3 4 5 6 7 8 9 10 11 12 13
| module.exports = { module: { rules: [ { test: /\.js$/ }, { oneOf: [ { test: /\.js$/ }, { test: /\.css$/ }, ] } ] } }
|
2.2 缓存
(1) babel-loader缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { cacheDirectory: true } } ] } }
|
(2) 文件资源缓存(文件指纹)
- hash:和整个项目的构建有关,只要项目文件有改动,整个项目构建的hash就会变
- chunkhash:和webpack打包的chunk有关,不同的entry会生成不同的chunkhash
- contenthash:根据文件内容定义hash,内容不变,则ContentHash不变
JS设置文件指纹:[name].[chunkhash:8].js
CSS设置文件指纹:[name].[contenthash:8].css
图片/字体设置文件指纹:[name]. [hash:8].[ext]
2.3 tree shaking
去除无用代码。
开启条件:
- 使用ES6模块化
- mode: production
在package.json中添加sideEffects: false
,表示所有代码都是没有副作用的(即都可以tree shaking),此时import css文件可能会导致tree shaking。
可以指定哪些文件有副作用,如sideEffects: ['*.css']
2.4 多进程打包
多进程打包:进程启动大概消耗600ms,进程通信也有开销,只有工作时间较长,才需要。
1 2 3 4 5 6 7 8 9 10 11 12 13
| module.exports = { module: { rules: [ { test: /\.js$/, use: [ 'thread-loader', 'babel-loader' ] } ] } }
|
2.5 code split
代码分割。
(1) 多入口文件分割
打包后自动分割,一个entry对应一个代码文件。
1 2 3 4 5 6
| module.exports = { entry: { index: 'src/index.js', test: 'src/test.js' } }
|
(2) 分割node_modules中公共依赖
1 2 3 4 5 6 7
| module.exports = { optimization: { splitChunks: { chunks: 'all' } } }
|
(3) 分割指定文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
import( 'x.js' ).then()
module.exports = { optimization: { splitChunks: { chunks: 'all', }, runtimeChunk: { name: entrypoint => `runtime~${entrypoint.name}` } } }
|
2.6 JS懒加载和预加载
正常加载:并行加载
懒加载:使用时才加载
预加载:等其他资源加载完,浏览器利用空闲时间加载资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
div.onclick = function(){ import( 'x.js' ).then() }
div.onclick = function(){ import( 'x.js' ).then() }
|
2.7 PWA
PWA:渐进式网络应用程序,即离线可访问。主要技术是service worker + cache。
service worker 必须运行在服务器上。
1
| npm i workbox-webpack-plugin -D
|
1 2 3 4 5 6 7 8 9 10 11
| module.exports = { plugins: [ new WorkboxWebpackPlugin.GenerateSW({ skipWaiting: true, clientsClaim: true }) ] }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('service-worker.js') .then(() => { console.log('sw注册成功') }) .catch(() => { console.log('sw注册失败') }) }) }
|
2.8 externals
当某些资源不要打包并且需要通过CDN引入时,可通过externals排除某些包
1 2 3 4 5
| module.exports = { externals: { jquery: 'jQuery' } }
|
2.9 DLL(动态链接库)
单独打包库文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
const { resolve } = require('path') const webpack = require('webpack')
module.exports = { mode: 'production', entry: { jquery: ['jquery'] }, output: { path: resolve(__dirname, 'dll'), filename: '[name].js', library: '[name]_[hash]' }, plugins: [ new webpack.DllPlugin({ name: '[name]_[hash]', path: resolve(__dirname, 'dll/manifest.json') }) ] }
|
1 2 3 4 5
| { "script": { "dll": "webpack --config webpack.dll.js" } }
|
1
| npm i add-asset-html-webpack-plugin -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
module.exports = { plugins: [ new webpack.DllReferencePlugin({ manifest: resolve(__dirname, 'dll/manifest.json') }), new AddAssetHtmlWebpackPlugin({ filepath: resolve(__dirname, 'dll/jquery.js'), outputPath: 'libs', publicPath: './libs' }) ] }
|
六、其他配置
1. entry
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
module.exports = { entry: 'index.js' }
module.exports = { entry: ['index.js', 'index2.js'] }
module.exports = { entry: { index: 'index.js', index2: ['index2.js', 'index3.js'] } }
|
2. output
- publicPath:所有资源引入的公共路径前缀
- chunkFilename:非入口chunk的名称(code split 分割出来的文件命名)
- library:整个文件向外暴露的变量名
- libraryTarget:变量名添加到那个对象上,‘window’ | ‘global’ | ‘commonjs’…
3. module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = { module: { rules: [ { test: /\./, use: [], loader: '', options: {}, exclude: /node_modules/, include: 'src', enforce: 'pre' | 'post', oneOf: [] } ] } }
|
4. resolve
1 2 3 4 5 6 7 8 9
| module.exports = { resolve: { alias: { @: 'src' }, extensions: ['.js'], modules: ['node_modules'] } }
|
5. resolveLoader
1 2 3 4 5
| module.exports = { resolveLoader: { modules: ['node_modules'] } }
|
6. devServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| module.exports = { devServer: { proxy: { before(app) { app.get("/user", (req, res) => {}) }, "/admin": "http://localhost:3000", "/api": { target: "http://localhost:3000", pathRewrite: { "^/api": "" } } }, watchContentBase: true, watchOptions: { ignored: /node_modules/ }, clientLogLevel: 'none', quiet: true, overlay: false, } }
|
7. optimization
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| module.exports = { optimization: { splitChunks: { chunks: 'all', minSize: 30 * 1024, maxSize: 0, minChunks: 1, cacheGroups: { vendors: { name: 'chunk-vendors', test: /[\\/]node_modules[\\/]/, priority: -10, chunks: 'initial' }, common: { name: 'chunk-common', minChunks: 2, priority: -20, chunks: 'initial', reuseExistingChunk: true } } }, minimizer: [ new TerserWebpackPlugin({ cache: true, paraller: true, sourceMa: true }) ] } }
|