如何有效提升快应用(Webpack)编译速度

快应用 Jul 22, 2020

背景

在开发快应用时,少不了构建操作:npm run build(官方 IDE 集成了这些操作,本质上也是调用一样的方法)。这是因为快应用有自己的 DSL 语法,直接这样写出来,在底层是不认识的,需要把业务代码编译成底层识别的样子(感兴趣的同学,可在 build 文件夹查看编译后的代码产物)。整个过程就跟 Vue 和 React 工程的打包一样。等待的过程总是漫长,当项目越来越大的时候,难免还要花上那么点时间。大家都希望这个过程越快越好。

快应用工程是基于 hap-toolkit 编译打包的。而它的功能,部分是基于 Webpack 开发。所以下面跟大家分享的,关于提升快应用编译速度的方法,同样也适用于基于 Webpack 构建的 Web 应用。

基于插件

不过毕竟是 Webpack 上的封装,所以不是所有方法都可以用上。现在 hap-toolkit 支持一些自定义配置了,可以使用部分 loader 和 plugin。详情请查看 ToolKit 项目配置 里面有自定义 webpack plugin 的代码示例。我们这里要讲的就是使用 hard-source-webpack-plugin 插件来为编译加速。

对于做过 Webpack 性能优化的同学,可能有用到过 HardSourceWebpackPlugin 插件,用于为模块提供中间缓存步骤。它能明显提升第二次构建速度:

HardSourceWebpackPlugin is a plugin for webpack to provide an intermediate caching step for modules. In order to see results, you'll need to run webpack twice with this plugin: the first build will take the normal amount of time. The second build will be significantly faster.

安装

yarn add --dev hard-source-webpack-plugin
// OR
npm i hard-source-webpack-plugin --save-dev

使用

在项目根目录下增加 quickapp.config.js 文件,做如下代码配置:

const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
module.exports = {
  webpack: {
    plugins: [new HardSourceWebpackPlugin()],
  },
};

如上,一点简单的配置,即可轻松使用。当然,也可以通过添加参数,来“量身”定制。下面拿快应用官方 Sample 来看一下。

参数介绍

首先,完全的第一次编译,会花费较长时间。这是因为编译过程需要依赖到 babel 模块,在 webpack 里它化身为 babel-loader,编译过程需要解析成 AST,再转换成我们要的输出格式(对该部分知识感兴趣的同学可自行查阅)。这一过程极为耗时,所以作为一个有担当的依赖,它也应该有自己的缓存。
这一配置已经在 hap-toolkit 写好了。可以在项目目录下node_modules/.cache/babel-loader看到 babel 缓存。

使用 hard-source-webpack-plugin 插件,编译速度会有显著提高。
在上述存放缓存的地点,可以发现多了个 hard-source 的文件夹,里面正是存放着插件生成的缓存。再看看一些参数的介绍,我们直接在代码上以注释的形式展示:

new HardSourceWebpackPlugin({
  // 缓存存放的地址,以 webpack 的执行目录加该字段拼接而成,一般都是存放于项目目录下
  // 也可以写成绝对路径,存放到别的地方
  cacheDirectory: "node_modules/.cache/hard-source/[confighash]",
  // 缓存文件夹的名字生成方式,这里的值对于上方的 configHash
  configHash: function (webpackConfig) {
    return require("node-object-hash")({ sort: false }).hash(webpackConfig);
  },
  // 环境hash,其实就是监听依赖有没有更改,有的话也更新缓存
  // 一般 files 填一个 package-lock.json 也够了
  environmentHash: {
    root: process.cwd(),
    directories: [],
    files: ["package-lock.json", "yarn.lock"],
  },
  cachePrune: {
    // 缓存的存在时间,默认为两天
    maxAge: 2 * 24 * 60 * 60 * 1000,
    // 缓存的最大容量,默认为 50 MB
    sizeThreshold: 50 * 1024 * 1024,
  },
});

需要注意下,如果用官方 IDE 来打开项目,由于 IDE 的运行路径,与项目路径不一致,会导致报错,需要设置 environmentHash 来修正:

