这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战
一、什么是 webpack
webpack 是一个现代的 JS 的应用程序的静态模块打包器。当 webpack 处理应用程序时,它会递归的建立一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包到一个或者多个 bundle (扒自官方文档,并不是我懒,担心总结的没有官方到位.....)。
很显然官方文档说的很清楚,webpack 是用来打包的,还有一个重要的作用就是管理应用程序的模块依赖关系,而且是递归的构建依赖关系。为什么说这个呢,如果你是一个出生在工程化时代的 FE,自然不会懂得手动去管理 script 标签时代的我们是如何痛苦。
举个很简单的例子,如果库 A 的运行需要,库 B ,那么一定要保证在 A 之前通过 script 引入 B,否则 A 不能正常运行。如果有个几十个,岂是苦不堪言可以形容;而有了 webpack就不用了,因为 webpack 实现了模块化,我们通过对应的模块化方式使用模块即可,至于谁先谁后,这个工作交给 wepback 去做;
当然,人家的功能很多,我们先用用再说
二、安装 webpack
webpack 4.x 的版本在安装 webpack 的同时还需要安装 webpack-cli;
npm install webpack webpack-cli
三、 配置使用 webpack
当然,webpack 支持零配置的开箱使用体验,但是学习 webpack 一个十分重要的事情就是学习如何编写 webpack 的配置文件。当然,最终目的是学习 webpack 的原理。当然,学会跑之前,我们先学会走。我们先来看几个 webpack 的重要概念:
3.1 入口和出口
-
入口(entry):指示 webpack 从哪个模块开始构建依赖;简言之,告诉 webpack 哪里是头,从哪儿开始查找依赖关系;
-
出口(output):告诉 webpack 在哪里输出它打包后的文件(bundles),以及如何命名这些文件;其默认值是 ./dist。
-
示例项目结构:
-
│ webpack.config.js
│
└─src
index.html
index.js
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="../dist/bundle.js"></script>
</body>
</html>
- index.js
document.addEventListener('DOMContentLoaded', function () {
let div = document.createElement('div');
div.innerHTML = 'Hello World';
document.body.appendChild(div);
div = null;
});
配置 webpack 需要在项目根目录下新建一个 ·· 的文件,webpack 是基于 Node.js 的,其配置文件是一个基于 CommonsJS 的模块;
- 示例:
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filelname: 'bundle.js'
}
}
现在我们已经配置了一个最最简单的配置文件,接下来我们需要在命令行工具(cmd/terminal/powershell)运行命令:
webpack
当然,我们也可以在 package.json 中配置两个脚本命令,如下的 dev 和 build:
{
"name": "test",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack"
},
"devDependencies": {
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
}
}
- 打包后输出的文件:
- 打包后启动浏览器查看:
经过上面的过程,我们已经发现在项目的 dist 目录下已经有一个 bundle.js 的文件,而且运行 html 文件,我们也得到我们预期的效果;
但是有一个问题:index.html 的 bundle.js 文件仍然使我们自己引入的,这仍然不方便,所以接着我们需要一个叫做 html-webpack-plugin 的插件,它的作用就是把打包出来的 js 自动引入到 html 文件中;
3.2 插件
插件可以扩展 webpack 的功能,让它可以完成跟多的任务,例如上面我们需要的能力,自动把打包后的 js 插入到 HTML 文件;
插件大多数都需要安装,也有一部分是 webpack 自带的插件,这些自带的插件随着 webpack 的安装也已经安装好了。
- 安装 html-webpack-plugin 插件
npm install html-webpack-plugin
- 使用插件需要在 webpack 配置文件中配置 plugins 属性:plugins 是一个数组,数组项是每个插件类的一个实例(new 插件类):
html-webpack-plugin 接收一些配置,如下
- template: string 需要引入输出文件的 html 文件模板
- hash: boolean 给引入的 js 加hash
- filename: string 输出的 html 文件的名字(插件会把模板 html 复制到输出目录中)
- minify: Object 压缩优化输出的 html 文件配置
- removeAttributeQuotes: true, 删除行内属性的双引号
- collapseWhitespace: true 删除换行,使内容保持在一行
const path = require('path');
+ const HTMLWebpackPlugin = require('html-webpack-plugin'); // 插件在使用前需要引入
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
+ plugins: [
+ new HTMLWebpackPlugin({
+ template: './src/index.html', // 需要引入输出文件的 html 文件模板
+ hash: true, // 给引入的 js 加hash
+ filename: 'index.html', // 输出的 html 文件的名字(插件会把模板 html 复制到输出目录中)
+ minify: { // 压缩优化输出的 html 文件配置
+ removeAttributeQuotes: true, // 删除行内属性的双引号
+ collapseWhitespace: true // 删除换行,使内容保持在一行
+ }
+ })
+ ]
}
修改陪之后,重新执行打包命令:npm run build
- 产出的 html 文件:
- html 文件详情
插件还有很多,包括后面的很多优化都需要依赖插件的功能,我们这里先介绍一个,后面还要有其他功能插件;
3.3 webpack-dev-server
我们在开发的过程中,发现每次都执行 npm run build 产出文件,然后再查看效果,这样做很低效。接着,我们介绍一个实用的功能,webpack-dev-server;它可以帮我们打开发环境的包,配置开发代理,文件修改后自动编译,然后通知浏览器刷新等功能;
- 安装
webpack-dev-server
npm install webpack-dev-server
- 配置 webpack-dev-server
- devServer: Object
- contentBase: string, 静态资源文件目录
- port: number 开发服务器启动的端口
- open: boolean 是否自动启动浏览器
- progress: boolean 是否展示进度条
- proxy: Objct 配置代理
- '/api': Object 将带有 /api 的请求,转发值 target 指向的域名
- target: 'domain.com/somet/api',
- changeOrigin: true,
- secure: false
- '/api': Object 将带有 /api 的请求,转发值 target 指向的域名
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
+ devServer: {
+ contentBase: './build',
+ port: 3000,
+ open: true,
+ progress: true,
+ proxy: { // 配置代理
+ '/api': {
+ target: 'http://domain.com/somet/api',
+ changeOrigin: true,
+ secure: false
+ }
+ }
+ },
plugins: [
new HTMLWebpackPlugin({
template: './src/index.html',
hash: true,
filename: 'index.html',
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true
}
})
]
}
- 配置启动开发服务器
需要注意的是,之前我们打包都是执行
npm run build(在package.json scripts中配置的),但是启动开发服务器需要通过命令:webpack-dev-server命令启动,同样我们将这个命令配置称为一个 script,名为 dev
npm run dev
执行命令后为,开始编译,浏览器自动启动;
3.4 样式处理和 loader
webpack 同样可以帮我们处理样式文件,包含 css 和 css 预处理文件;但是 webpack 自身只能理解 JS,所以处理这些非 JS 的文件需要借助 loader 的能力。loader 可以将将非 JS 文件处理成有效的模块,然后利用 webpack 的打包能力对其进行处理;
- 我们在 src 目录下新建 index.css 文件
div {
color: #fff;
background: deeppink;
font-size: 24px;
font-weight: bold;
}
- 在 js 中导入 css 文件
+ import './index.css';
document.addEventListener('DOMContentLoaded', function () {
let div = document.createElement('div');
div.innerHTML = 'Hello World';
document.body.appendChild(div);
div = null;
});
- 安装 loader,loader 也需要单独安装;
yarn add css-loader style-loader
- 配置文件,配置 loader 需要配置 module 字段,module 是一个对象,对象下有一个 rules 字段,是一个数组;数组中每一项又是一个对象,对象中一般有两个字段:
- test: 表示 loader 要处理文件的扩展名的正则
- loader: 处理对应文件的 loader
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
devServer: {
contentBase: './build',
port: 3000,
open: true,
progress: true,
proxy: {
'/api': {
target: 'http://domain.com/somet/api',
changeOrigin: true,
secure: false
}
}
},
+ module: {
+ rules: [
+ {
+ test: /.css$/,
+ use: ['style-loader', 'css-loader'] // 顺序不可颠倒,loader 在执行时是从右向左执行的
+ }
+ ]
+ },
plugins: [
new HTMLWebpackPlugin({
template: './src/index.html',
hash: true,
filename: 'index.html',
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true
}
})
]
}
- 打包结果
细心的朋友你一定发现了,webpack 是以内嵌的方式把 css 以 style 标签的形式插入到页面的 head 标签内,那么如何把它抽离成单独的 css 文件呢?下一篇我们会详细讲述这是过程