一篇搞懂Webpack常用的配置
1.认识 Webpack
Webpack 是什么?
官方的定义
Webpack
是一个用于现代 JavaScript
应用程序的 静态模块打包工具。当Webpack
处理应用程序时, 它会在内部从一个或多个入口点构建一个 依赖图(dependency graph
) 然后将你项目中所需的每一个模块组合成一个或多个 bundles
,它们均为静态资源,用于展示你的内容 简单的说
之前
在2010
年左右,前端当时还是利用 jQuery
进行开发 后端利用 php jsp
等技术将数据库的数据渲染到前端的页面上 前后端开始是耦合的 维护和开发 都是一个不规范 繁琐的流程 现在
当三大框架的横行后 逐渐以MVVM
(Model-View-ViewModel
)模式来 减少繁琐的 DOM
操作,以数据来驱动视图的变化,更加利于维护和开发 为什么使用
原生js
不会提供所有的特性, 因此就需要引入各种插件 而Webpack
就是大一统的集成方案
Webpack 可以做什么
使用Webpack
作为前端构建工具通常可以做到以下几个方面的事情
代码转换: TypeScript
编译成JavaScript
、SCSS
编译成CSS
等。 文件优化: 压缩JavaScript
、CSS
、HTML
代码,压缩合并图片等。 代码分割: 提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。 模块合并: 在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。 自动刷新: 监听本地源代码的变化,自动重新构建、刷新浏览器页面,通常叫做模块热替换HMR
。 代码校验: 在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。 自动发布: 更新完代码后,自动构建出线上发布代码并传输给发布系统。
2.搭建 Webpack
搭建基本环境
安装最新的环境, 新建一个文件夹叫 test
test
根路径下载基本的依赖
yarn add webpack webpack-cli --save-dev
yarn add webpack-dev-server -D
yarn add html-webpack-plugin --save-dev
在 test/package.json
文件添加启动和打包的 shell
命令
"scripts" : {
"serve" : "webpack serve " ,
"build" : "webpack"
} ,
# dist 目录不用手动创建 这个是打包自动生成的
| -- node_modules
├── dist
│ └── main. js
├── public
│ └── index. html
├── package . json
├── src
│ └── index. js
└── webpack. config. js
在 webpack.config.js 文件写入基本的配置
# 这些配置后面会具体解释 这里主要配置了打包的出口文件和入口文件
# webpack. config. js
const path = require ( 'path' )
module. exports = {
entry : './src/index.js' ,
output : {
filename : 'main.js' ,
path : path. resolve ( __dirname, 'dist' ) ,
} ,
mode : "development"
}
测试
此时就完成了基本的搭建,可以来使用shell语句测试一下效果了
在 /src/index.js
写入 console.log('Hello Webpck');
然后在项目根路径执行之前在package.json
定义的shell
命令 yarn build
就在在 /dist/main.js
发现转换后的代码了但是目前还是不能展示html页面 和 启动服务 在下一阶段就将安装两个必要的插件
( ( ) => {
var __webpack_modules__ = ( {
"./src/index.js" :
( ( ) => {
eval ( "console.log('Hello Webpck');\n\n//# sourceURL=webpack:///./src/index.js?" ) ;
} )
} ) ;
var __webpack_exports__ = { } ;
__webpack_modules__[ "./src/index.js" ] ( ) ;
} ) ( )
;
3.配置插件
导读
自己可以去npm
搜索各种loader
获取更多具体的配置 这里仅仅配置了我用过的常用配置 npm-loader 配置插件章节的项目最终结构
目录结构
.
├── dist
│ ├── index. html
│ ├── main. js
│ └── public
│ ├── assets
│ │ └── images
│ │ └── 99b6d6d0aac2e1ab068e. png
│ └── static
│ └── e7c3da3c784524f42673. html
├── node_modules
├── package . json
├── public
│ ├── avatar. png
│ └── index. html
├── src
│ ├── index. js
│ └── index. less
└── webpack. config. js
index.html
. / public / index. html
< body>
html5
< img id= 'im' src= "" alt= "" >
< ul>
< li> 1 < / li>
< li> 2 < / li>
< li> 3 < / li>
< / ul>
< / body>
index.js
# . / src/ index. js
import avatar from '../public/avatar.png'
import './index.less'
const im = document. getElementById ( 'im' )
im. src = avatar
console. log ( 'Hello Webpck' ) ;
const aaaa = ( ) => 10
console. log ( aaaa) ;
index.less
# ./src/index.less
@myColor: blue;
li{
color: @myColor;
}
webpack.config.js
# . / webpack. config. js
const path = require ( 'path' )
const HtmlWebpackPlugin = require ( 'html-webpack-plugin' )
module. exports = {
entry : './src/index.js' ,
output : {
filename : 'main.js' ,
path : path. resolve ( __dirname, 'dist' ) ,
assetModuleFilename : 'public/assets/images/[hash][ext][query]'
} ,
mode : "development" ,
devServer : {
compress : true ,
port : 8088 ,
open : true
} ,
plugins : [
new HtmlWebpackPlugin ( {
template : './public/index.html' ,
filename : 'index.html' ,
minify : {
collapseWhitespace : true ,
removeComments : true
}
} )
] ,
module : {
rules : [
{
test : / \.(?:ico|gif|png|jpg|jpeg)$ / i ,
type : 'asset/resource' ,
} ,
{
test : / \.svg / ,
type : 'asset/inline'
} ,
{
test : / \.txt / ,
type : 'asset' ,
parser : {
dataUrlCondition : {
maxSize : 4 * 1024
}
}
} ,
{
test : / \.js$ / ,
exclude : / (node_modules|bower_components) / ,
use : {
loader : 'babel-loader' ,
options : {
presets : [ '@babel/preset-env' ]
}
}
} ,
{
test : / \.css$ / ,
use : [
'style-loader' ,
'css-loader' ,
'postcss-loader'
]
} ,
{
test : / \.less$ / ,
use : [
'style-loader' ,
'css-loader' ,
'less-loader' ,
]
} ,
{
test : / \.scss$ / ,
use : [
'style-loader' ,
'css-loader' ,
'sass-loader'
]
}
] ,
} ,
}
package.json
# package . json
{
"scripts" : {
"serve" : "webpack serve" ,
"build" : "webpack"
} ,
"dependencies" : {
"html-webpack-plugin" : "^5.5.0" ,
"webpack" : "^5.69.0" ,
"webpack-cli" : "^4.9.2"
} ,
"devDependencies" : {
"@babel/core" : "^7.17.4" ,
"@babel/plugin-proposal-class-properties" : "^7.16.7" ,
"@babel/preset-env" : "^7.16.11" ,
"autoprefixer" : "^10.4.2" ,
"babel-loader" : "^8.2.3" ,
"css-loader" : "^6.6.0" ,
"less" : "^4.1.2" ,
"less-loader" : "^10.2.0" ,
"node-sass" : "^7.0.1" ,
"postcss-loader" : "^6.2.1" ,
"sass-loader" : "^12.6.0" ,
"style-loader" : "^3.3.1" ,
"webpack-dev-server" : "^4.7.4"
}
}
webpack-dev-server
这个插件其实就是一个小型的本地服务器 相关配置也比较简单 其他的具体配置后面再说 这时候就可以执行 yarn serve
此时还没有配置解析html的插件 因此先手动跳转到 main.js
测试下 http://localhost:8088/main.js
yarn add webpack- dev- server - D
# webpack. config. js
const path = require ( 'path' )
module. exports = {
...
devServer : {
compress : true ,
port : 8088 ,
open : true
}
}
html-webpack-plugin
这个包显然就是用来解析html的 配置后运行 yarn build
就可以在 dist
目录看到打包后的 index.html
了 同时运行 yarn serve
也可以直接运行解析了
yarn add html- webpack- plugin - D
# 在 . / public / index. html 随便写点东西
< body>
html5
< img id= 'im' src= "" alt= "" >
< / body>
# webpack. config. js
const path = require ( 'path' )
const HtmlWebpackPlugin = require ( 'html-webpack-plugin' )
module. exports = {
entry : './src/index.js' ,
output : {
filename : 'main.js' ,
path : path. resolve ( __dirname, 'dist' ) ,
} ,
mode : "development" ,
devServer : {
compress : true ,
port : 8088 ,
open : true
} ,
plugins : [
new HtmlWebpackPlugin ( {
template : './public/index.html' ,
filename : 'index.html' ,
minify : {
collapseWhitespace : true ,
removeComments : true
}
} )
]
}
此时我们完成了基本的架构,现在需要配置各种loader来满足项目的需要
Asset Modules
Asset Modules
是一种模块,它允许人们在不配置额外加载器的情况下使用资产文件(字体、图标等)webpack5
不需要再去手动下载三个loader
了在 webpack 5
之前,通常使用:
raw-loader
将文件作为字符串导入url-loader
将文件作为数据 URI 内联到包中file-loader
将文件发送到输出目录 Asset Modules
分为4中资源模块
asset/resource
将资源分割为单独的文件,并导出url
,就是之前的 file-loader
的功能asset/inline
将资源导出为dataURL(url(data:))
的形式,之前的 url-loader
的功能asset/source
将资源导出为源码(source code
). 之前的 raw-loader
功能asset
自动选择导出为单独文件或者 dataURL形式(默认为8KB)
. 之前有url-loader
设置asset size limit
限制实现。 当配置完成后运行 yarn build就会发现 图片也被打包进了dist目录
# 在 . / public 放一张图片 这里选择了avatar. png
# . / src/ index. js
import avatar from '../public/avatar.png'
const im = document. getElementById ( 'im' )
im. src = avatar
console. log ( 'Hello Webpck' ) ;
# webpack. config. js => module. exports. use
const path = require ( 'path' )
const HtmlWebpackPlugin = require ( 'html-webpack-plugin' )
module. exports = {
entry : './src/index.js' ,
output : {
filename : 'main.js' ,
path : path. resolve ( __dirname, 'dist' ) ,
assetModuleFilename : 'public/assets/images/[hash][ext][query]'
} ,
mode : "development" ,
devServer : {
compress : true ,
port : 8088 ,
open : true
} ,
plugins : [
new HtmlWebpackPlugin ( {
template : './public/index.html' ,
filename : 'index.html' ,
minify : {
collapseWhitespace : true ,
removeComments : true
}
} )
] ,
module : {
rules : [
{
test : / \.(?:ico|gif|png|jpg|jpeg)$ / i ,
type : 'asset/resource' ,
} ,
{
test : / \.svg / ,
type : 'asset/inline'
} ,
{
test : / \.txt / ,
type : 'asset' ,
parser : {
dataUrlCondition : {
maxSize : 4 * 1024
}
}
}
] ,
} ,
}
babel-loader
babel-loader
用于转化和识别高级语法当配置完成后运行 yarn build
可以查看 ./dist/main.js
后可以发现
代码已经将箭头函数转换成了es5
了 var aaaa = function aaaa() {\n return 10;\n};\n\nconsole.log(aaaa);
然后注销掉 babel-loader
的规则 重新打包 你会发现 main.js
中还是箭头函数 这就是 babel-loader
的作用之一
npm install - D babel- loader @babel/ core @babel/ preset- env
在 . / src/ index. js 加入一个 es6 箭头函数
const aaaa = ( ) => 10
console. log ( aaaa) ;
# webpack. config. js => module. exports. module. rules. xxx
{
test : / \.js$ / ,
exclude : / (node_modules|bower_components) / ,
use : {
loader : 'babel-loader' ,
options : {
presets : [ '@babel/preset-env' ]
}
}
} ,
css,sass,less,postcss loader
sass less
大家都知道是css
的预编译语言就不用多说了postcss
是用于自动添加css
的兼容前缀接下来就一次性安装多个loader
不过注意 node-sass
的上游依赖需要手动配置镜像或代理 否则一直可能下载失败 {% btn ‘https://www.ydyno.com/archives/1219.html’,node-sass配置镜像,far fa-hand-point-right,blue larger %} 配置完成后 就可以看到less-loader
生效了 其他的loader
,可以自己测试下,这里就不测试了
yarn add style- loader css- loader - D
yarn add less- loader less - D
yarn add sass- loader node- sass - D
yarn add postcss- loader autoprefixer - D
# . / public / index. html
ul> li{ $} * 3
# . / src/ index. js
import './index.less'
# . / src/ index. less
@myColor: blue;
li{
color : @myColor;
}
# webpack. config. js => module. exports. module. rules. xxx
{
test : / \.css$ / ,
use : [
'style-loader' ,
'css-loader' ,
'postcss-loader'
]
} ,
{
test : / \.less$ / ,
use : [
'style-loader' ,
'css-loader' ,
'less-loader' ,
]
} ,
{
test : / \.scss$ / ,
use : [
'style-loader' ,
'css-loader' ,
'sass-loader'
]
}
4. configuration
版本事项
以下的这些配置基于 webpack 4.x, 当使用webpack5 时可能有一些变动
webpack4.x webpack5
entry
# 新建目录src
默认入口 : . / src/ index. js
#
多入口打包
entry : {
'jquery' : './src/index.js' ,
'angular' : './src/2.js'
} ,
# 根目录创建webpack. config. js
module. exports = {
entry : "./src/main.js" ,
}
output
默认出口 : . / dist/ main. js
#
module. exports = {
output : {
path : path. join ( __dirname, "dist" ) ,
filename : "bundle.js" ,
publicPath : '/' ,
chunkFilename : 'js/[name]_chunk.js' ,
library : '[name]'
libraryTarget : 'window'
} ,
}
mode
module. exports = {
mode : 'development production'
}
devServer
module. exports = {
port : 5000 ,
host : 'localhost' ,
open : true ,
hot : true ,
proxy : {
'/api' : {
target : 'http://localhost:3000' ,
pathRewrite : {
'^/api' : ''
}
}
}
clientLogLevel : 'none' ,
quiet : true ,
overlay : false
compress : true ,
watchContentBase : true ,
watchOptions : {
ignored : / node_modules /
} ,
# 4.0 webpack
publicPath : "/assets/" ,
contentBase : resolve ( __dirname, 'dist' )
# 5.0 webpack
static : {
directory : path. join ( __dirname, 'assets' ) ,
publicPath : '/index.html' ,
}
}
module
noParse
module. exports = {
module : {
noParse : / jquery|lodash /
}
}
rules
module. exports = {
module : {
rules : [
{
test : / \.css$ / ,
use : [ 'style-loader' , 'css-loader' ]
} ,
{
test : / \.js$ /
exclude : / node_modules / ,
include : resolve ( __dirname, 'scr' ) ,
enforce : 'pre' ,
loader : 'eslint-loader' ,
options : { }
} ,
{
oneOf : [
{
}
]
}
]
}
}
oneOf
module. exports = {
module : {
rules : [
{
oneOf : [
{
test : / \.(jpg|png|gif) / ,
loader : 'url-loader' ,
options : {
limit : 8 * 1024 ,
name : '[hash:10].[ext]' ,
outputPath : 'imgs' ,
esModule : false
}
} ,
{
test : / \.html$ / ,
loader : 'html-loader'
} ,
{
exclude : / \.(js|css|less|html|jpg|png|gif) / ,
loader : 'file-loader' ,
options : {
outputPath : 'media'
}
}
]
}
]
}
}
resolve
alias
const path = require ( 'path' )
module. exports = {
resolve : {
alias : {
'@' : path. resolve ( __dirname, 'src' ) ,
'@/components' :
path. resolve ( __dirname, '..' , 'src/components' ) ,
'@/utils' :
path. resolve ( __dirname, '..' , 'src/utils' ) ,
'@/services' :
path. resolve ( __dirname, '..' , 'src/services' ) ,
'@/models' :
path. resolve ( __dirname, '..' , 'src/models' ) ,
'@/pages' :
path. resolve ( __dirname, '..' , 'src/pages' ) ,
}
}
}
extensions modules
module. exports = {
resolve : {
extensions : [ '.js' , '.json' , '.jsx' , '.css' ]
modules : [
resolve ( __dirname, '../../node_modules' ) ,
'node_modules'
]
}
}
plugins
module. exports = {
plugins : [
]
}
externals
module. exports = {
externals : {
jquery : 'jQuery'
} ,
plugins : [
new webpack. ProvidePlugin ( {
$ : "jquery" ,
} ) ,
]
}
Optimization
splitChunks
module. exports = {
optimization : {
splitChunks : {
chunks : 'all async'
} ,
} ,
}
devtool
source-map
module. exports = {
devtool : 'cheap-module-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:外部
错误代码准确信息 和 源代码的错误位置
module会将loader的source map加入
内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快
开发环境:速度快,调试更友好
速度快 ( eval> inline> cheap> ... )
eval- cheap- souce- map
eval- source- map
调试更友好
souce- map
cheap- module- souce- map
cheap- souce- map
-- > eval- source- map / eval- cheap- module- souce- map
生产环境:源代码要不要隐藏? 调试要不要更友好
内联会让代码体积变大,所以在生产环境不用内联
nosources- source- map 全部隐藏
hidden- source- map 只隐藏源代码,会提示构建后代码错误信息
-- > source- map / cheap- module- souce- map
4. demo
css抽离
plugins : [
new MiniCssExtractPlugin ( {
filename : 'css/built.css'
} ) ,
new OptimizeCssAssetsWebpackPlugin ( )
]
#
{
test : / \.css$ / ,
use : [ 'file-loader' ]
[ 'file-loader?name=[name].bundle[hash].css' ]
} ,
js抽离
output : {
filename : 'js/[name].js' ,
path : path. resolve ( __dirname, 'dist' ) ,
} ,
js语法检查
#
eslint eslint- loader
package . json 中添加
"eslintConfig" : { "extends" : "airbnb-base" }
#
module : {
rules : [
{
test : / \.js$ / ,
exclude : / node_modules / ,
loader : 'eslint-loader' ,
options : {
fix : true
}
}
]
}
代码切割
1. 多入口 自动打包多个文件
entry : {
'jquery' : './src/index.js' ,
'angular' : './src/2.js'
} ,
2. 异部的chunk
require. ensure ( [ ] , function ( _require ) {
_require ( './xxx' )
} )
import ( './2.css' ) . then ( ( ) => {
...
} )
}
#
3.
optimization : {
splitChunks : {
chunks : 'all'
}
}
#
externals : {
jquery : 'jQuery'
}
cdn
dll
#
硬链接一个 包, 再手动scrit引入这个包 之后webpack就不用编译了
#
yarn add add- asset- html- webpack- plugin
webpack -- config webpack. config. dll. js && webpack
# 文件路径
webpack. config. js
webpack. config. dll. js
dist
index. html
src
public
dll
jquery
manifest. json
# webpack. config. js
const path = require ( 'path' )
const webpack = require ( 'webpack' ) ;
const HtmlWebpackPlugin = require ( 'html-webpack-plugin' ) ;
const AddAssetHtmlPlugin = require ( 'add-asset-html-webpack-plugin' ) ;
module. export = {
...
externals : {
jquery : 'jQuery'
} ,
plugins : [
new HtmlWebpackPlugin ( {
template : './index.html' ,
filename : 'index.html' ,
minify : { removeComments : true }
} ) ,
new webpack. DllReferencePlugin ( {
manifest : resolve ( __dirname, 'dll/manifest.json' )
} ) ,
new AddAssetHtmlPlugin ( {
filepath : path. resolve ( __dirname, 'dll/jquery.js' ) ,
publicPath : '../dll' ,
outputPath : 'vendor' ,
} ) ,
]
}
# webpack. config. dll. js
const path = require ( 'path' ) ;
const webpack = require ( 'webpack' )
module. exports = {
entry : {
jquery : [ 'jquery' ] ,
} ,
output : {
filename : '[name].js' ,
path : path. resolve ( __dirname, 'dll' ) ,
library : '[name]_[hash]'
} ,
plugins : [
new webpack. DllPlugin ( {
name : '[name]_[hash]' ,
path : path. join ( __dirname, 'dll/manifest.json' ) ,
} )
] ,
mode : 'production'
} ;
externals : {
jquery : 'jQuery'
} ,
new webpack. ProvidePlugin ( {
$ : path. resolve ( path. join ( __dirname, 'dll/jquery.js' ) )
} ) ,