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


    前言

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

    作为系列文章的第一篇。我们会重点介绍 Webpack 中的 resolve.alias ,也就是请求重定向。

    只是请注意 Webpack 里的请求是对模块的依赖,也就是一个 require语句,而不是一个 HTTP 请求。

    必要的准备

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

    样例:本地时钟

    要实现的功能非常easy。就是在页面上用中文显示当前时间,须要用到 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 project师简历请发 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;


    本文系OneAPMproject师原创文章。

    OneAPM是中国基础软件领域的新兴领军企业,能帮助企业用户和开发人员轻松实现:缓慢的程序代码和SQL语句的实时抓取。想阅读很多其他技术文章。请訪问OneAPM官方技术博客

  • 相关阅读:
    Hibernate课程 初探一对多映射3-1 单向多对一简介
    Hibernate课程 初探一对多映射2-8 set元素属性
    Hibernate课程 初探一对多映射2-7 测试-修改和删除学生信息
    vue.js源码学习分享(二)
    vue.js源码学习分享(一)
    用百度地图做了一个输入地址查询经纬度的小例子
    今天碰到的400错误
    ajax同步
    json键的不能像值一样拼写的问题
    日期格式化
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5416045.html
Copyright © 2020-2023  润新知