前言
如今的前端,必定都用过Vue或者React,多多少少都有接触过webpack,我还清楚地记得第一次看Vue的Webpack配置时,仿佛在看天书一般。webpack作为前端工程化的核心知识之一,因为其功能太多,配置项又多又杂,初学时也比较难啃。
这是一个面向初学者的webpack系列博文。从0开始,一步步搭建出完整的、适用于常规环境的webpack配置。
每章都以树状结构扩深知识点,尽可能的涵盖更多更深的知识内容,同时保留每个章节的所有代码。
目录构建
首先创建一个文件,然后执行 npm init
随后创建对应子文件夹
.webpackLemo // 文件夹
│
│-- config // 存放配置文件
│
│-- public // 存放模板
│
│-- src // 存放入口文件
│
└─- package.json
写Demo用的简单配置
我们有了基本文件夹,接下来开始构建最基本配置,能满足平时写demo用。
支持打包
首先安装webpack工具库
npm i webpack webpack-cli
创建入口文件
// src/index.js
console.log("Webpack Lemo");
// public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webpack Lemo</title>
</head>
<body>
<script src="./main.js"></script> //打包后的文件为 main.js 在这里手动写入
</body>
</html>
创建配置文件
// config/webpack.config.js
const path = require('path');
module.exports = {
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, '../build')
}
}
entry :入口起点配置。简单来说,由html直接引用的文件称为入口起点,从这个起点开始,应用程序启动执行。例子中的
main
为入口起点的 [name] 值output:输出配置。webpack打包完成,怎么输出,要输出哪些文件,输出到哪等相关信息的配置
output.path
要求接受一个绝对路径。path.resolve
方法会把一个路径或路径片段的序列解析为一个绝对路径,__dirname
指向当前文件的路径entry 配置里使用的是相对路径,这个路径默认指向代码被执行时路径,即 webpackLemo 文件夹的根目录。可通过设置 context 来修改路径上下文。
// 与正文中的配置效果等同 { context: path.resolve(__dirname), entry: { main: '../src/index.js' } }
// package.json
{
...
"scripts": {
"dev": "webpack --config ./config/webpack.config.js"
},
...
}
接下来只需在命令行里执行 npm run dev
就能进行第一次打包
先忽视 warning ,可以看见webpack自动打包了 src
目录下的文件,在根目录下自动生成build文件,并将打包后的入口文件命名为 main.js 放入build文件中。
然后我们可以把 public 目录下的 index.html 手动复制到 build 目录下。
完成以上操作后,我们的目录会长成这样
.webpackLemo
│
│-- build // 存放打包后的文件
│ │-- main.js // src/index.js打包生成的文件
│ │-- index.html // 从public中手动复制
│
│-- config
│ │-- webpack.config.js // 新建webpack配置文件
│
│-- public
│ │-- index.html // 新建html模板文件
│
│-- src
│ │-- index.js // 新建入口文件
│
└─- package.json
用浏览器打开 ./build/index.html,可以看见控制台里输出了 在 ./src/index.js 里写的 console.log信息
模板处理
如果每次打包,都需要我们手动粘贴一次 index.html,那也太麻烦了。如果有一个哥们儿能 帮我们自动复制index.html文件,并且在index.html中自动引入我们打包后的文件 那就好了。
这个哥们儿叫做 html-webpack-plugin 。
执行安装 npm i html-webpack-plugin
然后修改文件
// public/index.html
<!DOCTYPE html>
<html lang="en">
...
<body>
// <script src="./main.js"></script> // 删除手动写入的 对main.js的引用
</body>
</html>
// config/webpack.config.js
...
// 引入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
output: {
...
filename: '[name].bundle.js', // 添加filename配置
},
plugins: [ // 注意是个数组
new HtmlWebpackPlugin({ // 在plugin中使用 html-webpack-plugin
template: './public/index.html' // 配置模板项地址
}),
]
}
plugins:配置webpack插件的地方
filename 配置项中使用了[name],这里的[name]为输出的模块名。除了[name],webpack还提供了别的模板语法,可在这里找到所有信息 output.filename
现在我们再执行 npm run dev
可以看见这次打包生成的文件名为main.bundle.js,并且 build 目录下的index.html 文件里自动引入了 main.bundle.js
完成以上操作后,我们的目录会长成这样
.webpackLemo
│
│-- build
│ │-- main.js // 第一次打包生成的文件
│ │-- main.bundle.js // 第二次打包生成的文件
│ │-- index.html // 使用 html-webpack-plugin 后生成的html文件
│
...
文件清理
可以看见第二次打包时,我们修改了输出配置,生成了新的文件,但是第一次打包的内容还是被保留了下来。其实第一次打包的内容已经用不上了,有没有什么哥们儿能帮我们自动删除不需要的内容?
有,这个哥们儿叫做 clean-webpack-plugin
执行安装 npm i clean-webpack-plugin
然后修改文件
// config/webpack.config.js
...
// 引入clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
...
output: {
path: path.resolve(__dirname, '../build'),
filename: '[name].[contenthash].js', // 我们再修改一次输出文件名
},
plugins: [
...
new CleanWebpackPlugin(); // 使用 clean-webpack-plugin
]
}
现在我们第三次执行 npm run dev
,可以看见build目录下头两次生成的入口文件都被清除了,只保留了第三次生成的内容。
完成以上操作后,我们的目录就长成这样
.webpackLemo
│
│-- build
│ │-- main.417cd68a8235c5cd4f89.js // 第三次打包生成的文件
│ │-- index.html
│
...
到目前为止,已经支持了基本功能打包,下一步来处理css样式和file文件
打包css和file文件
对css的支持
对css文件的处理有2种方式:
-
将css代码以
<style>
标签的形式嵌入 html 中 -
生成css文件,以
<link>
标签的形式在 html 中引入
这里我们只讨论第一种形式,第二种会在 代码分割(卫星) 中介绍
我们需要4个插件
- style-loader (github) 将css代码以
<style>
标签的形式嵌入 html 中 - css-loader (github) 解析通过模块化引入的css文件
- postcss-loader (github) 提供预处理css的一些能力,拥有许多子插件,提供了许多能力
- autoprefixer (github) postcss-loader的子插件,提供厂商前缀自动补全能力,如 -m-
loader和plugin的区别 :
// 官方文档原文 While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables. // 渣译 loader一般用来转换某些类型的modules,而插件可以用来执行更广泛的任务,例如对包的优化、资源管理、环境注入等 // 大白话 loader是一个转换器,将A文件进行编译成B文件,比如:将A.less转换为A.css,单纯的文件转换过程。 plugin是一个扩展器,丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务
执行安装 npm i style-loader css-loader postcss-loader autoprefixer
然后修改文件
// ./config/webpack.config.js
...
module.exports = {
...
module: { // 这里是对象
rules: [ // 这里是数组
{ // 数组中的每个具体配置
test: /\.css$/, // 匹配 .css文件
use: [ // 当匹配上 .css 文件后,会依次使用loader进行处理
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')
]
}
}
]
}
]
}
}
module:各种模块应该如何处理,由module进行配置
loader的处理顺序为从下到上。正文中loader处理顺序为:postcss-loader、css-loader、style-loader
// ./src/index.js
import './index.css'
...
// ./src/index.css
body {
width: 100%;
height: 200px;
background-image: linear-gradient(to right, red , yellow);
}
// ./package.json
{
...
"browserslist": [
"iOS >= 6",
"Android >= 4",
"IE >= 9"
]
}
browserslist 用来配置 我们的应用会在哪些平台、哪些浏览器和浏览器版本上使用。 autoprefixer 会读取该项值,用来觉得需要添加哪些前缀,如果运行在最新的浏览器上,也许不会添加相应的前置信息
browserslist 默认值:> 0.5%, last 2 versions, Firefox ESR, not dead
现在执行 npm run dev
,build 目录里没有新增 .css文件,用浏览器打开 ./build/index.html 文件,可以看见背景有个渐变色,从控制台里可以看见样式代码以 <style>
标签的形式嵌入到了html中,并且自带了厂商前缀
完成以上操作后,我们的目录就长成这样
.webpackLemo
│
│-- src
│ │-- index.js
│ │-- index.css // 新增加的css代码
│
...
对less的支持 (非必要)
less其实就是css的预处理器,一些代码里可能会用sass,但是原理类似,这里就用less举例
在上一节的基础上,额外安装一个 less-loader
处理less文件
执行安装 npm i less-loader
然后修改文件
// ./config/webpack.config.js
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader', // 只增加一行less-loader 处理less文件
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')
]
}
}
]
}
]
}
}
// ./src/index.js
import './index.less' // 引入less文件
...
// ./src/index.less
body {
width: 100%;
height: 200px;
background-image: linear-gradient(to right, red , yellow);
}
执行 npm run dev
,得到的效果和处理css一样
完成以上操作后,我们的目录就长成这样
.webpackLemo
│
│-- src
│ │-- index.js
│ │-- index.css
│ │-- index.less
│
...
对file文件的支持
其实只要是文件,都可以处理,这里用图片文件举例
可用2种 loader 进行处理
- file-loader (github) 解析通过模块化引入的文件,在打包的时候会获取打包后的地址,并在代码中返回这个地址,使得文件可以被读取
- url-loader (github) 作用同file-loader,但是能把文件压缩成base64的形式,一般用于图片
这两个loader作用比较类似,最大的区别在于 file-loader 会生成文件, url-loader 会把文件数据压缩成base64直接使用,不会生成文件。两者能一起使用,在某些场景下比使用单一loader效果更好,可参考 这篇博文(卫星)
这里我用file-loader举例
执行安装 npm i file-loader
然后修改文件
// ./config/webpack.config.js
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
}
// ./src/index.js
...
import img from '../statics/imgs/img.jpg'
const myimg = new Image();
myimg.src = img;
document.body.appendChild(myimg);
...
执行 npm run dev
,可以看见build文件里增加了打包后的图片文件,打开 ./build/index.html 后也能看到相应的图片被挂载到页面上
完成以上操作后,我们的目录就长成这样
.webpackLemo
│
│-- build
│ │-- 051fa5032109fbc8005c856a48cf1c52.jpg
│ │-- index.html
│ │-- main.a14556a5d43741e69cdd.js
│
│-- statics // 存放静态资源
│ │-- imgs // 存放图片
│ │ │-- img.jpg
│
...
到现在,我们已经支持css打包和文件打包,自己写demo时,使用这样的webpack配置已经OK了,但是我每次改了代码都要重新执行一次 npm run dev
,真的太麻烦,如果能让webpack自动刷新页面就好了。
构建开发环境
让我们来构建一个开发环境,让我们每次更新的时候,webpack能自动帮我们打包,并且能实时刷新页面.
要能实现实时刷新页面,webpack提供了3种能力
- webpack watch mode webpack 观察模式,最简单的方法,但功能因此也少了很多
- webpack-dev-server 相对简单的web server,并且具备live reloading(实时重新加载) 功能
- webpack-dev-middleware 最复杂的一种,需要自己写服务配置,但可塑性也最高
这里我们采用 webpack-dev-server
执行安装 npm i webpack-dev-server
然后修改文件
// ./config/webpack.config.js
...
module.exports = {
mode: 'development', // 设置mode为'development'模式
devtool: 'cheap-module-eval-source-map', // 设置source-map
devServer: {
contentBase: './build',
open: true
},
...
}
mode:相当于设置一套预设优化配置,默认为
none
。详细可查看 官网文档devtool:用于配置source-map,用来增强调试能力,不同的值会影响构建和重构建的速度。 详细可查看 官方文档
// package.json
{
...
"scripts": {
// 把dev改成build
"build": "webpack --config ./config/webpack.config.js",
// 这里改成dev,并使用新命令项
"dev": "webpack-dev-server --config ./config/webpack.config.js"
},
...
}
接下来执行 npm run dev
,webpack帮我们启动了一个端口号为8080的服务器,并自动打开了浏览器,我们修改代码,能自动帮我们打包新代码,并刷新页面。
总结
到这里,一个能打包、能处理css、处理文件、自启服务器、修改代码后能自动打包,自动刷新页面的配置就完成了。这样一个简单的配置已经实现个人写demo的需求,例如跑跑算法代码,测试一段代码的输出等等。
工具梳理
本章使用:
-
库
webpack、webpack-cli
webpack工具库webpack-dev-server
webpack web-server
-
loader
file-loader
解析通过模块化方式引入的文件,输出成文件url-loader
(非必要) 通过模块化方式引入的文件,以base64的形式输出style-loader
将css代码以<style>
标签的形式嵌入 html 中css-loader
解析通过模块化引入的css文件postcss-loader
提供预处理css的一些能力,拥有许多子插件,提供了许多能力less-loader
(非必要) 解析less代码文件
-
plugins
clean-webpack-plugin
每次打包能清空打包文件夹里之前的内容html-webpack-plugin
能自动生成Html文件,并自动引入打包生成的js文件autoprefix
postcss-loader的子插件,提供厂商前缀自动补全能力,如 -m-
文档梳理
- 关于entry的更多信息
- 关于output的更多信息
- 关于plugins的更多信息
- 关于module的更多信息
- 关于browserslist的更多信息
- 关于dev-server的更多信息
- 关于3种配置的更多信息
- mode官网文档
- devtool官方文档
- 代码分割(卫星)
- 这篇博文(卫星)
尾声
本章节代码仓库:代码仓库
本章节Github-blog: Github-blog
如果文中有错误/不足/需要改进/可以优化的地方,希望能在评论里友善提出,作者看到后会在第一时间里处理
如果你喜欢这篇文章,👍点个赞再走吧,github的星星⭐是对作者持续创作的支持❤️️