Webpack 性能优化 (一)(使用别名做重定向)

原创 2015年07月09日 23:03:47

前言

Webpack 是 OneAPM 前端技术栈中很重要的一部分,它非常好用,如果你还不了解它,建议你阅读这篇 Webpack 入门指迷 ,在 OneAPM 我们用它完成静态资源打包,ES6 代码的转换 ,React 组件的组织等,在接下来的日子里,我们将通过一系列文章和业界分享我们在使用 Webpack 过程中关于性能方面的经验。

作为系列文章的第一篇,我们会重点介绍 Webpack 中的 resolve.alias ,也就是请求重定向。不过请注意 Webpack 里的请求是对模块的依赖,也就是一个 require语句,而不是一个 HTTP 请求。

必要的准备

  • 需要你有一定的 Node.js 基础
  • 电脑上装有最新版的 Webpack (npm install webpack -g)
  • 了解 Webpack 配置文件的格式

例子:本地时钟

要实现的功能很简单,就是在页面上用中文显示当前时间,需要用到 moment 这个库,这个库封装了很多和日期相关的函数,而且自带了国际化的支持。

新建一个 Node.js 项目

使用 npm init 初始化你的项目,然后通过npm install moment -D加上 moment 的开发者依赖。

新建一个entry.js作为入口文件,当然你也可以用 app.js 这样的名字,只是大部分的 Webpack 示例都是用的是 entry.js

var moment = require('moment');
document.write(moment().locale('zh-cn').format('LLLL'));

新建一个页面index.html, 引用 bundle.js:

<body>
<h5>当前时间:</h5>
<script src="dist/bundle.js"></script>
</body>

此时的文件目录看起来是这样的:

index.html
package.json
entry.js
node_modules/moment

到目前为止 bundle.js 这个文件还不存在,不过别着急,接下来的工作就交给 Webpack 来完成。

index.html  ------------------------+               
package.json                        |               
                                    +--> <Clock App>
entry.js    --------+               |               
                    +-->bundle.js+--+               
node_modules/moment-+ 

如图,Webpack 会把 entry.jsmoment模块一起打包成一个 bundle.js 文件,和 index.html 一起构成了我们的 Clock App。怎么样,是不是已经听到 Clock App 滴答作响了?

使用 webpack 打包代码

在命令行执行:

webpack --entry ./entry.js --output-path dist --output-file bundle.js

你会看到类似下面的输出结果:

Hash: bf9007fb1e0cb30e3ef7
Version: webpack 1.10.0
Time: 650ms
    Asset    Size  Chunks             Chunk Names
bundle.js  378 kB       0  [emitted]  null
   [0] ./entry.js 125 bytes {0} [built]
    + 86 hidden modules

可以看到,耗时 650ms,这么慢着实让人意外,一定要想办法提高“新一代神器”速度;另一方面,最后一行的 + 86 hidden modules 非常让人怀疑:明明是一个简单的 Clock App,怎么会有这么多的依赖。

如何快速定位 Webpack 速度慢的原因

再一次,在命令行输入:

webpack --entry ./entry.js --output-path dist --output-file bundle.js \
--colors \
--profile \
--display-modules

不过这次新增加了三个参数,这三个参数的含义分别是:

  • --colors 输出结果带彩色,比如:会用红色显示耗时较长的步骤
  • --profile输出性能数据,可以看到每一步的耗时
  • --display-modules默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块
    这次命令行的结果已经很有参考价值,可以帮助我们定位耗时比较长的步骤
Hash: bf9007fb1e0cb30e3ef7
Version: webpack 1.10.0
Time: 650ms
    Asset    Size  Chunks             Chunk Names
bundle.js  378 kB       0  [emitted]  null
   [0] ./entry.js 125 bytes {0} [built]
       factory:11ms building:8ms = 19ms
   [1] ../~/moment/moment.js 102 kB {0} [built]
       [0] 19ms -> factory:7ms building:141ms = 167ms
   [2] (webpack)/buildin/module.js 251 bytes {0} [built]
       [0] 19ms -> [1] 148ms -> factory:132ms building:159ms = 458ms
   [3] ../~/moment/locale ^\.\/.*$ 2.01 kB {0} [optional] [built]
       [0] 19ms -> [1] 148ms -> factory:6ms building:10ms dependencies:113ms = 296ms
   [4] ../~/moment/locale/af.js 2.57 kB {0} [optional] [built]
       [0] 19ms -> [1] 148ms -> [3] 16ms -> factory:52ms building:65ms dependencies:138ms = 438ms
                  ..... 广告分割线,Node.js 工程师简历请发 nodejs@oneapm.com ......
   [85] ../~/moment/locale/zh-cn.js 4.31 kB {0} [optional] [built]
        [0] 22ms -> [1] 162ms -> [3] 18ms -> factory:125ms building:145ms dependencies:22ms = 494ms
   [86] ../~/moment/locale/zh-tw.js 3.07 kB {0} [optional] [built]
        [0] 22ms -> [1] 162ms -> [3] 18ms -> factory:126ms building:146ms dependencies:21ms = 495ms