new HardSourceWebpackPlugin({
  environmentHash: {
    root: __dirname,
  },
});

基于文件操作

一 移除 source-map

source-map,简而言之,就是编译后的代码与源代码的一个映射。在进行代码调试时候,执行环境运行的是编译后的代码,但可以看到对应的源代码;最常见的就是浏览器中,通过打断点,定位到源代码去,这样就可以发现源代码出现的问题。

通过阅读 Webpack Devtool 文档,可以知道,source-map 构建也需要时间;且跟还原度也有关系,越精确越耗时间,尤其是值为 source-map 模式。

hap-toolkit 默认设置,release 就是设置为 “none”;debug 模式由于要考虑到还原度,选择了 “cheap-eval-source-map”。那么,如果平时无需 source-map,可以直接将其设置为 “none”,即可进一步加快速度。同时,也会带来问题,即调试之时,无法精确对应源代码。您可以有两种方式,来关闭 source-map

  1. 通过修改 quickapp.config.js 配置:
module.exports = {
  cli: {
    devtool: "none",
  },
};
  1. 通过命令行
// 此方法,在 IDE 中不适用
npm run build -- --devtool none

这个对于速度有小幅提升。这个虽然效果不如缓存提升得那么明显,但是胜在作用于每次打包,对于首次打包也是有效的。

二 减少编译代码

我们知道,编译速度跟代码量也有关系,那么减少“需要编译的文件”,当然就可以加快速度。开发时候,通常都是一个或者几个相关页面来开发。所以开发过程中可以只编译当前需要的页面。
hap-toolkit 收集页面是通过 manifest.json 的 router.page 配置,而不是通过工程的 src 下具体文件。所以可以先去掉不相干的页面配置,而不需要去除实际文件。
最简单情况可以只留下单个页面,对比起好几个甚至十几个页面的编译速度,提速效果还是相当明显的。

三 加快文件搜索

代码量对编译速度有影响,自然的,寻找需要编译的文件也要花时间的。
我们在代码中对文件的引用,写了很多相对路径,而且也习惯不写文件后缀。webpack 没有那么神可以洞察开发者内心的诉求,而是根据配置好的搜索条件,不断地循环查找。
webpack 的 resolve 配置就是干这个活的。

我们摘取其中实用的来说,先上代码,在快应用中 quickapp.config.js 配置:

const path = require("path");
module.exports = {
  webpack: {
    resolve: {
      alias: {
        "@src": path.resolve(__dirname, "src"),
      },
      modules: ["./src/components"],
    },
  },
};

1、resolve.modules:配置 Webpack 去哪些目录下寻找第三方模块,默认配置是去 node_modules 目录下寻找。
有时做了一个公用组件库,给不同的地方调用。在不同地方的引入,就有可能导致这个路径会很长,如 import '../../../components/button'。这时可以利用 modules 配置项优化,假如组件库在 ./src/components 目录下,就可以给 modules 增加'./src/components'这样一个地址,供 webpack 快速命中资源位置。

2、resolve.alias: 给路径起别名。当代码中使用了众多相对路径,不仅我们去找这个文件费劲,webpack 也是需要时间去解析。所以直接配置好绝对路径,在代码中用别名代替冗长的路径。于编译解析,于代码书写,都是利好,两开花!

3、resolve.extensions: 则是告知 webpack 获取哪些类型的文件。比如:['js', 'ux'],当我们引用文件是这样写 import util from './util',在定位到目录之后,就开始按照 util.js -> util.ux 的顺序去寻找该文件。
也就是这个字段配置作用是方便了代码的书写,但是增加 webpack 的搜索时间(故此字段仅为介绍,无需增加配置)。所以如果代码中精确到完整文件名,在大工程多引用文件数的情况下,也是能节省一笔可观的时间消耗哟。

随着工具的升级,及开放自定义程度的提高,整体编译速度也会不断提升,敬请期待。

vivo developer

快应用引擎、工具开发者、快应用生态拓展达人(vivo)。

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.