likes
comments
collection
share

给webpack定制一个酷炫的启动页

作者站长头像
站长
· 阅读数 9

本文是azuo和萌妹俩技术创作之旅的第10篇原创文章,内容创作@azuo😄,精神支持@大头萌妹😂

前言:本文将围绕 webpack 的 DevServer 来定制酷炫的构建进度启动页。它越过命令行的限制,做成浏览器页面,可以做的非常酷炫、花里胡哨的。通阅读本文可以理解webpack如何获取构建进度和DevServer请求拦截,讲讲 CS架构 实现跨进程通信是如何越过命令行,完成原本在命令行显示的进度条改成交互更加丰富的浏览器中展示。

一、效果展示

直接上效果展示😄

给webpack定制一个酷炫的启动页

注意观察命令行的进度和浏览器的进度的步调基本是一致的哟

二、原理讲解

使用 webpack 进行项目打包,在开发模式启动一般会使用 webpack-dev-server ,进行构建监听和页面效果预览。 webpack 只是提供构建代码的能力,而热更新能力则是webpack-dev-server提供,它可以编辑器修改代码文件,浏览器会自动更新对应的内容,解放 F5 键😄。

原本是命令行中进度数值,通过使用CS架构(启动一个本地 Express 服务器和浏览器进行通信)转移到浏览器端。

webpack-dev-server是本质是一个Express服务器, 内部使用webpack-dev-middleware中间件,来提供热更新的。 在项目目录下执行 webpack-dev-server 命令后,会本机会启动一个本地 Express 服务器和浏览器进行通信(热刷新也是这样实现的😄)。webpack 在项目启动是首次构建,耗时很长,在这个段时间内,就可以先展示一个已经构建的启动页面,等构建完了再替换项目的页面。

那么,我们要实现一个带构建进度条的启动页面,需要两部分功能:

  • 启动页面上展示的进度,则在Express服务上增加一个新的接口:获取当前构建的状态返回接口。

  • 在首次构建时候劫持 项目的index.html 替换 启动页面的index.html , 再可以通过ajax访问进度接口来获取到当前进度。

整体流程如下:

给webpack定制一个酷炫的启动页

三、代码实现

这章将讲解如何如何获取当前构建的进度和替换项目页面。

3.1 获取构建进度

webpack 提供了 ProgressPlugin ,应用该插件可以轻松获取当前构建的进度。为了获取进度,只需在 webpack.config.js配置该插件即可。

ProgressPlugin 通过回掉函数来更新构建进度,具体调用如下:

/**
* @param percentage 当前构建进度,取值范围[0,1]
* @params message 构建模块信息
*/
const handler = (percentage, message, ...args) => {
  // e.g. Output each progress message directly to the console:
  console.info(percentage, message, ...args);
};

new webpack.ProgressPlugin(handler);

3.2 修改 devServer.before

devServer.before 的配置项作用是在服务内部的所有其他中间件之前, 提供执行自定义中间件的功能。启动页面在该配置项要完成两个功能:

  • 提供获取进度接口;
  • 在未完成首次构建前,替换项目的html页面请求;

devServer.before,说白就是可以拦截和新增请求

3.2.1 获取构建进度的接口

通过 ProgressPlugin 插件获得的进度, 在 Express 中新增一个接口来返回给浏览器,具体内容如下:

// webpack.config.js配置内容
const webpack = require('webpack');

// 存构建进度
let progress = 0

module.exports = {
   plugins: [
       new webpack.ProgressPlugin(function(percentage) {
          // 获取当前构建进度
          progress = percentage
       })
   ],
   devServer: {
       open: true, // 记得打开哈
       before: (app, server) => {
          // 获取进度
          app.use('/__progress', (_res, res)=> {
            res.json({progress});
          });
       }
   }

}

devServer.open 记得设置成 true, 不然,webpack开发模式下启动,就不会去自动打开浏览器,启动页面就没办法展示了~。

3.2.2 劫持项目html请求

通过 ProgressPlugin 插件获得的进度,判断是否首次构建完成(progress为1,则首次构建完成):

  • progress < 1, 替换项目html为启动页面的html内容;
  • progress = 1, 直接返回项目html,html内容拦截失效;
// webpack.config.js 配置内容:
const webpack = require('webpack');

// 存构建进度
let progress = 0module.exports = {
   plugins: [
       new webpack.ProgressPlugin(function(percentage) {
          // 获取当前构建进度
          progress = percentage;
       })
   ],
   devServer: {
       open: true, // 记得打开哈
       before: (app, server) => {
          // 获取进度
          app.use('/__progress', (_res, res)=> {
            res.json({progress});
          });
          
          // 劫持 index.html
          app.get('/', (_req, res, next) => {
            // 进度 < 100%,劫持请求,返回启动页面的html
            if (progress < 1) {
              res.set('Content-Type', 'text/html');
              const htmlPath = path.resolve(__dirname, './start-page.html');
              const html = fs.readFileSync(htmlPath, { encoding: 'utf-8' });
              res.send(html);
            } else { // >=100%,交回 webpack-dev-server 来处理
              next();
            }
          });
       }
   }

}

3.3 启动页面实现

前面已经说明:接口的具体实现,接下来浏览器的页面只要通过ajax轮训的方式发请求就可以获取首次构建进度。

start-page.html文件具体内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="utf-8">
 <title>启动页面</title>
 <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1,minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
</head>

<body>
  <div id="pg" style="color: red;font-size:24px"></div>
  <script>
    (function() {
      function getProgress() {
        return fetch('/__progress').then(res=>res.json());
      }

      const $progress = document.querySelector('#pg');
      // eslint-disable-next-line no-unused-vars
      function freshProgress() {
        getProgress().then((res)=>{
          const progress = res.progress;
          $progress.innerText = `构建进度:${Math.ceil(progress * 10000) / 100}%`;
          if (progress < 1) {
            setTimeout(freshProgress, 500);
          }
        });
      }
      freshProgress();
    })();
  </script>
</body>
</html>

四、总结

传统的构建工具交互信息一般通过命令行输出给用户。交互反馈能力薄弱。如果,通过 CS架构把原本输出在命令行下贫弱的交互方式改成在浏览器页面中丰富的交互方式中。这样,整个思路就可以打开了,构建工具产品形态将会对用户体验更加友好。