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

一、前言

这个系列的学习笔记,前面的文章涉及知识点较多,内容篇幅较长,从这篇开始打算转变一下,缩短篇幅,划分出更多篇章方便学习。

二、Library 的打包

之前都是讲述如何对业务代码进行打包,这次我们来讲下当要开发一个组件库或者函数库的时候,这样的库代码应该如何通过 Webpack 进行打包。

初始化库

demo06 下的 library 作为项目文件夹,也就是 library 目录是项目根路径,接着从零开始创建每一个文件。

进入 library 目录,运行命令生成 package.json 文件。

1
npm init -y

对生成的 package.json 进行一些修改。

package.json
1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "library",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "phao",
"license": "MIT"
}

创建 src 目录,里面新增 math.js 进行加减乘除函数编写,新增 string.js 进行字符串拼接函数的编写,最后新增 index.js 引入 math.js 及 string.js 编写的内容,进行导出。

math.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export function add(a, b) {
return a + b
}

export function minus(a, b) {
return a - b
}

export function multiply(a, b) {
return a * b
}

export function division(a, b) {
return a / b
}
string.js
1
2
3
export function join(a, b) {
return a + ' ' + b
}
index.js
1
2
3
4
import * as math from './math'
import * as string from './String'

export default { math, string }

这就是一个简单的函数库,提供了加减乘除以及字符串的处理。这个库如果直接提供给浏览器肯定是运行不了这段代码的,所以我们还需要 Webpack 来进行一个打包处理。

安装 webpack 以及 webpack-cli

1
npm install webpack@4.27.1 webpack-cli@3.1.2 -D

项目根路径下创建 webpack.config.js 作为打包的配置文件。

webpack.config.js
1
2
3
4
5
6
7
8
9
10
const path = require('path')

module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'library.js'
}
}

这时候我们项目结构为

项目根路径下运行打包命令

1
npm run build

打包成功,生成 dist 目录下 library.js,文件大小为 1.31kb。

一般来说,业务代码的打包到这里就结束了,但现在要写的是一个库,需要提供给别人使用。

库的配置

别人引入这个库可能会采用各种形式:

  • ES Module
    1
    import library from 'library'
  • CommonJS
    1
    const library = require('library')
  • AMD
    1
    require(['library'], function(){})

如果想要编写的库支持各种外部的引用,可以设置打包配置文件里 output 的 libraryTarget。

webpack.config.js
1
2
3
4
5
6
7
8
9
10
...
module.exports = {
...
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'library.js',
// 通过任何形式去引入这个库,都能够得到正确的引用。
libraryTarget: 'umd'
}
}

有时候我们还希望通过在 html 文件内插入脚本去引入,引入后通过 library 这个全局变量去使用这个库。

1
<script src="library.js"></script>

需要设置打包配置文件里 output 的 library。

webpack.config.js
1
2
3
4
5
6
7
8
9
10
...
module.exports = {
...
output: {
...
// 把打包生成的代码挂载到页面的全局变量 'library' 上。
library: 'library',
libraryTarget: 'umd'
}
}

运行打包命令,观察打包结果,生成的 library.js 从 1.31kb 变成 1.54kb。

在 dist 目录下创建 index.html 引入 library.js 进行测试。

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>

<script src="library.js"></script>
<script>
console.log(library)
</script>
</body>
</html>

index.html 放入浏览器运行,控制台能够打印出 library 的内容。

当我们的库需要用到第三方的库,例如 lodash。

安装 lodash

1
npm install lodash@4.17.11 --save

string.js 引入 lodash 并使用。

string.js
1
2
3
4
5
import _ from 'lodash'

export function join(a, b) {
return _.join([a, b], ' ')
}

运行打包命令,打包成功,dist 目录下 library.js 的文件大小从 1.54kb 变成 72.5kb。

当别人在使用我们的库时,有可能也会去引入 lodash 这个库,导致别人的代码里存在两份 lodash,这时候我们可以去进行一些配置。

webpack.config.js
1
2
3
4
5
6
7
8
9
...
module.exports = {
...
// 在打包库代码时遇到 lodash 这个库,忽略它,不要打包到代码里面去。
externals: ['lodash'],
output: {
...
}
}

运行打包命令,打包成功,dist 目录下 library.js 的文件大小从 72.5kb 变成 1.64kb。

externals 这个配置项还有其他写法(参考)。

1
2
3
4
5
6
7
8
9
10
11
12
externals: {
lodash: {
// library 这个库用到的 lodash,
// 需要别人在使用 library 的时候,同时通过 script 标签引入 lodash,在页面注入一个全局变量 _ 。
root: '_',
// library 这个库用到的 lodash,
// 别人在使用 library 的时候,通过 CommonJs 规范去引入 lodash 时,需指定引入名为 lodash。
// 错误的写法:const _ = require('lodash')
// 正确的写法:const lodash = require('lodash')
commonjs: 'lodash'
}
}

没有特殊需求直接 externals: ['lodash'] 即可。

配置完后,别人在使用我们的 library 库的同时,需要去引入 lodash 这个库才能正常使用。

