前端流程化:基于WebPack构建一套前端工程模板
这篇博文和后续的几篇博文将主要介绍前端工程化方面的构建,分别基于webpack和gulp来探讨。本篇博文将主要使用webpack作为前端工程化构建工具。
开始
本篇博文主要使用webpack实现前端的流程化,流程化表示为具有 流模型 特点的工作提供便利的工具。例如前端开发中最主要的三大块 HTML、CSS和JS都是可以基于 流模型 来实现流程化管理。这样带来的好处首先是省去了开发环节一些额外的工作,同时也提高了项目维护的效率。
例如,前端开发中经常使用 JavaScript 语言,因为浏览器各大厂商的支持性不同以及浏览器本身版本的不同,对 Js 新特性的支持也就千差万别。如果开发时使用 ES6 的语法,那么在生产环境下很大概率会面临一些浏览器不兼容 ES6 的问题,此时需要将 ES6 降维,这是 ES6 转 ES5 就是一段可以用 流模型 来管理的模块。同样常见的还有:
- JavaScript –> 混淆 —> 压缩
- Less/Sass –> CSS –> 压缩
- …
本篇博文主要使用webpack来完成这些基础的流程化工作,以便提高开发效率,主要包括了:
- 项目目录构建;
- CSS/Less/Sass文件的处理;
- 样式文件SourceMap;
- 样式文件的抽离;
- 处理HTML以便动态引入Js/CSS;
- 增加Js/CSS文件名Hash;
- 输出目录的自动清理;
- 处理图片和字体文件;
- …
目录结构
创建项目文件夹 web_workflow ,切入项目文件夹,运行 npm init -y
初始化项目。之后在项目目录下创建文件和目录如下:
+-- assets 资源目录
+-- dist webpack输出目录
| +-- index.html 入口页面
+-- src 源码目录
| +-- style 样式目录
| +-- js js模块目录
| +-- main.js 项目入口
+-- webpack.config.js webpack配置文件
+-- package.json ...
安装必备模块:
npm i -D webpack webpack-cli
然后在 main.js
下加入脚本:
(function pageInit() {
let divEl = document.createElement("div");
divEl.innerHTML = `<p>Webpack Workflow Project</p>`;
divEl.className = "app";
document.body.appendChild(divEl);
})();
在 webpack.config.js
配置文件下写入配置:
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
}
}
使用 webpack 执行打包:
npx webpack
后续 webpack 命令会经常使用到,可以将命令添加到 package.json 的 script
下面:
"script": {
"build": "webpack"
}
之后使用 npm run build
即可实现同样效果。
打包完成后会在 dist 目录下看的 main.js 文件。下来在 dist/index.html 下引入 main.js 以便观察:
<!-- dist/index.html -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./main.js"></script>
</body>
</html>
在浏览器打开 index.html 即可看的效果,此时 main.js 会在body下创建 div ,并在 div 下加入 p 标签,然后在 p 标签里写入一句话:Webpack Workflow Project。
处理样式
前端页面必须是有样式的,前端使用最多的是CSS样式,但是由于CSS样式的语法复用性和参数化并不好,所以有了很多CSS预编译工具用来编写CSS,常见的有Sass和Less,这里将使用Less作为CSS的预编译语言,来处理这样的一个流:less –> css –> html style。
webpack中处理除了 js 以外的模块全部通过各种 loader 来实现,对于实现上述样式流,需要安装如下 loader:
npm i -D less less-loader css-loader style-loader
之后在 webpack.config.js
下写入加载规则:
// webpack.config.js
module.exports = {
entry: "./src/main.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /\.(less|css)$/,
include: [path.resolve(__dirname, "src/style")],
use: ["style-loader", "css-loader", "less-loader"]
}
]
}
}
在 src/style 目录下创建 main.less (或main.css),然后写入一点样式:
// main.less
@tx-color: gray;
@bk-color: #eee;
body {
font-size: 16px;
}
.app {
font-size: 1rem;
color: @tx-color;
background-color: @bg-color;
}
写完以后在 main.js 中引入样式文件:
// main.js
import "./style/main.less";
然后运行 npm run build
重新打包,之后刷新浏览器,即可看的样式生效。
CSS SourceMap
启动样式文件的SourceMap,主要是为了能在 Less/Sass 和 CSS 之间建起一个关联的桥梁,开发者在浏览器里审查样式时可以直接看的样式在原 Less/Sass 文件的位置。
开启 SourceMap 依然在 webpack.config.js 文件中进行配置:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.(less|css)$/,
include: [path.resolve(__dirname, "src/style")],
use: [
"style-loader",
{
loader: "css-loader",
options: {
sourcemap: true
}
},
{
loader: "less-loader",
options: {
sourcemap: true
}
}
]
}
]
}
}
配置完成后重新打包,之后在浏览器审查元素样式时就肯定看的样式在 main.less 中的位置,调试起来更方便。
抽离样式文件
可以看到上面打包后的文件全部集中在 main.js 中,样式文件也被打包在了 js 文件里,这是 webpack 默认的处理方式,因为对于 webpack 来说,一切皆模块,而 js 是他唯一天生可以识别的语言,所以 js 之外的部分全部使用各种 loader 来处理。
抽离样式文件需要用到一个 webpack 的插件:
webpack的plugin和loader不同,loader是为了让webpack识别新的语言语法,plugin是为了让webpack具备更多功能;loader是对语言的扩展,plugin是对功能的扩展。
npm i -D mini-css-extract-webpack-plugin
安装完成后继续在 webpack.config.js 文件中进行配置:
// webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-webpack-plugin");
module.exports = {
...
module: {
rules: [
{
test: /\.(less|css)$/,
include: [path.resolve(__dirname, "src/style")],
use: [
MiniCssExtractPlugin.loader, // 替换了原有的 style-loader
{
loader: "css-loader",
options: {sourcemap: true}
},
{
loader: "less-loader",
options: {sourcemap: true}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "main.css"
})
]
}
重新打包,就能看到在 dist 目录下多了一个 main.css 文件,这就是抽离出来的样式文件。由于抽离样式文件后,样式文件不会再被 main.js 自动注入,所以需要在 index.html 里引入样式:
<!-- index.html -->
<head>
<link rel="stylesheet" href="./main.css">
</head>
然后刷新页面,就能看到和之前一样的效果了,此时 main.css 就是抽离出来的样式文件,可以独立发布。
处理 HTML
上面可以发现每次增加 js 或者 css 文件的时候都需要对 index.html 进行编辑,非常不方便,也非常不符合流程化的理念,所以这里就对 HTML 文件进行自动处理。
安装需要用到的 webpack 插件:
npm i -D html-webpack-plugin
然后修改 webpack.config.js ,引入插件:
// webpack.config.js
const HtmlPlugin = require("html-webpack-plugin");
module.exports = {
...
plugins: [
...
new HtmlPlugin({
title: "WebPack Workflow",
filename: "index.html"
})
]
}
此时可以删除 dist 目录下的 index.html 文件,之后重新打包,就可以发现 dist 目录下自动创建了 index.html 文件,刷新浏览器页面依然有效。
自动 Hash
虽然上面已经实现了 HTML、js和CSS的流程化处理,主要通过 loader 和少量 plugin 实现,但实际生产环境下还有很多优化的地方,比如对引用资源添加Hash就是其中之一。
对资源添加 Hash 主要是为了管理缓存:资源没有变更时使用浏览器缓存,资源有变更时使用最新资源并缓存起来。
一般对资源文件加缓存有常见的两种方式,一种是在文件名中引入 Hash,另一种是在引脚引用是加入查询参数今儿引入 Hash,这里使用第一种文件名 Hash 就可以了。
webpack支持对输出的文件增加 [hash]
标志,来表示这里是一个 Hash 字符串。对应在配置文件里,需要修改两个地方即可:
// webpack.config.js
module.exports = {
...
output: {
filename: "main.[hash].js"
},
...
plugins: [
new MiniCssExtractPlugin({
filename: "main.[hash].css"
})
]
}
上述配置主要对入口文件 main.js 和抽离出的样式文件 main.css 添加 Hash,重新打包后就可以看到原有的 main.js 变成了 main.32r3gu56***.js 的样子,main.css同理。此时 index.html 里引用也自动做了改变。
Perfect!
hash也可以指定长短,以便视觉上更好看,例如 [hash:8] 就是保留 hash 字符串的 8 位。
自动清理
到目前为止,似乎一切都还不错,只是因为增加了 hash 以后出现了新问题:每次更改 js 和 css 文件以后 hash 字符串都会变化,这样一来 dist 目录下的文件会越来越多,非常不美观,也不便于识别。
此时可以使用一个名为 clean-webpack-plugin 的插件来实现对 dist 目录的自动清理:
npm i -D clean-webpack-plugin
然后修改配置文件:
// webpack.config.js
const CleanPlugin = require("clean-webpack-plugin");
module.exports = {
...
plugins: [
...
new CleanPlugin({
cleanStaleWebpackAssets: false // 不要清理 index.html 等资源
})
]
}
重新打包,可以看到 dist 目录非常干净了。
clean-webpack-plugin 清理的目录是由配置文件决定了,并不是说它只会清理 dist 目录,如果配置的输出目录是 build ,那么它就会清理 build 目录。
处理图片和字体
到目前为止,一切看起来都不错,前端可以将重点放到 main.js 的逻辑开发上面,而不用关注各种转码问题。不过一个前端也的展示除了三大块以外,还有一些静态资源,也被成为 assets
,这些也是一个页面的组成部分,常见的例如图片和字体。
使用 webpack 也可以对这些静态资源进行打包处理,这类文件通畅被称为 file ,所以使用 file-loader
进行处理。
npm i -D file-loader
安装完 loader 后修改webpack配置文件:
// webpack.config.js
module.exports = {
...
modules: {
rules: [
...
{
test: /\.(png|jpg|svg|jpeg|gif)/,
include: [path.resolve(__dirname, "assets")]
use: "file-loader"
},
{
test: /\.(tff|woff|woff2|ect|otf)/,
include: [path.resolve(__dirname, "src/style/fonts")],
use: ["file-loader"]
}
]
}
}
在 main.less 中加入样式,使用背景图片:
// main.less
.app {
backgound-image: url(../../../assets/bg.jpg);
}
重新打包,即可看的 dist 目录下输出的图片。
总结
以上是 webpack 的基础用法,虽然在实际工作中也用了很多,但不是每次都需要手动构建整个项目,所以这里重新整理和记录,好在以上配置基本上不用看文档手写就能写完,也算是webpack没有白用这么久。
流程化构建到这里,其实还存在一些不完美,例如 CSS/JS 的压缩、image 的优化压缩、快捷引用、自动刷新、热更新等一系列小问题,不过也没有关系,webpack 这篇博文本来就是计划用两篇文件来整理,本篇主要侧重在 流程化方面,实现 很方便 的目的,下一篇将整理自动化方面,实现 非常方便 的目的。
下一篇将围绕 自动化 来继续构建,自动化构建后的项目将更接近生产和发布版。