或许为了自身写,或许为了知己写!

一小时的时间,入门 Webpack

前言

前端构建工具 Webpack 最近特火,火到 Vue/React 官方推出的脚手架都是基于 Webpck 打造的。

为了更了解 Webpack,特意实打实地安装配置 Webpack。对以后进阶学习也能夯实基础,现在一起学习入门级的 Webpack 吧!

认识 Webpack

先来观察应用 Webpack 能做的事:

webpack

从图中得出:Webpack 能打包所有 JS 脚本;能打包所有 style 样式;能打包所有图片;能打包所有预编译语言。通俗的理解就是能打包前端所有资源

安装 Webpack

首先确保你已经安装了 Node.jsGit。找到存放项目的目录,在该目录下初始化项目。在终端执行:

1
2
3
$ npm init
// 或者
$ npm init -y

初始化后生成一个 package.json 文件,大致内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "webpack",
"version": "1.0.0",
"description": "study-webpack",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"study-webpack"
],
"author": "yuan",
"license": "ISC"
}

为了后续快速安装其他依赖,这里使用淘宝镜像。在终端执行:

1
$ npm install -g cnpm --registry=https://registry.npm.taobao.org

接来下安装 Webpack,在 npm 官网查询安装手册。在终端执行:

1
2
3
4

$ cnpm install --save-dev webpack
// 或者
$ yarn add webpack --dev

附:使用 yarn 语法安装,确保已经安装 yarn

注意:最新版本 Webpackwebpack-cli 从中分离了出来需要单独安装。在终端执行:

1
$ cnpm i webpack-cli --save-dev

安装完 Webpack 之后需要其运行起来,得需要一个配置文件,其名称为 webpack.config.js,不能为其他名称。如果是其他名称 Webpack 找不到该配置文件,就抛出错误提示。

运行 Webpack

查询官网手册后,填写 webpack.config.js 配置。对 entry 属性值和 filename 属性值进行简单修改,webpack.config.js 大致内容如下:

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

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

module.exports 导出一个对象,其中:

entry 表示打包资源入口,该字段属性值可以是 String / Array / Object

output 表示打包资源出口,也就是经打包的资源从该口输出。

distWebpack 打包完成后存放资源的目录。

配置完内容后,在根目录下创建目录 src,里面编写一个叫 index.js 脚本:

1
2
3

// index.js
document.write('Hello Webpack!');

为了方便看效果,在根目录下创建一个 index.html 模版,并引入打包后的资源 bundle.js

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>入门webpack</title>
</head>
<body>

</body>
</html>
<script src="./dist/bundle.js"></script>

使用预定义命令启动 Webpack,可以在 package.json 文件中的 scripts 字段中添加命令。

1
2
3
4
5
6
// ...
"scripts": {
"start": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
}
// ...

在终端执行 cnpm start 打包完成后会看到在根目录下生成 dist 目录,里面包含 bundle.js 脚本,在浏览器端运行 index.html 可以看到页面输出 Hello Webpack!。

到此完成了 Webpack 初步的打包。

执行 Webpack 打包时,终端执行输出一些信息:

1
2
3
4
5
6
7
8
9

Hash: 9d157b09dd8d37122dad
Version: webpack 4.42.1
Time: 560ms
Built at: 2020-04-19 11:48:36
Asset Size Chunks Chunk Names
bundle.js 961 bytes 0 [emitted] main
Entrypoint main = bundle.js
[0] ./src/index.js 31 bytes {0} [built]
  • Hash 表示当前文件打包生成的 hash 值,文件改变,hash 值就会变。

  • Version 表示项目当前安装 Webpack 的版本。

  • Time 表示项目打包所花费的时间。

  • Build 表示项目打包日期,打包生成文件名称和文件大小

  • Entrypoint 表示项目打包入口点。

即:

1
2
3
4
5
6
7
8
9
10
11
12
13

module.exports = {
entry: './src/index.js',
// ...
}

