|

前端流程化:基于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 这篇博文本来就是计划用两篇文件来整理,本篇主要侧重在 流程化方面,实现 很方便 的目的,下一篇将整理自动化方面,实现 非常方便 的目的。

下一篇将围绕 自动化 来继续构建,自动化构建后的项目将更接近生产和发布版。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注