Webpack 4.0 学习笔记(七)
phao 在路上的前端开发

一、前言

项目结构

本文会以 demo07 作为项目文件夹,各文件直接拿之前项目 demo05 的,接下来做些修改。

package.json
1
2
3
4
{
"name": "demo07",
...
}
package-lock.json
1
2
3
4
{
"name": "demo07",
...
}
webpack.common.js
1
2
3
4
5
6
7
8
9
10
11
12
...
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
...
title: 'demo07自定义title'
}),
...
],
...
}

安装依赖

1
npm ci

运行开发环境启动服务命令

1
npm run dev

服务启动成功,浏览器打开 http://localhost:8080/ 页面正常显示。

二、WebpackDevServer 实现请求转发

安装 axios 进行请求发送

1
npm install axios@0.18.0 --save

在业务代码中引入 axios 去发送一个请求,发送请求的地址是网上随便找的天气 API。

index.js
1
2
3
4
5
6
...
import axios from 'axios'

axios.get('http://wthrcdn.etouch.cn/weather_mini?city=北京').then(res => {
console.log(res)
})

运行开发环境启动服务命令,控制台正常打印结果。

一般发送请求的地址会有测试以及正式环境的区分,所以不会写绝对路径,而是写相对路径。

index.js
1
2
3
4
...
axios.get('/weather_mini?city=北京').then(res => {
...
})

在本案例请求地址就会被拼接为 http://localhost:8080/weather_mini?city=北京,导致无法正常获取到数据。

我们可以设置 WebpackDevServer 的 proxy 配置项进行代理转发。

webpack.dev.js
1
2
3
4
5
6
7
8
9
10
11
12
13
...
const devConfig = {
...
devServer: {
...
proxy: {
// 代理转发,当我们请求 '/weather_mini' 这个地址,会代理到 'http://wthrcdn.etouch.cn' 服务器下。
'/weather_mini': 'http://wthrcdn.etouch.cn'
}
},
...
}
...

重新运行开发环境启动服务命令,浏览器打开 http://localhost:8080/,控制台打印结果并没有获取到刚刚的天气数据。

这是因为这些网站对 origin 做了限制,它会防止外部的爬虫去爬它的网站内容,所以有时候直接做代理会发现拿不到接口数据,需要通过配置 changeOrigin: true 来突破网站对 origin 的限制,建议做代理转发时始终带上该参数。

webpack.dev.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
const devConfig = {
...
devServer: {
...
proxy: {
'/weather_mini': {
changeOrigin: true,
target: 'http://wthrcdn.etouch.cn'
}
}
},
...
}
...

重新运行开发环境启动服务命令,这次控制台打印结果正常获取到天气 API 的数据。通过对 proxy 配置项的设置,在业务代码里只需请求 /weather_mini,就能够获取到 http://wthrcdn.etouch.cn/weather_mini 下的数据。

有时候开发过程中有这样一个场景,http://test.com/api/a 接口尚未开发完毕,后端提供了一个 http://test.com/api/b 的临时接口,如果在业务代码里进行请求地址的修改,后面要是忘了改过来去进行线上代码的打包会出问题,我们可以通过设置 WebpackDevServer 的 proxy 配置项来解决该问题,而不改动业务代码。

1
2
3
axios.get('/api/a').then(res => {
console.log(res)
})
1
2
3
4
5
6
7
8
9
proxy: {
'/api': {
changeOrigin: true,
target: 'http://test.com',
pathRewrite: {
'a': 'b'
}
}
}

这时候请求地址写 /api/a 不是去拿 a 下的数据,而是间接帮我们去取 b 下的数据,就可以获取到 http://test.com/api/b 下的数据。

其他场景

当请求的是 https 的地址,我们进行代理转发需要设置 secure: false

1
2
3
4
5
6
7
8
9
10
proxy: {
'/api': {
changeOrigin: true,
target: 'https://test.com',
secure: false,
pathRewrite: {
'a': 'b'
}
}
}

可以做一些拦截

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
proxy: {
'/api': {
changeOrigin: true,
target: 'https://test.com',
secure: false,
// 当发现请求的路径是 /api/xxx.html 这种对 html 文件的请求,
// 就会跳过代理转发的流程,直接返回项目根路径下 index.html 的内容。
bypass: function(req, res, proxyOptions) {
if (req.headers.accept.indexOf('html') !== -1) {
console.log('Skipping proxy for browser request.')
return '/index.html'
// 也可以返回一个 false,当请求的是 html 的地址,直接按原地址给你返回你想要的内容。
// return fasle
}
},
pathRewrite: {
'a': 'b'
}
}
}

可以在请求的时候修改请求头内容,去模拟一些登录、鉴权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
proxy: {
'/api': {
changeOrigin: true,
target: 'https://test.com',
secure: false,
pathRewrite: {
'a': 'b'
},
headers: {
// 请求头 host 修改为 aaa.com
host: 'aaa.com',
// 携带 cookie
cookie: 'abcd'
}
}
}