// 等价于
module.exports = {
entry: {
main: './src/index.js'
},
// ...
}
  • chunks: 打包文件的 id,现在只有一个 bundle.js 打包文件,有多个的时候,会有多个不同的 chunk

  • Chunk Names: 打包文件的名字。

最后一行表示打包生成的文件路径。

大多数网站中都会使用缓存,减少页面加载时长。

Webpack 打包也可以做到这点,把之前的 bundle.js 改成带有 hash 值。

修改后的 webpack.config.js

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

module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[hash].js' // hash
}
};

重新运行 cnpm start 打包完成后会看到根目录下生成 dist 目录,里面包含带 hash 值的资源。如果想缩短 hash 值,可以进行截取长度,比如:[hash:6]

如果想要优化打包后的资源和想使用 Webpack 强大的功能,请继续往下看。

认识 Plugin

pluginWebpack 的核心,Webpack 自身的多数功能都是用这个插件接口,让 Webpack 打包变得极其灵活。

经过认识 Webpack 初次打包后,发现每次执行 cnpm start 打包完成后都会在 dist 目录中追加打包生成后的新资源。造成 dist 文件很大。这时 clean-webpack-plugin 就可以登场,帮助我们解决这个问题。

npm 官网搜索该插件,点击名称进入详情查看安装手册,在终端执行:

1
2

$ cnpm i --save-dev clean-webpack-plugin

webpack.config.js 中添加该配置:

1
2
3
4
5
6
7
8
// ...
const { CleanWebpackPlugin } =require('clean-webpack-plugin');
module.exports = {
plugins: [
new webpack.ProgressPlugin(),
new CleanWebpackPlugin()
]
}

在终端执行 cnpm start 会看到上一次打包生成的资源自动删除后,重新创建新的打包资源。

如果在项目中要引入打包后的资源,并且该资源带有 hash 值时不易方便使用,脚本太多也不易区分,这时可以使用 Webpack 提供的 HTML 模版插件解决问题。

npm 官网搜索该插件,点名称进去查看安装手册,在终端执行:

1
2

$ cnpm i --save-dev html-webpack-plugin

安装成功后,在根目录 package.json 中的 devDependencies 里能看到该插件和该插件的版本。

Webpack 中配置该插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[hash:6].js'
},
plugins: [
new HtmlWebpackPlugin() // htmlPlugin
]
};

在终端执行 cnpm start 会看到在根目录下生成 dist 目录,里面包含带 hash 值的资源和压缩过的 index.html

如果不想使用压缩过的资源,可以在 webpack.config.js 中进行配置:

1
2
3
4
5
6

// ...
module.exports = {
mode: "development",
// ...
}

根据 mode 参数 Webpack 会区分是生产环境还是开发环境。一般生成环境 mode 设置为 production,开发环境设置为 development

设置完成后,在终端执行 cnpm start 打包完成后,然后在浏览器上运行 index.html 可以看到输出内容没变化,页面代码没有压缩。

如果想对 src/index.html 做一些调整,比如:修改 title,创建多个模版文件,多个模版文件引入不同的脚本等等;只需要在 new HtmlWebpackPlugin() 中添加一些配置项就能解决。

比如修改 title

1
2
3
4
5
6
7
module.exports = {
// ...
new HtmlWebpackPlugin({
title: '学习webpack'
})
// ...
}

在终端执行 cnpm start 打包完成后,在浏览器上运行 index.html 会看到 title 的变化。

项目难免会美化页面,那么就得给页面添加一些样式,可以写在单独文件中,可以写在 .html 模版中,这时处理 CSS 可以使用 css-loader 解决问题。

认识 loader

loader 用于对模块的源代码进行转换。loader 可以在 import 或”加载”模块时预处理文件。可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URLloader 甚至允许直接在 JavaScript 模块中 import CSS 文件。

①、处理 CSS

首先安装处理 CSS 相应的 loadercss-loaderstyle-loader

npm 官网搜索该 loader,点击名称进去查看安装手册,在终端执行:

1
2

