webpack的基本使用

为什么要使用webpack

  • 模块化、将复杂的程序细化为小的文件
  • 对于TS,vue,react,以及一些高级ES语法,webpack能够将其转换为浏览器能识别的代码
  • 对于scss,less等CSS预处理器也能转化
  • 模块热更新
  • 配置代理解决跨域
  • 就算是vue,react等脚手架中也是封装了webpack来进行使用

配置

1
2
3
4
5
6
7
//package.json
"scripts":{
"build":"webpack --config prod.config.js"
}

//快速生成配置文件
npx webpack-cli init

入口和上下文

1
2
3
4
5
6
const path = require('path');
module.exports = {
context:path.resolve(__dirname,'app'),
entry:'文件路径' //单个入口
entry:['./a.js','./b.js']
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//对象写法,可扩展
module.exports = {
entry:{
app:'./src/app.js'
adminApp:'./src/adminApp.js',
}
}

//多页面
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js',
},
};

模式

提供mode配置选项,告知webpack使用相应模式的内置优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = {
mode:'development'|'production'|'none'
}
//如果要根据 webpack.config.js 中的 mode 变量更改打包行为,则必须将配置导出为函数,而不是导出对象:
var config = {
entry: './app.js',
//...
};

module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}

if (argv.mode === 'production') {
//...
}

return config;
};

输出

可以存在多个entry起点,只能指定一个output配置

1
2
3
4
5
6
module.exports = {
output:{
filename:'build.js',
path:path.resolve(__dirname,'dist') //所有输出文件的目标路径,必须是绝对路径
}
}

resolve

resolve.alias

创建import或require的别名,来确保模块引入变得更简单

1
2
3
4
5
6
7
8
9
10
11
const path = require('path');

module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/'),
},
},
};

resolve.extensions

1
2
3
4
5
6
7
module.exports = {
//...
resolve: {
extensions: ['.js', '.json', '.wasm'],
},
};
//能够使用户在引入模块时不带扩展

loader

用于对模块的源代码进行转换,可以处理less,scss等

内联方式(不推荐使用)

1
import 'css-loader! ../css/login.css'

配置方式(推荐使用)

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
//加入下面这部分
module:{
rules:[
{
test:/\.css$/, //一般为正则表达式,匹配我们需要处理的文件类型
use:[
{loader:'css-loader'}
]
},
//第二种方式
{
test:/\.css$/,
loader:'css-loader' //简写
}
//第三种方式
{
test:/\.css$/,
use:['css-loader']
//如要添加其他的loader,因为是从右向左执行的
use:['style-loader','css-loader']
//less-loader的使用,由于less-loader本身也需要用到less,所以需要先安装less
use:['style-loader','css-loader','less-loader']
}
]
}

常用loader

  • babel-loader:使用Babel加载ES高级代码将其转化为ES5
  • ts-loader:像加载JS一样加载TS
  • style-loader:将模块导出的内容作为样式添加到DOM中
  • css-loader:加载CSS文件并解析import的CSS文件,最终返回CSS代码
  • less-loader:加载并编译LESS文件
  • sass-loader:加载并编译SASS/SCSS文件
  • postcss-loader:使用postCSS加载并转换css文件
  • stylus-loader:加载并编译Stylus文件
  • vue-loader:加载并编译Vue组件

postcss-loader

安装

1
2
npm i postcss-loader -D
npm i postcss-cli -D //命令行操作

使用postcss在css-loader之前进行工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
test:/\.css$/,
use:[
'style-loader',
'css-loader',
{
loader:'postcss-loader',
options:{
postcssOptions:{
plugins:[
require('autoprefixer') //需要确保自己安装过前缀插件
]
}
}
}
]
}

在真正开发过程中,也可以直接使用集成的postcss-preset-env插件

1
npm i postcss-preset-env -D

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
test:/\.css$/,
use:[
'style-loader',
'css-loader',
{
loader:'postcss-loader',
options:{
postcssOptions:{
plugins:[
<!-- require('autoprefixer'), //需要确保自己安装过前缀插件
require('postcss-preset-env'),//或者'postcss-preset-env' -->
'postcss-preset-env' //由于它本身就包含autoprefixer
]
}
}
}
]
}

对于less文件的配置,如果也需要使用postcss-preset-env,又要进行相同的配置

为了防止代码的冗余,可以将这些配置信息写到另一个postcss.config.js文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//postcss.config.js
module.exports = {
plugins:[
require('postcss-preset-env')
]
}
//在webpack中
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}

需要注意的问题

1
2
3
4
5
6
7
8
当在一个css文件中import另一个文件时
01.login.css @import test.css
02.login.css可以被匹配,当它被匹配之后就是postcss-loader进行工作
03.基于当前代码,post-loader拿到了login.css当中的代码之后分析不需要额外的处理
04.于是代码转给了css-loader
05.此时css-loader可以处理@import media url...
这时候拿到了test.css文件,但它并不能进行一些兼容性处理也不能回头找postcss-loader
06.最终将处理好的css代码交给style-loader进行展示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
importLoaders:1 //往前找一个loader
}
},
'postcss-loader'
]
}
]
}

babel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//安装
npm install -D babel-loader @babel/core @babel/preset-env webpack
//配置
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/, //exclude避免转译第三方库的代码
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env',{target:'chrome 91'}], //如果指定了target将会忽视browserlistsrc文件中需要适配的场景
plugins: ['@babel/plugin-proposal-object-rest-spread']
}
}
}
]
}
//也可以创建babel.config.js文件进行配置
module.exports = {
preset:['@babel/preset-env']
}
//webpack
use:['babel-loader']

polyfill