最后希望提供给别人使用的是打包生成 dist 目录下的 library.js,设置下 package.json 的入口 main。

package.json
1
2
3
4
5
{
...
"main": "./dist/library.js",
...
}

注意

例如本案例中,当别人使用我们的 library 库,别人引入的 lodash 库版本和 library 库需要的 lodash 版本不一致,甚至出现 API 变更等情况,这时候就不能通过 externals 的设置把 lodash 库从 library 的打包中进行忽略,我们的 library 库需要打包一份 lodash 这个库。

发布到 npm

在完成库的开发后,可以去 npm 官网 上注册一个账号进行登录。

当有了自己的账号后,在项目根路径下运行命令 npm adduser,这时候就会提醒你输入用户名以及密码。

添加完自己账号后运行 npm publish 直接把这个库发布到 npm 的仓库上。

当发布成功别人想使用这个库,直接 npm install library 就可以了。

当然,每个库名是不可以重复的,像 library 这种太平常的名字,在运行 npm publish 肯定会报错,因为和线上的库重名了。

这里就不做具体的步骤演示了,因为我们这个库其实没有任何意义,打包一个库也没有那么简单,真正写一个库代码的打包文件需要涉及的知识非常多,该案例只做简单了解。

注意

在运行命令 npm publish 有时会出现403的报错,这可能是因为用户邮箱没有进行验证。

三、PWA 的打包配置

PWA 是什么?

PWA 的全称是 Progressive Web App,渐进式 Web 应用,它不单指一种技术,也是一种思想和概念。它的目的是通过一系列的 Web 技术去优化网页应用,提升其安全性、性能、流畅性、用户体验等指标,最后达到用户使用网页应用就像用原生 App 一样的效果。

项目结构

我们需要结合一个案例来继续下面的讲述,以 demo06 下的 pwa 作为项目文件夹,也就是 pwa 目录是项目根路径,各文件直接拿之前项目 demo05 的,接下来做些修改。

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

安装依赖

1
npm ci

运行生产环境打包命令

1
npm run build

打包成功,dist 目录下生成相应文件。

PWA 的运用

下面我们来简单的使用一下 PWA 相关的技术。

首先安装一个启动服务的插件,帮助我们在 dist 目录下启动一个服务器去访问页面。

安装 http-server

1
npm install http-server@0.11.1 -D

新增 scripts 脚本命令 start

package.json
1
2
3
4
5
6
7
8
{
...
"scripts": {
...
"start": "http-server ./dist"
},
...
}

运行 start 命令启动服务器

1
npm run start

当服务启动成功,我们去谷歌浏览器访问 localhost:8080/index.html 就可以看到页面效果。

当我们把服务停了,页面也就不能正常访问了,这就是传统的网页。

PWA 可以实现用户第一次访问网页成功时,在本地进行一份缓存,当服务器突然之间挂掉,用户第二次重新访问该页面,可以直接用本地这份缓存把之前访问的页面展示出来。

Webpack 中有个第三方插件 workbox-webpack-plugin 可以实现这种 PWA 技术。

安装 workbox-webpack-plugin

1
npm install workbox-webpack-plugin@3.6.3 -D

只有上线的代码需要做 PWA 处理,对生产环境的打包配置文件进行该插件的使用。

webpack.prod.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
const WorkboxPlugin = require('workbox-webpack-plugin')

const prodConfig = {
...
plugins: [
...
// 实际上 PWA 底层用到的一个知识点叫做 serviceWorker,简写就是 SW。
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
})
],
...
}
...

接着来对业务代码增加一些内容

index.js
1
2
3
4
5
6
7
8
9
10
11
...
// 如果浏览器支持 serviceWorker,控制台打印结果。
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('service-worker registed')
}).catch(error => {
console.log('service-worker register error')
})
})
}

重新运行生产环境打包命令,打包成功后,观察 dist 目录下文件,会比上次打包多出两个文件。

通过这两个文件就能让 serviceWorker 生效,使这个项目达到 PWA 应用的要求,这里的 serviceWorker 也可以理解为一种另类的缓存。

再次运行 start 命令启动服务器,谷歌浏览器访问页面正常显示效果,控制台正常打印结果。

这时候,当我们把服务停了,也能继续访问该页面。

注意

  • 谨慎使用 serviceWorker,一旦用户访问过某页面一次,浏览器注册了 serviceWorker,以后我们再打包新代码到该页面使用,浏览器会拦截一些文件的请求,沿用之前缓存里的文件,导致页面内容无法更新。这份缓存并不会随着时间过期,除非用户手动去进行清除。
  • 本案例适合于那种做一个 Web 计算器之类的,没什么数据交互的页面。
  • 本案例我在尝试过程中,有时成功有时失败,原因暂时未知。

四、官方文档

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

建议阅读的内容为:
(1)文档 > 指南,页面左侧的 创建 library。
(2)文档 > 配置,页面左侧的 外部扩展(externals)。

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

五、总结

通过以上的学习,我们了解了下对库代码的打包以及 PWA 的运用,这两方面涉及的知识很多,这里只是简单的提及一下,感兴趣的小伙伴可以去网上搜索相关的文章及视频进行学习。

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

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

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