$ cnpm i --save-dev css-loader style-loader
  • css-loader 处理以 .css 后缀的文件。

  • style-loader 经过 css-loader 处理过的 CSS 插入到 DOM 中。

安装成功后,在根目录 package.json 中的 devDependencies 里能看到该 loader 和该 loader 的版本。

webpack.config.js 中添加处理 CSSloader 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
},
// ...
}

注意:use 选项顺序,先使用 css-loader 再使用 style-loader

项目中一般都是使用单独文件写入样式,这里使用以 .css 为后缀的文件负责控制页面样式:

1
2
3

*{ margin:0px;padding:0px;}
body{ background: red; }

在根目录 src/index.js 中引入该样式文件:

1
2
document.write('hello webpack') 
require('./style.css')

在终端执行 cnpm start 打包完成后,在浏览器上运行 index.html,能看到页面背景色变红色。使用开发者工具也能看到页面插入 style 标签,style 标签里面嵌入刚刚写的样式:

webpack

随着项目复杂度的提升,控制页面的样式也很多;如果按照这样写法,页面会有一大段来控制样式,考虑到对后期的性能优化不友好,可以考虑把样式单独打包一个文件。

②、提取 CSS

新版本 Webpack4.x 建议使用 mini-css-extract-plugin

npm 官网搜索该插件,点击名称进去查看安装手册,在终端执行:

1
2

$ cnpm install --save-dev mini-css-extract-plugin

安装完该插件后,在根目录 package.json 中的 devDependencies 里能看到该插件和该插件的版本。

webpack.config.js 中的 module 选项和 plugin 选项中配置该插件:

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
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true,
},
},
'css-loader',
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: '学习webpack'
}),
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}

在终端执行 cnpm start 打包完成后,在浏览器上运行 index.html,可以看到与之前效果一样;使用开发者工具能看到生成的样式文件 style.css,之前嵌套在页面中的样式不见了。

效果如下:

webpack

大型项目中一般会选择应用预编译语言,这里使用 Sass 预编译语言。

nmp 官网搜索该 loader,点击名称进去查看使用安装手册,在终端执行:

1
2

$ cnpm install --save-dev sass-loader node-sass

安装完毕后,在根目录 package.json 中的 devDependencies 里能看到该 loader 和该 loader 的版本。

webpack.config.js 中的 module 选项中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
}
]
}
// ...
}

同样在根目录 src 下创建一个专门存放 sass 样式文件:

1
2
3
4

// index.scss
$fontSize: 16px;
body{ font-size:$fontSize;}

在根目录 src/index.js 中引入该 scss 文件:

1
2
3
4

document.write('hello webpack')
require('./style.css')
require('./index.scss')

在终端执行 cnpm start 打包完成后,在浏览器上运行 index.html,能看到页面字体变化。

③、打包图片

打包处理图片使用 url-loaderfile-loader

npm 官网搜索该 loader,点击名称进去查看安装手册,在终端执行:

1
2

$ cnpm install url-loader file-loader --save-dev

安装完该插件后,在根目录 package.json 中的 devDependencies 里能看到该 loader 和该 loader 的版本。

webpack.config.js 中的 module 选项中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(png|jpe?g|gif)$/i,
loader: 'file-loader',
options: {
outputPath: 'images',
name: '[1]-[name].[ext]'
},
}
]
},
// ...
}

style.css 样式文件中引入一张图片作为背景图:

1
2
3
4

*{ margin:0px;padding:0px;}
body{ background-color: red; }
body{ background: url('./webpack1-3.jpg') repeat-x; }

在终端执行 cnpm start 打包完成后,在浏览器上运行 index.html,能看到之前的背景色被改变了。

技术不断创新,前端技术领域也是如此。新项目中越来越喜欢使用 ES6 作为处理 JS 页面数据交互,接下来继续打包 ES6

④、打包 ES6

打包处理 ES6 使用 babel-loaderbabel-corebabel-preset-envbabel-preset-es2015

npm 官网搜索该 loader,点击名称金进去查看安装手册,在终端执行:

