后端小白的Webpack学习笔记
文章目录
写在前面
本博文仅作为个人学习过程的记录,可能存在诸多错误,希望各位看官不吝赐教,支持错误所在,帮助小白成长!
七、模块化开发
随着前端工程的不断膨胀,使用原生开发方式会出现很多问题,例如:管理多个 JavaScript 文件时,全局变量命名冲突。
使用匿名函数+闭包可以解决多文件命名冲突问题,但是是降低代码的复用性!于是我们想到了使用 namespace(命名空间)
模块化中最重要的就是两个操作:导出与导入
ES5 中原生的模块化开发:可以通过匿名函数然后加上返回值,返回值中包括一个 JS 文件中需要导出的内容。然后文件内使用一个全局变量接收返回值,其他 JS 文件直接使用这个全局变量即可使用匿名函数内部的东西。
var moduleA = (function () {
var flag = true
function sum(a, b) {
return a + b
}
let obj = {}
obj.flag = flag
obj.sum = sum
return obj
})()
// 其他文件中使用:
if (moduleA.flag) {
moduleA.sum(40, 50)
}
早期开发,大家都只能这样写,而随着模块化需求的日渐强烈,业内也出现了很多关于模块化的规范:
常见的有:CommonJS
、AMD
、CMD
以及 ES6 的Modules
Node
就是 CommonJS 的最好的实现者!
CommonJS 的导入
// moduleA.js
var flag = true
function sum(a, b) { return a + b; }
module.exports(
flag: flag,
sum: sum
)
// 使用ES6 字面量可以简写为module.exports(flag, sum)
// module为Node中内置的模块,因此我们需要Node环境的支持
CommonJS 的导入
let moduleA = require('./moduleA.js')
// 使用moduleA中导出的内容
if (moduleA.flag) {
console.log(moduleA.sum(30, 20))
}
ES6 规范
ES6 规范下,我们在引用管理 JavaScript 文件时,需要加上type="module"
,此时每个 JavaScript 文件都会被视为一个独立的模块,模块之间的命名不会再发现冲突,每个模块都有独立的命名空间。
那也就意味着你想要使用其他模块的内容时,必须通过模块的导入和导出!
导出:
// 文件名: moduleA.js
// 导出方式一:
export { flag, sum }
// 导出方式二: 声明时导出
export var flag = falseexport
function abs(num) {
if (num < 0) {
num = -1 * num
}
return num
}
export class Person {
// ...
}
// export default 在导入时可以使用自定义名,但是一个模块中只能有一个export default
var address = '武汉市江夏区'
export default address
导入:(除了 default 导出,其他对应的导入时,都要保证与导出时变量名一致!!!!)
// 导入方式一:
import { flag, sum } from './moduleA.js'
// 导入方式二:导入单个
import { flag } from './moduleA.js'
// 作用export default, 我们可以自定义导入的数据名
import addr from './moduleA.js'
// 通配符导入
import * as moduleA from './moduleA.js'
console.log(moduleA.flag)
八、Webpack
8.1、初识 Webpack
什么是 Webpack
一个 JavaScript 静态模块打包工具
类似的工具还有:grunt
,gulp
我们在进行模块化开发时,可能会借助一些工具,即使不使用工具也会使用 ES6 的模块化方案。但是开发时使用的这些东西是无法被浏览器识别的,而 Webpack 的工作就是将他们处理转换为浏览器能识别的代码。
官方的这张图就展现了其功能:
8.2、webpack 安装
webpack 依赖 node 环境
node 安装与配置
官网下载安装后,使用node -v
和npm -v
,检查是否安装成功。
后续 npm 安装模块会默认安装到 C 盘,如果 C 盘空间不大可以改到其他地方,这里以转移到 node 安装的目录下作为演示:
-
在安装目录下创建两个文件夹📁
node_global、node_cache
-
使用 cmd 执行下列命令:(例如我的安装路径是 F:\Environment\Nodejs)
npm config set prefix "F:\Environment\Nodejs\node_global" npm config set cache "F:\Environment\Nodejs\node_cache"
-
在系统环境变量中创建【NODE_PATH】
设置变量值为: F:\Environment\Nodejs\node_global\node_modules
-
修改用户变量中的【Path】
默认是 C 盘用户目录下 AppData\Roaming\npm ,修改为:F:\Environment\Nodejs\node_global
注意:使用 npm 安装,要带上-g 选项,否则会安装在当前目录下。
全局安装 webpack (当前版本 webpack5、node:14.16.2、npm: 6.14.12) 官方并不推荐!更推荐使用本地安装
npm install webpack -gnpm install webpack-cli -g
8.3、Webpack 入门案例
初始准备工作
-
首先我们创建一个文件夹作为我们项目的根:webpack-demo。
-
然后我们先使用
npm init
为项目加入 npm 包管理工具。(此命令可以加上-y 选项使用默认 npm 配置)命令执行完成后会生成一个
package-info.json
文件,这就是 npm 用于管理我们项目依赖的文件。
(作用类似于 Java 项目中 Maven 管理项目的 pom.xml 文件) -
使用 npm 在项目本地安装 webpack、webpack-cli(用于在终端使用 webpack 命令)
npm install webpack webpack-cli --save-dev
-
然后在项目目录下创建一个
src
目录,作为我们开发文件的存放目录。 -
然后在项目根目录下创建一个
index.html
作为我们项目的主页面 -
在 src 目录下创建一个
index.js
文件,作为我们项目开发的第一个 js 文件:function component() { const element = document.createElement('div') // lodash(目前通过一个 script 引入)对于执行这一行是必需的 element.innerHTML = _.join(['Hello', 'webpack'], ' ') return element }
-
我们返回到 index.html 中用
<script>
标签手动导入所需的 js 文件<!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>Webpack-Demo</title> </head> <body> <!-- 使用script标签手动导入lodash --> <script src="https://unpkg.com/lodash@4.17.20"></script> <script src="../src/index.js"></script> </body> </html>
-
我们调整一下
package-info.json
, 删除其中的“main”入口,并设置安装包为 private:- "main": "index.js" + "private": true
现在的项目结构,就是我们所说的传统开发模式,通过在 html 中管理多个 script 文件。而多个 script 之间又存在隐式的依赖关系。(例如:index.js 需要 lodash,但是并没有显式声明,而是通过推测已经存在一个全局变量_
)这样就会出现一些问题:
- 倘若我并没有引入 lodash 或者引入顺序有问题,那么代码在执行时就会报错。(更糟的是可能出现命名冲突)
- 无法体现出脚本依赖于外部库
- 一些引用了但是没有使用的库,浏览器会被迫下载!
那么,就让 webpack 来管理这些脚本吧!
创建 Bundle
我们需要调整一下项目结构,在项目目录下增加一个dist
文件夹,作为存放我们分发代码的文件夹。
分发代码:是指开发编辑的代码经过最小会优化后得到的输出结果,可以直接运行在浏览器上!
经过调整前后的项目结构:
webpack-demo
|- package.json
+ |- /dist
+ |- index.html
- |- index.html
|- /src
|- index.js
Tip:将 index.html 手动移动到 dist 目录下,后续我们会选择自动生成 index.html。
-
使用 npm 下载 lodash 到项目的本地 library 中,进行统一管理
npm install --save lodash
Tip: 注意区分
--save
和--save-dev
,前者是将依赖加入生产环境依赖中(对应package-info.json
中的dependencies
),它意味着我们的项目运行需要这些环境支持。而后者是将其加入到开发环境依赖中(对应其中的devDependencies
)意味着它是我们开发过程中所需的依赖。很显然 lodash 是我们生产运行时所需的库,所以使用
--save
! -
在
index.js
使用 ES6 语法,显式导入 lodashimport _ from 'lodash' function component() { const element = document.createElement('div') // lodash 通过 import进行引入 element.innerHTML = _.join(['Hello', 'webpack'], ' ') return element } document.body.appendChild(component())
-
然后也需要修改 index.html 中的 script 标签:暂时先删除所有引入 script 标签。加入使用
./main.js
的 script 引用<!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>Webpack-Demo</title> </head> <body> <script src="./main.js"></script> </body> </html>
目前我们还没有这个 main.js 文件,而这个文件应该就是我们使用 webpack 后替我们打包的输出文件。
在使用 webpack 打包的时候,以 index.js 作为入口,因为其显式声明了对 lodash 的依赖,webpack 会通过这个依赖链为我们打包好所有的内容并优化输出,即 Bundle。
-
执行
npx webpack
。此命令会将我们的脚本
src/index.js
作为入口,也会生成dist/main.js
作为输出。Node 8.2/npm 5.2.0 以上版本提供的npx
命令,可以运行在初次安装的 webpack package 中的 webpack 二进制文件(即./node_modules/.bin/webpack
)然后我们的
dist
目录中就出现了这个 main.js(确认过代码,是我看不懂的~) -
此浏览器访问页面,恢复了显示。
Webpack 的打包过程就是帮我们管理依赖之间的引用,同时把浏览器无法识别的内容(例如模块的 import、export)通过一种通用的方式翻译了出来。(保证绝大部分浏览器都能解析显示)
配置文件
在一些复杂的项目中,我们需要一些复杂配置,使用一个配置文件比在命令中使用各种参数选项要更高效!
这个配置文件有一个通用的名字:webpack.config.js
,通常放在项目根目录下。
这是官方给出的基础配置:
const path = require('path')
module.exports = {
entry: './src/index.js',
output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') },
}
我们先来解读一下这个配置:
第一行导入的是一个node
的内置模块path
,后面使用的__dirname
的resolve
就是依赖这个模块。
然后看 module.exports 里面的内容:
-
entry
: 指定项目的入口,也即依赖链的顶端 -
output
: 指定 Bundle 的输出位置:-
filename
: 输出文件名。 -
path
: 输出文件的绝对路径!path.resolve(__dirname, 'dist')
表示先 cd 到__dirname
(即当前文件所在的目录),然后 cd 进dist
然后返回所在路径。
这样 path 和 filename 就可以指定一个输出位置了。
-
使用webpack --config ./webpack.config.js
指定配置文件并执行打包。
由于我们并没有在全局安装,所以此时 webpack 命令不能使用。因为我们在项目本地安装了 webpack,要想使用本地的 webpack 命令,我们需要在 npm script 中(即package-info.json
中的script
)进行配置。

script
中放置的是一个个可执行脚本:"脚本名": "script code"
。
我们在里面加上 webpack 打包的脚本代码,并命名为 build:
"build": "webpack --config webpack.config.js"
# 由于配置文件的默认名,所以可以省略,缩写为:
"build": "webpack"
然后运行npm run build
,就会利用本地 lib 下的 webpack 进行执行,这是大多数基于 npm 的项目遵循的标准。
8.4、资源管理之 Loader
8.4.1、样式文件管理
现在我们掌握了如何使用 webpack 来处理我们 JavaScript 代码中的依赖结构,但是我们的项目代码中往往不止有 JavaScript 代码。比如最常见的还有css
文件。所以 webpack 必须还具备处理 CSS 文件的能力。
但是首先要考虑,怎么让 webpack 能在打包的时候能扫描到 CSS 文件。如果我们在 src 下直接创建一个 CSS 文件,然后直接使用 webpack 进行打包,会发现 CSS 文件并没有被处理!
webpack 在管理资源文件时是通过一条依赖链,去扫描处理文件的,依赖链的入口则就是我们写在 webpack 配置文件中的
entry
。
由于我们的 CSS 文件并没有在一个依赖链中,所以不会被处理。那么我们需要在index.js
中使用 import 显式声明对 CSS 文件的依赖,将 CSS 文件加入到依赖链中!
import './style.css'
但是当我们运行打包命令时,它出错了:
这里我们的Loader
首次登场!
官方文档告诉我们需要两个 Loader:style-loader
和css-loader
。我们需要使用 npm 将他们安装到项目本地 lib 中。
npm install --save-dev style-loader css-loader
然后在 webpack 的配置文件中加入以下内容:
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: ['style-loader', 'css-loader'],
+ },
+ ],
+ },
};
先来解读一下,我们加入的内容:
rules:我们定义了一条规则{
-
test(一般用于条件判断):
/\.css$/i
这是一个正则表达式:最外层的
//i
表示忽略大小写,然后内部的\.css$
首先对特殊字符.
进行转义将其作为普通的字符,然后$
表示结尾位置。那么这个正则表达式匹配的就是所有结尾是.css
的内容,即我们的样式文件! -
use: 表示我们针对通过 test 筛选的文件,我们使用一下 loader 进行链式处理(逆序,从右向左)
}
然后确保入口 JavaScript 文件中已经显式引入了 CSS 文件,然后重新执行 build 脚本。然后重新打开页面,检视页面的 head 标签时,你会发现 style 标签内正是我是写在样式文件中的内容!这就是 webpack 为我们做的事情!
什么是 loader?
简单尝试后,我们再来回答这个问题。准确来说,它相当于是对 webpack 资源处理能力的一个扩展机制!基础的 webpack 只能处理模块化的 JavaScript 的文件,但是在面对复杂的项目时需要管理各式各样的资源文件,而这些 Loader 就可以扩展 Webpack,让其拥有处理它们的能力。
官方网站专门为 Loader 制作了一个管理页面:[Webpack-LOADER](Loaders | webpack 中文文档 (docschina.org))
CSS Loader 和 Style Loader 的使用注意事项
在项目中引用使用 Loader 时,需要在 Webpack 配置文件中加入 rule,其中的 use 则表示我们针对某类文件使用的 Loader,它可以链式调用,即上一个 Loader 处理完成后的结果然后交给下一个 Loader,所以loader 的顺序不能乱写!!
那 CSS 文件,处理的顺序就是 .css
文件 -> css-loader
->style-loader
。但是我们在写 use 数组的顺序时,最后用的 loader 要写在最前面!即['style-loader', 'css-loader']
。因为官方说这个链式调用是**逆序(从右向左)**的!
关于两个 loader 更高阶的用法请参考官网 Loader 页面。
8.4.2、图片文件管理(旧版)
以下例子在 webpack5 时,开始向
asset modules
迁移~~~
我们再利用一个例子来演示一下,如何通过官方文档来学习使用 Loader。
现在我们的项目的 src 目录下创建了一个 img 目录,并且其中有两张图片。
大小分别为 20kb(图片 A)、4kb(图片 B)。
假如我们在我们的样式文件中引用了图片 B 作为背景图片:
body {
background-image: url('./img/ubuntu.png');
}
可是当我们打包生成的时候,预料之内地提醒我们没有合适的 loader 来处理对这个图片的依赖。找了一圈官方的 Loader,最终只找到了url-loader
URL-Loader
功能类似于
file-loader
, 但是在文件大小(单位为字节)低于指定的限制时,可以返回一个 DataURL。
废话不多说,直接下载引入:
npm install --save-dev url-loader
webpack.config.js: rules 中加入以下内容:
{
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: { limit: 8192, }
}],
}
之前我们的 use 是使用字符串数组,而这里为了加上一些复杂的配置,所以选择使用了对象数组。
每个 Loader 用一个 Object 描述:{
loader
: 指定对应loader的名字
options
:针对此loader的一些高级配置(若干键值对)。(官网中每个Loader都有选项的说明)
}
这里的limit
选项就是 url-loader 作用描述中的文件大小限制,单位 b。那么也就是 8192b(即 8kb)一下的文件有效。其会直接将文件数据转换为一个含数据的 URL,如 Base64。
打包完成,看看效果。在检视代码的时候,果然我们的图片被转换为了 DataURL,即一个 Base64 字符串:
可是当我们换了图片 A 后,很明显它的大小是超出 limit 选项的限制的。那么我们就要使用前面提到和 url-loader 类似但是没有文件大小限制的file-loader
了
npm install --save-dev file-loader
webpack.config.js, rules: 使用如下配置,name
选项可以选择将将文件打包后输出到dist
目录中的指定文件夹(例码中是 dist 中 img 文件夹)中!同时保留文件的原始文件名和扩展名。
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[ext]',
},
},
],
},
注意:尽量避免 file-loader 和 url-loader 混合使用!
运行打包命令后,项目的依赖的图片(png、jpeg、jpg、gif)都会被统一打包生成放在 dist/img 目录下。
悄悄告诉你一个绝望的消息!~~
url-loader
、file-loader
以及raw-loader
~~即将在 webpack5 被弃用!!(好家伙!白忙活)官方推荐我们使用asset module
代替。👍
8.4.3、图片资源管理之 Asset MODULE
官方在 webpack.config.js 中的 rules 中加入以下内容:
+ {
+ test: /\.(png|svg|jpg|jpeg|gif)$/i,
+ type: 'asset/resource',
+ },
并加入以下说明:
现在,在 import MyImage from './my-image.png'
时,此图像将被处理并添加到 output
目录,并且 MyImage
变量将包含该图像在处理后的最终 url。在使用 css-loader 时,如前所示,会使用类似过程处理你的 CSS 中的 url('./my-image.png')
。loader 会识别这是一个本地文件,并将 './my-image.png'
路径,替换为 output
目录中图像的最终路径。而 html-loader 以相同的方式处理 <img src="./my-image.png" />
。
简单来说就是:
当你在 JavaScript 文件使用 import 导入图片资源时,或者使用 css-loader,CSS 文件中使用 url 对图片的引用,以及使用 html-loader,img 标签中的 src 对图片的引用。这些图片都会被统一输出到 ouput 目录,并且将原路径替换为最终的输出路径!
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现。asset/source
导出资源的源代码。之前通过使用raw-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现。
Resource 资源-自定义输出名
当我们使用asset/resource
代替 file-loader 时,我们迫切需要自定义资源文件的输出位置!默认是以 [hash][ext][query]
文件名发送到输出目录。
在 webpack.config.js 中使用output.assetModuleFilename
来指定输出的文件路径及文件名。
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'images/[hash][ext][query]'
},
同时,当你的资源分为多种的时候,可以单独为某一种或几种资源设置独立的输出目录:
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
},
{
test: /\.html/,
type: 'asset/resource',
generator: {
filename: 'static/[hash][ext][query]'
}
}
]
},
例如上述配置,html 文件会被设置发送到输出目录下的 static 目录,而 png 则是使用默认的输出位置。
代码中的 hash
、ext
、都属于模板字符串。
更多详细查看:输出(output)-Template-string | webpack 中文文档 (docschina.org)
对于文件来说常见的有:
模板 | 描述 |
---|---|
[file] | filename 和路径,不含 query 或 fragment |
[query] | 带前缀 ? 的 query |
[fragment] | 带前缀 # 的 fragment |
[base] | 只有 filename(包含扩展名),不含 path |
[filebase] | 同上,但已弃用 |
[path] | 只有 path,不含 filename |
[name] | 只有 filename,不含扩展名或 path |
[ext] | 带前缀 . 的扩展名 |
使用这种模板式的动态生成名,可以很好地控制生成重复文件的问题!
8.5、Webpack 使用 Vue
首先,利用 npm 安装 vue 到本地运行环境(因为 Vue 是我们运行时所需的环境,所以使用-save
)
npm install --save vue
然后在 JavaScript 文件中进行引入:(我们下载 Vue 中已经 export Vue 了)
import Vue from 'vue'
完成后,我们可以在 JavaScript 文件中正常写 Vue 代码,然后对应 Html 文件中,创建挂载的标签。然后进行打包构建。打包成功,可是当我们打开网页发现,内容并没有被渲染上去?!
这是由于我们默认引入的 Vue from ‘vue’,并不是标准的带编译版本。我们需要在 webpack.config.js 中为‘vue’创建别名,指向
vue.esm.js
。在本地 lib 包的 vue 中的 dist 文件夹下,可以看出其分发了很多版本:![]()
在 webpack.config.js 中加入以下代码:
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [...],
},
+ resolve: {
+ alias: {
+ vue$: 'vue/dist/vue.esm.js',
+ },
+ },
};
然后重新打包,就可以正常使用了!!
8.6、Webpack 使用 Vue(进阶)
他来了他来了,他带着 vue 扩展名走来了!我们前面在学习 Vue 的时候,我们都是写在 script 标签内,或者写在 JavaScript 文件中。但是这样的写法在我们前端模块化以后,可能就不太合适了。我们不希望我们去频繁修改我们的 index.html,并且在 JavaScript 文件中又不便于做组件的管理(template 难写!)。所以就出现了一个文件格式:.vue
其文件内容主要由三部分组成:
<template><!--组件模板--></template>
<script>
// js代码
</script>
<style>
/*样式列表*/
</style>
这一个文件就可以完成对一个组件的定义。想要在外部使用此组件,只需要在 script 代码块中使用 export default 将组件导出即可,其他文件要使用时,使用import
导入对应文件的组件进行注册使用即可。
例如,这里有一个 App.vue:
// App.vue
<template>
<div>
<h3>{{ message }}</h3>
<button @click="showMessage">show message</button>
</div>
</template>
<script>
export default {
data() {
return { message: 'Hello Vue' }
},
methods: {
showMessage() {
console.log(this.message)
},
},
}
</script>
<style></style>
如果在 index.js 我们要使用此组件:
// 1. 导入App
import App from './App.vue';
const app = new Vue({ el: '#app',
// 3. 使用
template: `<app-component/>`,
components: {
// 2. 注册组件
'app-component': App,
},
});
你可能注意到我在实例中同时使用了 el 和 template,最终的效果是template 中的内容,最终会替换掉 Vue 实例挂载的元素。
此时 index.html:
<body>
<div id="app"></div>
<script src="./bundle.js"></script>
</body>
现在重要的东西来了,我们的 vue 文件是需要进行打包的,但是 webpack 肯定不认识这个类型的文件,所以我们需要 Loader,也本节的主角vue-loader
。
官方文档在这:介绍 | Vue Loader (vuejs.org)
安装
vue-loader
、vue-template-compiler
npm install -D vue-loader vue-template-compiler# -D等效于 --save-dev、 -S等效于 --save
Webpack 配置
这里除了要配置 vue-loader 外,还需要引入一个 Vue Loader Plugin,以确保你定义过的其他规则(rule)可以被应用到 vue 文件的相应的语言块(一个 vue 文件,包含了 js、css、html 代码)。这个插件是必须的!!
例如你针对.css 文件、.html 文件定义了 rule 使用 loader,那么这个插件就要保证这些 loader 也被用于处理 vue 文件中的对应代码块。
// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
module: {
rules: [
// ... 其它规则
{ test: /\.vue$/, loader: 'vue-loader' },
],
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
],
}
配置完成后,打包运行即可。
如果还需要更深度的定制,可以参考使用 vue-loader 的选项:选项参考 | Vue Loader (vuejs.org)
8.7、HtmlWebpackPlugin 以及清理/dist
目前,我们的项目使用的index.html
都是我们手动创建的,并且里面的打包 JS 文件也是我手动进行管理的。当项目逐渐庞大,我们开始使用 JS 文件名输出多个 bundle 时,继续手动管理,就显得捉襟见肘。
案例场景:
现在项目文件夹中有两个 js 文件:index.js
,print.js
。并且 index.js 引用了 print.js,但是我们通过配置 webpack.config.js,为其设置了多个文件入口,并且通过字符串模板,告诉他我们希望为每个入口,单独输出一个 bundle:
// webpack.config.js
module.exports = {
entry: { index: './src/index.js', print: './src/print.js' },
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
// 其他内容,略
}
现在,我们执行 build 脚本后,非但旧的分发文件没有删除,index.html 中的 script 依赖还得我们手动去改!
这么点文件弄起来就已经够恼火了,项目大了,输出的 bundle 多了还不得上天!?
其实官方早已想到了这个问题,推荐我们使用HtmlWebpackPlugin
来解决这个麻烦!
这个插件可以帮助我们自动生成 index.html(支持自定义模板),同时自动为我们加上 bundle 引入代码!
HtmlWebpackPlugin 使用
-
安装此插件
npm install --save-dev html-webpack-plugin
-
修改 webpack 配置文件,引入插件
// 声明依赖引入 const HtmlWebpackPlugin = require('html-webpack-plugin') // 加入plugins: plugins: [ // 其他插件.. new HtmlWebpackPlugin({ title: '管理输出' }), ]
-
执行打包脚本。
你会发现此时,index.html 将会自动生成,并且 title 自定设为了配置中的内容,同时自动加入了对 bundle 的引入。
但是我们之前的 Vue 实例挂载的内容也会被清除,如果需要同时生成那部分内容需要参考 HtmlWebpackPlugin 源码仓库中的使用说明:jantimon/html-webpack-plugin: Simplifies creation of HTML files to serve your webpack bundles (github.com)
这里我记录几个比较实用的:
自定义生成的 HTML 文件模板
同时生成多个 HTML
现在 html 生成的问题以及解决了,但是dist
文件夹一团糟,我们希望每次 build 的时候都先清理一下dist
文件夹
只需要在 webpack 配置文件中的 output 选项中加上clean: true
即可!!
8.8、Webpack-dev-server 热部署
webpack-dev-server
为你提供了一个简单的 web server,并且具有 live reloading(实时重新加载) 功能。
咦?这不就是 Spring Boot 里面的 Dev Tools 吗?!搞起来搞起来。每次修改代码都要手动跑一下 build 脚本,着实心累。有了这个东西,就能体验到所见即所得的顺滑体验!
-
安装 webpack-dev-server
npm install --save-dev webpack-dev-server
-
修改 webpack 配置文件
entry:{ // ... }, output:{ // ... }, devServer: { contentBase: './dist', },
这个功能的本质就是在本地启动一个服务器(默认 8080 端口,以 localhost:8080 访问),然后将分发内容的资源放入服务器进行实时更新。
所以此配置
contentBase: '/dist'
就是告诉服务器,将此目录的文件作为可访问文件。 -
配置 npm 脚本,用于启动服务器:
"scripts": { "start": "webpack server --open"}
--open
参数意思是,启动服务器后,自动打开浏览器进行访问。
8.9、source map 查错安利
webpack 打包时,帮助你定位错误代码的好帮手!只需要在 webpack 配置文件中加上一句就能搞定:
entry: { ... },
output: { ... },
+ devtool: 'inline-source-map',
8.10、webpack 配置分离
在我们的后端项目中,Spring Boot 的 yaml 配置文件,我们通常会使用多环境的配置,以满足开发和生产的不同需求。前端项目也不例外,我们开发时我们需要更完善的代码检查和更顺滑的开发体验,生产时我们则需要将代码进行压缩,内容进行精简。那么就注定我们需要将两套配置文件。
在 Webpack4.0 以后,在配置文件中添加了一个
mode
选项,在面对开发环境时,我们可以将其设置为development
; 在面对生产环境时,我们将其设置为production
同样在文件名上也可以根据习惯加以区分:
例如:开发时:webpack.dev.js、生产时: webpack.prod.js。当然他们之间还是要共享一部分代码的,我们可以使用一个通用的配置文件: webpack.common.js。
那么就涉及到一个配置文件合并的问题,就需要用到一个新的东西:webpack-merge
-
无脑安装到本地,作为开发依赖
npm install --save-dev webpack-merge
-
在开发/生产的配置文件中引入并使用
// 引入webpack-merge const { merge } = require('webpack-merge') // 引入通用配置内容 const common = require('./webpack.common.js') // 使用merge, 合并通用内容和当前场景需要的配置内容 module.exports = merge(common, { mode: 'development', devtool: 'inline-source-map', devServer: { contentBase: './dist' }, })
第一次用 webpack 时,我们就知道了 webpack 命令是可以使用
--config
指定配置文件的。由于我们生产环境下我们只会执行一次 build 脚本,所以在 npm 脚本中,对 build 脚本使用生产时配置文件!
而在使用 webpack-dev-sever 时,则要使用开发时配置文件!
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack --config webpack.prod.js", + "start": "webpack server --open --config webpack.dev.js" },
用的配置文件: webpack.common.js。**
那么就涉及到一个配置文件合并的问题,就需要用到一个新的东西:webpack-merge
-
无脑安装到本地,作为开发依赖
npm install --save-dev webpack-merge
-
在开发/生产的配置文件中引入并使用
// 引入webpack-merge const { merge } = require('webpack-merge') // 引入通用配置内容 const common = require('./webpack.common.js') // 使用merge, 合并通用内容和当前场景需要的配置内容 module.exports = merge(common, { mode: 'development', devtool: 'inline-source-map', devServer: { contentBase: './dist' }, })
第一次用 webpack 时,我们就知道了 webpack 命令是可以使用
--config
指定配置文件的。由于我们生产环境下我们只会执行一次 build 脚本,所以在 npm 脚本中,对 build 脚本使用生产时配置文件!
而在使用 webpack-dev-sever 时,则要使用开发时配置文件!
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack --config webpack.prod.js", + "start": "webpack server --open --config webpack.dev.js" },