小结

注意的是,WebpackDevServer 的 proxy 只能方便我们在开发环境进行接口的调试,处理跨域的问题,生产环境无法使用。

proxy 有非常多的可选项,除了前往 Webpack 官网去查阅,它的底层实际上用到了 http-proxy-middleware,可以前往这个包的文档进行学习。

三、WebpackDevServer 解决单页面应用路由问题

一般在编写单页面应用时改变路由地址,服务器会认为你想要访问项目下其他资源,结果找不到对应的文件,导致页面展示出现问题,这时候我们可以对 WebpackDevServer 进行配置。

webpack.dev.js
1
2
3
4
5
6
7
8
9
10
11
...
const devConfig = {
...
devServer: {
// 访问任何页面路径都去到根路径下的 index.html,交给 index.html 内 js 的路由决定页面展示内容。
historyApiFallback: true,
...
},
...
}
...

还可以去编写一些规则

1
2
3
4
5
6
7
8
9
devServer: {
historyApiFallback: {
rewrites: [{
// 当访问的是 abc.html,实际上展现的页面是 index.html。
from: /abc.html/,
to: '/index.html'
}]
}
}

to 除了写成固定的字符串,还可以接受函数的形式,结合其 context 参数做一些 js 逻辑的处理来决定跳转到哪里。

最常见的还是做单页面应用开发设置 historyApiFallback: true

注意的是,因为 WebpackDevServer 是在开发环境才有的,historyApiFallback 只能在开发环境配置。

historyApiFallback 有非常多的可选项,除了前往 Webpack 官网去查阅,它的底层实际上用到了 connect-history-api-fallback,可以前往这个包的文档进行学习。

四、ESLint 在 Webpack 中的配置

安装 eslint

1
npm install eslint@5.12.0 -D

快速生成 ESLint 的配置文件

1
npx eslint --init

按着流程进行配置,主要就是选择了通用模板、Airbnb 规范以及以 js 文件的形式去生成配置文件。

项目根路径下会生成一个文件 .eslintrc.js。

.eslintrc.js
1
2
3
4
module.exports = {
// 当你需要对其他代码做检测,例如 React,需要去 ESLint 官网寻找相关配置。
"extends": "airbnb-base"
};

因为用的版本比较旧,有些用到的依赖需要我们手动安装下。

1
npm install eslint-config-airbnb@17.1.0 eslint-plugin-import@2.14.0 -D

接着进行配置,使用 babel-eslint 作为 ESLint 的解析器,不使用默认的。

1
npm install babel-eslint@10.0.1 -D
.eslintrc.js
1
2
3
4
5
module.exports = {
...
// 设置使用的解析器
"parser": "babel-eslint"
};

这时候就可以运行命令使用 ESLint 去检测 src 目录下的代码是否遵循规范。

1
npx eslint src

开发工具安装 ESLint 插件

还可以给开发工具安装 ESLint 相关插件,这些插件结合项目里的 .eslintrc.js 及对应功能去对代码进行分析,更方便定位错误。

例如 VSCode 安装 ESLint 插件

代码内直接出现报错提示

使用 eslint-loader

除了使用开发工具的 ESLint 插件,还可以把 ESLint 结合进 Webpack。

安装 eslint-loader

1
npm install eslint-loader@2.1.1 -D

打包 js 模块前用 eslint-loader 对代码进行一个检测。

webpack.common.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.js$/,
exclude: /node_modules/,
use: [
'babel-loader',
'eslint-loader'
]
}
]
},
...
}

运行开发环境启动服务命令

1
npm run dev

服务启动成功,控制台直接提示 ESLint 报错信息。

如果觉得控制台看报错信息不够清晰的,可以对 WebpackDevServer 进行配置。

webpack.dev.js
1
2
3
4
5
6
7
8
9
10
11
12
...
const devConfig = {
...
devServer: {
...
// 当运行 WebpackDevServer 打包的时候,一旦打包过程中出现问题,
// 就会直接在浏览器页面上弹层提示报错信息。
overlay: true
},
...
}
...

重新运行开发环境启动服务命令,服务启动成功后,直接在打开的浏览器页面提示报错信息。

处理报错信息

现在,我们来看下 ESLint 提示的报错信息,处理一下。

(1)

1
报错:Expected linebreaks to be 'LF' but found 'CRLF'

这个错误是因为操作系统的换行符不一致引起的,我们可以进行一些配置忽略该错误(具体的配置项可以参照报错信息右侧的错误类型)。

.eslintrc.js
1
2
3
4
5
6
7
module.exports = {
...
"rules": {
// 0(off),1(warn),2(error)
"linebreak-style": 0
}
};

(2)

1
报错:Missing semicolon

这个错误是因为编写的 js 代码结尾没加分号,这个我觉得没必要加,同样通过配置忽略该错误。

.eslintrc.js
1
2
3
4
5
6
7
module.exports = {
...
"rules": {
...
"semi": 0
}
};