从命令行的结果里可以看到从 Request[4] 到 Request[86] 都是在解析 moment.js附带的大量本地化文件。所以我们遇到的速度慢的问题其实是由 moment引起的。

如果你想知道为什么 Webpack 会加载这么多的模块,可以参考这篇文章 Why Enormous Locales During Webpack MomentJS

我们再来看看 entry.js代码的第一行,标准的 CommonJS写法:

var moment = require('moment');

也就是说,请求的是 moment的源码。实际上,通过 NPM 安装moment 的时候会同时安装 moment 的源码和压缩后的代码,试验证明下面这种写法也是可行的:

var moment = require('moment/min/moment-with-locales.min.js');

只不过这样改,可读性会有所下降,而且每一个用到moment 的地方都得这么写。另外,如果同样的问题出现在第三方模块中,修改别人代码就不那么方便了。下面来看看用 Webpack 怎么解决这个问题。

在 Webpack 中使用别名

别名(resolve.alias) 是 Webpack 的一个配置项,它的作用是把用户的一个请求重定向到另一个路径,例如通过修改 webpack.config.js配置文件,加入:

  resolve: {
    alias: {
        moment: "moment/min/moment-with-locales.min.js"
    }
  }

这样待打包的脚本中的 require('moment'); 其实就等价于 require('moment/min/moment-with-locales.min.js'); 。通过别名的使用在本例中可以减少几乎一半的时间。

Hash: cdea65709b783ee0741a
Version: webpack 1.10.0
Time: 320ms
    Asset    Size  Chunks             Chunk Names
bundle.js  148 kB       0  [emitted]  main
   [0] ./entry.js 125 bytes {0} [built]
       factory:11ms building:9ms = 20ms
   [1] ../~/moment/min/moment-with-locales.min.js 146 kB {0} [built] [1 warning]
       [0] 20ms -> factory:8ms building:263ms = 291ms
   [2] (webpack)/buildin/module.js 251 bytes {0} [built]
       [0] 20ms -> [1] 271ms -> factory:3ms building:1ms = 295ms

WARNING in ../~/moment/min/moment-with-locales.min.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./locale in */webpack_performance/node_modules/moment/min
 @ ../~/moment/min/moment-with-locales.min.js 1:2731-2753

Webpack中忽略对已知文件的解析

module.noParsewebpack 的另一个很有用的配置项,如果你 确定一个模块中没有其它新的依赖 就可以配置这项,webpack 将不再扫描这个文件中的依赖。

  module: {
    noParse: [/moment-with-locales/]
  }

这样修改,再结合前面重命名的例子,更新后的流程是:

  • webpack 检查到 entry.js 文件对 moment的请求;
  • 请求被 alias 重定向,转而请求 moment/min/moment-with-locales.min.js;
  • noParse 规则中的 /moment-with-locales/一条生效,所以 webpack 就直接把依赖打包进了 bundle.js
Hash: 907880ed7638b4ed70b9
Version: webpack 1.10.0
Time: 76ms
    Asset    Size  Chunks             Chunk Names
bundle.js  147 kB       0  [emitted]  main
   [0] ./entry.js 125 bytes {0} [built]
       factory:13ms building:13ms = 26ms
   [1] ../~/moment/min/moment-with-locales.min.js 146 kB {0} [built]
       [0] 26ms -> factory:13ms building:5ms = 44ms

时间进一步被压缩,只需要 76ms,比前一步还减少了 75%。

在 Webpack 中使用公用 CDN

Webpack 是如此的强大,用其打包的脚本可以运行在多种环境下,Web 环境只是其默认的一种,也是最常用的一种。考虑到 Web 上有很多的公用 CDN 服务,那么 怎么将 Webpack 和公用的 CDN 结合使用呢?方法是使用 externals声明一个外部依赖。

  externals: {
    moment: true
  }