1
$ cnpm i babel-loader babel-core babel-preset-env babel-preset-es2015 --save-dev

附:如果运行出错可以安装:@babel/core@babel/preset-env;其中babel-loader 转换 js 加载器;@babel/corebabel 的核心模块;@babel/preser-envES6 转为 ES5babel-preset-es2015将部分 ES6 转化成 ES5 语法。

安装完该插件后,在根目录 package.json 中的 devDependencies 里能看到该 loader 和该 loader 的版本。

webpack.config.js 中的 module 选项中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
// ...
}

项目中处理页面数据交互一般都是存放单独脚本文件,因此在根目录下中的 src 目录下创建一个脚本文件 es6.js

1
2
3

let today = '今天天气很好';
alert(today);

在根目录 src/index.js 中引入该脚本文件:

1
2
3
4

document.write('hello webpack')
require('./style.css')
require('./es6.js')

此时运行 cnpm install 肯定会报错,因为需要设置 babel。在根目录下创建 .babelre 大致内容大致如下:

1
2
3
4

{
'presets':['env']
}

这时在终端执行 cnpm start 打包完成后,在浏览器上运行 index.html,能看到页面弹出的语句。

目前为止打包生成的 index.html 模版每次都得重新刷新页面。为了提高开发效率 Webpack 提供了开启服务热更新替换,不用刷新界面就能实现热更新。下面实现自动开启服务热更新。

devServer

查看 Webpack 手册安装相关模块,在终端执行:

1
2

$ cnpm i --save-dev webpack-dev-server

安装成功后,在根目录 package.json 中的 devDependencies 里能看到 dev-serverdev-server 的版本

webpack.config.js 中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
// ...
const webpack = require('webpack')

module.exports = {
// ...
devServer: {
contentBase:path.resolve(__dirname, 'dist'),
host: 'localhost',
port:8090,
open: true, // 自动打开浏览器
hot: true // 热更新
}
}

修改 package.json 文件中的 scripts 字段里面的 start 属性对应的属性值:

1
2
3
4
5
6
// ...
"scripts": {
"start": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ...

也可以添加新的自定义启动 Webpack 命令,如 run 命令:

1
2
3
4
5
6
7
// ...
"scripts": {
"start": "webpack",
"run": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ...

在终端执行 cnpm start 或者 cnpm run 会看到浏览器自动打开生成的 index.html。然后在 styl.css 中添加样式,如:font-size:30px 页面会自动更新并显示最新内容。

项目中或许会用到 jQuery 库或者 Vue,那么如何使用呢?继续往下看。

快捷导入

首先在 npm 官网查询要安装的包,如 jQuery,在终端执行:

1
2

$ cnpm install --save-dev jquery

安装完毕之后,在根目录 package.json 中的 devDependencies 里能看到 jQueryjQuery 的版本。

webpack.config.js 中的 plugins 选项中配置:

1
2
3
4
5
6
7
8
9
10
11

modules.exports = {
// ...
plugins: [
// ...
new webpack.ProvidePlugin({
$: 'jquery',
})
]
// ...
}

为了方便看效果,在根目录下的 src 中创建新的文件,如:es5.js

1
2
// es5.js
$('body').text('Hello World!!!')

在根目录 src/index.js 中引入该脚本:

1
2
3
4
document.write('hello webpack') 
require('./style.css')
require('./es6.js')
require('./es5.js')

这时在页面上能看到 Hello World!!! 字样。到此为止一个入门级带有热更新的 Webpack 学习完毕。

总结

到这里入门级 Webpack 就算结束了。以上内容从认识 Webpack 到使用各种 loader 和各种 plugins 打包生成资源应用在项目中,认识了基本的使用语法和应用各种配置,也为以后进阶的学习夯实基础。

针对不同的项目,还有更多的 Webpack 打包细节需要调优,如:如何减少搜索文件;如何提高 loader 的打包速度;如何排除项目中无用的打包文件等等。在接下来的时间里,慢慢研究与学习。

———— / END / ————
0%