(3)

1
报错:`axios` import should occur before import of `./jquery.ui`

这个错误是因为它认为引入的第三方库 axios 应该在我们编写的文件前引入。

我们调整下引入的顺序

index.js
1
2
3
import axios from 'axios'
import { ui } from './jquery.ui'
...

(4)

1
报错:'$' is not defined

这个错误是因为我们的业务代码里使用了 $,但是它没有找到 $ 定义的位置。

我们的 $ 在前面的文章中通过设置 Webpack 的打包配置,Webpack 在打包时会自动帮我们去引入相应模块,所以这里的报错我们同样选择忽略。

.eslintrc.js
1
2
3
4
5
6
7
module.exports = {
...
"rules": {
...
"no-undef": 0
}
};

(5)

1
报错:Expected parentheses around arrow function argument having a body with curly braces

这个错误是因为我们业务代码中箭头函数的参数列表没加括号。

index.js
1
2
3
4
5
...
// res 改为 (res)
axios.get('http://wthrcdn.etouch.cn/weather_mini?city=北京').then((res) => {
...
})

(6)

这时候 jquery.ui.js 里面还报了两个错误。

这同样是一些写法引起的问题,我们来修改下 jquery.ui.js。

jquery.ui.js
1
2
3
4
5
6
7
8
// 原写法
// export function ui () {
// $('body').css('background', 'green')
// }

export default function ui() {
$('body').css('background', 'green')
}

当 jquery.ui.js 内 export function 改为了 export default function,需要修改下 index.js 的引入方式。

index.js
1
2
3
4
...
// import { ui } from './jquery.ui'
import ui from './jquery.ui'
...

因为修改了 ESLint 的配置文件,需要重启下服务。现在,我们的代码写法就符合 ESLint 规范,没有报错提示。

拓展

还可以在打包配置文件里对 eslint-loader 进行一些其他设置(仅举例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'eslint-loader',
options: {
// 在 eslint 做代码检测的时候碰到代码中一些浅显的出错问题,它会自动把代码修复。
// 需要配合编辑器插件的使用
fix: true
},
// loader 的执行顺序是从后往前,从右往左。
// 如果这里的执行顺序先 babel-loader 再 eslint-loader 肯定是不对的,
// 通过该配置项强行让 eslint-loader 优于其他 loader 执行。
force: 'pre'
},
'babel-loader'
]
}
]

课程内提及,在一些项目开发中不会去使用 eslint-loader,因为它需要在打包前检测代码规范,不可避免地导致打包效率下降,这时会采用把代码提交到团队的 git 仓库时,通过 git 的钩子去命令行运行 eslint src,当代码不符合规范,则禁止你把代码提交到 git 仓库去,但这样做也就意味着没有办法去实现浏览器弹层这种形式交互的代码提示。如果感觉对打包速度没有太大影响,或者不在意,直接使用 eslint-loader 即可。

当发现 build 目录下的 Webpack 配置文件以及项目下其他配置文件也报 ESLint 的错误提示,可以在项目根路径下新建文件 .eslintignore 进行配置,让 ESLint 忽略对这些目录及文件的内容检测。

.eslintignore
1
2
build
postcss.config.js

五、官方文档

学习完以上内容,可以前往 Webpack 4.0 官网 阅读相关中文文档。

建议阅读的内容为:
(1)文档 > 配置,页面左侧的 开发中 server(devServer) 内 proxy、historyApiFallback。
(2)文档 > LOADER,页面左侧的 eslint-loader。

英语比较好的小伙伴可以直接阅读 Webpack 4.0 官网 的英文文档。

对 WebpackDevServer 下 proxy 以及 historyApiFallback 底层实现的包感兴趣的,可以查看官方文档:
(3)http-proxy-middleware
(4)connect-history-api-fallback

(5)对 ESLint 配置项感兴趣的,可以前往 ESLint 官网 学习。

六、总结

通过以上的学习,我们知道了 WebpackDevServer 可以进行请求转发、解决本地跨域问题以及单页面应用路由的问题,并且了解了 ESLint 在 Webpack 中如何配置使用。

当在进行 Webpack 的配置以及是否运用一些插件时,我们不仅需要考虑使用起来是否方便,还要去考虑在打包过程中是否对打包速度的影响特别大。如果对打包速度影响比较大的话,也可以在便捷性上做些牺牲,从而提高打包的速度。

本文最后的代码我会上传到 码云(gitee.com/phao97)上,项目文件夹为 demo07。

如果觉得本篇文章对你有帮助,不妨点个赞或者给相关的 Git 仓库一个 Star,你的鼓励是我持续更新的动力!

  • 本文标题:Webpack 4.0 学习笔记(七)
  • 本文作者:phao
  • 创建时间:2022-05-17 11:23:50
  • 本文链接:http://phaode.cn/2022/05/17/Webpack-4-0-学习笔记(七)/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!