当然了 HTML 代码里需要加上一行

<script src="//apps.bdimg.com/libs/moment/2.8.3/moment-with-locales.min.js"></script>

这次打包,结果只用了 49 ms,几乎达到了极限。

总结

本文结合本地时钟的例子,展示了定位 Webpack 性能问题的步骤,以及所需要的两个参数 :--display-modules--profile。然后,重点介绍了 resolve.alias 即利用别名做重定向的方法和场景,在此基础上,配合module.noParse 忽略某些模块的解析可以进一步加快速度。最后介绍了用 externals 定义外部依赖方法来使用公用 CDN。

关于

本文相关的源码在: https://github.com/wyvernnot/webpack_performance/tree/master/moment-example;


本文系OneAPM工程师原创文章。OneAPM是中国基础软件领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和SQL语句的实时抓取。想阅读更多技术文章,请访问OneAPM官方技术博客

版权声明:本文为博主原创文章,未经博主允许不得转载。

关于常用路径可在 webpack 中注册以避免代码中过长的引入路径的思考

在进行大型项目的时候,文件里面套文件,层叠太多,会导致引入的文件名过长。过长。。。是吧,就不美了。那么问题来了,怎么解决? 一、对于模块引用路径 我们可以采取这样的措施,在webpack里面注册常...
  • qq_24122593
  • qq_24122593
  • 2016年12月19日 19:05
  • 1224

Webpack中使用相对路径定义别名

var path = require('path'); resolve: { //自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名 exte...
  • xiaoguang44
  • xiaoguang44
  • 2017年04月20日 17:43
  • 2014

webpack中alias别名配置

webpack的alias匹配问题初现在模块开发过程中,我们可能会对可以复用的组件封装成一个可被git管控的模块,并在引用的过程中采用 带版本号的方式引用 ,这就要求我们在webpack.中添加相关a...
  • fengyun125a
  • fengyun125a
  • 2016年12月23日 01:30
  • 3068

webpack对样式的处理 ,导入样式require和import的区别

我们可以在js中引入样式文件 require('myStyle.css') 这时我们便需要引入相应的webpack loader来帮助我们解析这段代码。 一般来说需要引入css-...
  • chengnuo628
  • chengnuo628
  • 2016年09月08日 21:44
  • 15058

webpack 语法(精华篇)

WebPack是什么 一个打包工具一个模块加载工具各种资源都可以当成模块来处理网站 http://webpack.github.io/ 如今,越来越多的JavaScript代码被使...
  • github_37360787
  • github_37360787
  • 2017年02月07日 21:57
  • 903

前端--React之webpack使用教程

记住:一定要一步一步的跟着执行,不要漏过任何一部项目的文件架构为: 最近在学习react,记录一下在学习webpack过程中的经历,希望帮助新人少走弯路。 1、新建一个测试目录,这里叫做React...
  • zhuzhiqiang_zhu
  • zhuzhiqiang_zhu
  • 2016年12月07日 15:03
  • 1907

如何十倍提高你的webpack构建效率

前言webpack 是个好东西,和 NPM 搭配起来使用管理模块实在非常方便。而 Babel 更是神一般的存在,让我们在这个浏览器尚未全面普及 ES6 语法的时代可以先一步体验到新的语法带来的便利和效...
  • u011413061
  • u011413061
  • 2016年07月10日 17:25
  • 7566

webpack前段构建性能优化策略小结

方案一、合理配置CommonsChunkPlugin    webpack的资源入口通常是以entry为单元进行编译提取,那么当多entry共存的时候,CommonsChunkPlugin的作用就会...
  • weifenwang
  • weifenwang
  • 2017年06月07日 16:31
  • 405

webpack打包过程中因为图片的路径导致的问题

最近在制作一个自己的个人博客的时候遇到这么一个问题, 在CSS中使用了相对路径来充当背景图片, 如下所示: 然后将整个工程使用webpack打包之后, 在浏览器上运行却报错了, 报错如下: ...
  • u012863664
  • u012863664
  • 2017年05月23日 15:51
  • 9030

Apache 别名与重定向

转载自:http://my.oschina.net/davehe/blog/101414 当apache接受请求时,在默认情况下会将DocumentRoot目录中的文件送到客户端,如果想将一...
  • doushen1
  • doushen1
  • 2014年03月27日 18:52
  • 1375
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Webpack 性能优化 (一)(使用别名做重定向)
举报原因:
原因补充:

(最多只允许输入30个字)