babel:可以将ES6转换为ES5代码,而不转换新的API,比如可以转换箭头函数,但是不能转换Array.of

作用:用于实现浏览器并不支持的原生API代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//安装
//在webpack5中polyfill被拆成了两个部分,可以按需配置
//core-js 包含ES核心功能
//regenerator-runtime 填充

module.exports = {
presets:[
[
'@babel/preset-env',
{
//false:不对当前的JS处理做polyfill的填充
//usage:根据用于原代码当中所使用到的新语法进行填充
//entry:依据目前要兼容的浏览器进行填充
useBuiltIns:'usage',
corejs:3 //指定corejs版本
}
]
]
}

打包图片/字体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
test:/\.(png|svg|gif|jpe?g)$/,
use:[
{
loader:'file-loader',
options:{
name:'img/[name].[hash:6].[ext]', //打包后的名称
// outputPath:'img', //打包输出目录
}
}
]
}
//这是用file-loader的方式打包图片
// [ext]:扩展名
// [name]:文件名
// [hash]:文件内容
// [contentHash]:
// [hash:<length>]
// [path]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
npm i url-loader -D
{
test:/\.(png|svg|gif|jpe?g)$/,
use:[
{
loader:'url-loader',
options:{
name:'img/[name].[hash:6].[ext]', //打包后的名称
// outputPath:'img', //打包输出目录
limit:25 * 1024 //超过25KB做拷贝,否则就转base64
}
}
]
}

上面两种打包方式的关系

01.url-loader 把图片以base64 uri的方式加到文件中,减少请求次数
02.file-lodaer 将资源拷贝至指定的目录,分开请求
03.url-loader 内部其实也可以调用 file-loader
04.limit 决定调用file-loader 还是 url-loader

1
2
3
4
5
6
7
8
9
10
asset处理图片
使用asset module type 可以简化处理图片
这个模块在webpack5中内置,不需要额外安装
在webpack5中也放弃了url-loader,raw-loader,file-loader
新增了四种资源模块(asset modules)替代了这些loader的功能

01.asset/resource——>file-loader(输出路径)
02.asset/inline——>url-loader(输出为url(data:))
03.asset/source——>raw-loader(导出为源码source code)
04.asset(parser) //根据情况来进行处理
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
test:/\.(png|svg|gif|jpe?g)$/,
type:'asset/resource
}

如果想配置图片输出的目录以及名字
可以在更改output相应的配置,但是这样在打包字体的时候也会到配置目录
output:{
filename:'main.js',
path:path.resolve(__dirname,'dist'),
assetModuleFilename:"img/[name].[hash:4][ext]"
},

所以也可以单独配置
{
test:/\.(png|svg|gif|jpe?g)$/,
type:'asset/resource,
generator:{
filename:"img/[name].hash[4][ext]"
}
}
//asset/inline
{
test:/\.(png|svg|gif|jpe?g)$/,
type:'asset/inline
}
原理和url-loader差不多

//如果想要按照图片大小分别处理
{
test:/\.(png|svg|gif|jpe?g)$/,
type:'asset',
generator:{
filename:'img/[name].[hash:4][ext]'
},
parser:{
dataUrlCondition:{
maxSize:30*1024
}
}
}

//处理字体
{
test:/\.(ttf|woff2?)$/, //根据自己的字体来确认正则
type:'asset/resource,
generator:{
filename:'font/[name]/[hash:3][ext]'
}
}

plugin

插件的目的在于解决loader无法实现的其他事情

copy-webpack-plugin

作用:将单个文件或整个目录复制到构建目录

拷贝一些固定不变的文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//安装
npm i copy-webpack-plugin --save -dev
const CopyPlugin = require('copy-webpack-plugin')

module.exports = {
plugins:[
new CopyPlugin({
patterns:[
{
from:'public' //在这个目录下查找资源
globOptions:{
ignore:['**/index.html'] //可以忽略里面的文件
}
}
]
})
]
}

html-webpack-plugin

1
2
3
4
5
6
7
8
npm i html-webpack-plugin --save-dev

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins:[
new HtmlWebpackPlugin()
]
}

会在output目录下自动生成一个index.html

webpack-dev-server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
devServer:{
hot: true, //热更新
hotOnly: true, //组件数据不被另一个组件语法错误影响
port: 4000, //服务端口号
open: false, //不自动打开 ,也可以指定打开页面或浏览器
compress: true //压缩文件,提升性能
historyApiFallback: true //在404时跳转页面
//代理设置
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: { '^/api': '' },
changeOrigin: true, //不保留主机头的来源
},
}

devtool

控制是否生成,以及如何生成source-map

1
[inline-|hidden-|eval-][nosources-]|[cheap-[module-]]source-map

其中vue的错误处理方式是source-map

react的错误处理方式是cheap-module-source-map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
eval-source-map
map信息以base64的方式放入man.js中

inline-source-map
也是以base64塞到打包后的文件中
就会少一次请求

cheap-source-map
报错的时候只提供行信息,不提供列信息,减少消耗

cheap-module-source-map
报错的时候显示源代码,而不是打包兼容后的代码

hidden-source-map
无法定位到源文件

nosources-source-map
知道报错,无法定位到错误

target

往往采用browserslist配置来规定需要兼容的环境

可以在package.json文件中添加broswerslist属性

1
2
3
4
5
"broserslist":[
">1%",
"last 2 version",
"not dead"
]

也可以新建.browserlistrc文件

1
2
3
>1%
last 2 version
not dead

webpack的基本使用
https://blog-theta-ten.vercel.app/2021/08/25/webpack的基本使用/
作者
Chen
发布于
2021年8月25日
许可协议