加入新公司一个月,最近接手在做一个 chrom 浏览器插件的项目,开发过程中发现项目打包的时间很长,足足有30多秒,这是让人很难接受的,而且构建的显示了几条包体积过大的提示信息:

可以看到,打包后有三个包超过了建议的体积,是什么导致了打包时间长和包的体积过大呢?

下面通过一些具体方法来分析原因和解决这个问题。

什么原因导致构建包变得这么大?

为了分析是什么导致构建包为什么会变得这么大,可以安装 webpack-bundle-analyzer 插件,通过它可以直观地查看构建包中所有项目的大小。

npm install —save-dev webpack-bundle-analyzer

对应的需要在 webpack.config.js 中做如下配置:

const { BundleAnalyzerPlugin } = require(‘webpack-bundle-analyzer’)

plugins: [
    ...,
    new BundleAnalyzerPlugin({
        analyzerPort: 8081,
    }),
]

配置完成后再次运行构建 npm start,浏览器会自动打开 http://127.0.0.1:8081,在网页上可以看到构建包中每个文件的详细信息。

从图中可以找出影响体积的罪魁祸首有:

jquery、Moment、mammoth、html2canvas、xlsx、cpexcel、dexie

那么这些体积庞大的依赖库都需要打到项目的运行包里面吗?当然不是的。那我们逐步来优化这些依赖。

减少 moment 大小

moment 在包中占用了 545k 的体积,查看分析图可以看到,库文件中主要是各种用于支持语言版本的的locale文件,

但是项目中并不需要这部分功能,因此这部分数据是应该优化的。

查看项目中引入 moment 的方式

import moment form 'moment'

这样会将整个 moment 包都导入到文件中,为了避免导入不必要的文件,可以这么写:

import moment from 'moment/src/moment'

但是这么写会有个问题,如果项目中有新成员加入,极大的可能他不会这样写,而是像原来一样导入了整个 moment 包,因此为了避免这样的问题,可以考虑在 webpack 中创建一个别名,这样每次导入 moment 的时候就默认只导入文件夹下面的 moment.js 文件了,如下:

resolve: {
  alias: {
    moment: 'moment/src/moment',
  }
}

好了,重新启动服务进行打包,报错提示无法找到 ./locale:

查看 moment 的 官方 issue 发现这是一个存在已久的问题:moment.js 总是会加载 locales,还假定 locales 存在。你不能让 moment 只加载日期操作函数。

官方提供的解决方案是把 package.json 中的 Moment 的版本改成 2.18.1, 如下:

不过在 Stack Overflow 上找到了另一种解决方案

简单介绍一下 IgnorePlugin

  • 这是webpack内置插件
  • 作用:忽略第三方包指定目录,让这些指定目录不要被打包进去

尝试一下,在 webpack.config.js 中添加如下配置:

plugins:[
    // moment这个库中,如果引用了./locale/目录的内容,就忽略掉,不会打包进去
    new Webpack.IgnorePlugin(/\.\/locale/,/moment/),
]

按照上面的方法,减小了 moment 的打包体积,同时也避免了报错,但是如果项目中需要用到语言包该怎么办呢?很简单,手动引入一下就可以了:

import moment from 'moment'
//手动引入所需要的语言包
import 'moment/locale/zh-cn';
moment.locale('zh-cn');

const r = moment().endOf('day').fromNow();
console.log(r);

Ok,这么一来能够显示中文,又把不必要的语言包都忽略打包了,重新构建一下,看一下体积有没有变化:

可以看到包已经缩减到了1.67M,打包时间缩短了29s(减少了4s),对应的观察网页上的显示结果,moment 包的大小也从 545k 变成了 155.32k,小了很多,不是吗?

使用 DllPlugin 加快打包速度

在用 Webpack 打包的时候,对于一些不经常更新的第三方库,比如 react,lodash,vue ,可以将这些库同项目代码分离开来,

提前打包,从而每次只打包项目自身的代码,节省了打包时间。常用的方案是使用 DllPlugin。

如何使用 DllPlugin 呢?

首先在 webpack 文件夹下新建 webpack.dll.js文件

配置如下:

const webpack = require('webpack')
const path = require('path')

module.exports = {
  entry: {
    // manifest 的前缀名,这里会在webpack 文件下生成一个dll.manifest.json 文件
    dll: [
      'react',
      'react-dom',
      'antd',
      'classnames',
      'jquery',
      'xlsx',
      'mammoth',
      'html2canvas',
      'dexie',
      'cheerio', // 这些都是比较稳定,不常做修改的库文件
    ],
  },
  output: {
        // 指定在 dist/static 下生成一个  dll.min.js 文件
      path: path.join(__dirname, '../dist/static/'),
    filename: '[name].min.js',
    library: '[name]',
  },
  plugins: [
    new webpack.DllPlugin({
        // 指定在当前文件夹下生成 manifest 文件
      path: path.resolve(__dirname, './dll.manifest.json'),
      name: '[name]',
      context: __dirname,
    }),
    // 压缩,让包更小一点
    new webpack.optimize.UglifyJsPlugin({ minimize: true }),
  ],
}

配置完成后对应的需要在 webpack.config.js 中做如下修改:

const manifest = require('./dll.manifest.json')
plugins: [
   new webpack.DllReferencePlugin({
      context: __dirname,
       manifest,
   })]

然后在入口文件中引入 dll.min.js

对应的,为了方便启动,在 package.json 中添加快捷命令:

"scripts": {
    "dll": "webpack —config webpack/webpack.dll.js",
}

到这里,DllPlugin 的相关配置就完成了,打包的时候执行 npm run dll  会在 webpack 目录下生成 dll.manifest.json 文件,在 dist/static 目录下会生成 dll.min.js 文件,在打包过程中, webpack 会将 webpack.dll.js 中配置包含的库做一个索引,并写在 dll.manifest.json 文件中,而引用 dll 的代码在打包的时候,只要读取这个 manifest 获取对应的库就可以了。

生成的 dll.manifest.json 文件

最后执行 npm run build 测试打包速度:

发现现在的打包时间不到19秒,相比于原来的33s减少了将近一半,对应两个比较大的包体积也各自减少了2/3 还多。所以使用了 DllPlugin 之后,对项目的打包效率的提升还是很明显的。

总结

项目最开始开始构建,打包后需要将近 4M 的空间,通过手动修改 moment 库的引入方式和引入 webpack 的 DllPlugin 进行优化,打包后最终体积减少到了 1.2M,压缩了一半多,对应打包时间也缩短了将近一半,所以通过 webpack 进行打包优化还是很有效果的。这给我的启发是,在实际开发和打包上线过程中,需要细致地评估项目的构建体积和打包时间,通过 webpack-bundle-analyzer 可以直观的观察构建包的构成和体积分布,并且根据分析的结果有针对性地进行优化,以此来精简项目体积,提升应用效率。当然,打包优化的方式不仅限于此,还可以通过 HappyPack 利用 Node 的多线程充分使用电脑多核来提升构建速度(但是实际效果不一定会变快),此外,还可以使用 webpack 的 externals 不打包某些文件,而在其他地方通过 cdn 引入,利用缓存下载 cnd 文件达到减少打包时间的目的,有兴趣可以在项目中尝试,相信你会有很多收获。

参考:

DllPlugin | webpack 中文网

DllPlugin提升webpack打包速度

使用webpack的插件DllPlugin加快打包速度 - 前端下午茶

项目构建分析和 webpack 优化实践的更多相关文章

  1. Vue实战Vue-cli项目构建(Vue+webpack系列之一)

    用Vue比较长一段时间了,大大小小做了一些项目,最近想总结一下知识点,出一个Vue+webpack系列,先从项目构建说起--vue-cli. 由于是Vue+webpack这里就不赘述git那些东西,默 ...

  2. React项目构建(利用webpack打包)

    引言 最近React作为当前最为火热的前端框架.最近也相继而出来相关ES7的新语法. 当然,在使用React开发web项目的时候,不得不提到的就是与之配套的相应的打包技术,之前上文已经简单的提到Rea ...

  3. webpack 教程 那些事儿04-webpack项目实战分析

    这节主要讲解真正项目用用到的 webpack配置问题,项目实战篇 就像我们不会完全做一个项目,不用别人的轮子一样.这个配置我们借用 vue-cli 搭建的配置来研究,因为它已经足够优秀. 有了前面的基 ...

  4. paip.前端加载时间分析之道优化最佳实践

    paip.前端加载时间分析之道优化最佳实践 1.另存为 ,查看文件尺寸..和图片. 2.view the 另存为的htm静态的文件单个的加载,看时间...可以排除编程语言的问题and 数据库.. ## ...

  5. 基于webpack+react+antd 项目构建

    工欲善其事必先利其器,学习React也是如此. 下面分享一篇基于webpack+react+antd 项目构建的好文章, https://blog.hduzplus.xyz/articles/2017 ...

  6. WEB项目构建优化之自动清除CSS中的图片缓存

    在web项目构建发布时,经常遇到css中图片的修改优化,那么如何清除图片的缓存成为必须要解决的问题.曾经有过傻傻的方法就是直接在图片后面添加随机数.今天主要是从构建自动化方式来解决这个问题,提高开发及 ...

  7. HoloLens开发与性能优化实践

    HoloLens中国版终于于5月底在中国上市,同时国内的技术社区经过一年的成长也有了很大的扩张,越来越多的开发者开始进入了HoloLens开发领域,尝试着使用混合现实(Mixed Reality)技术 ...

  8. Unity3D游戏GC优化总结---protobuf-net无GC版本优化实践

    protobuf-net优化效果图 protobuf-net是Unity3D游戏开发中被广泛使用的Google Protocol Buffer库的c#版本,之所以c#版本被广泛使用,是因为c++版本的 ...

  9. vue项目首屏加载优化实战

    问题 单页面应用的一个问题就是首页加载东西过多,加载时间过长.特别在移动端,单页面应用的首屏加载优化更是绕不开的话题.下面我会写出我在项目中做的一些优化,希望大家能够相互讨论,共同进步. 我的项目vu ...

  10. webpack优化相关操作

    1.缩小文件搜索的范围 • 优化loader配置 尽量精确使用 include 只命中需要的文件.    module.exports = {      module: {        rules: ...

随机推荐

  1. 2016第二届陕西省网络空间安全大赛WriteUp

    2016年5月28号(正式比赛) 有选择题和实践题,俩队员在弄选择题时,我去拿了web1的一血. 0x01 web 是一道代码审计题,发包,返回了源代码: <?php if (isset($_G ...

  2. String or binary data would be truncated 解决办法

    原因: 一般出现这个问题是因为数据库中的某个字段的长度小,而插入数据大 解决: 找到相应字段,修改表结构,使表字段大小相同或大于要插入的数据

  3. codeforces 496A. Minimum Difficulty 解题报告

    题目链接:http://codeforces.com/contest/496/problem/A 题目意思:给出有 n 个数的序列,然后通过删除除了第一个数和最后一个数的任意一个位置的数,求出删除这个 ...

  4. easyui combotree 默认 初始化时就选中

    做得权限管理系统,用combotree来控制权限,combotree是通过后台json来初始化,但是前台不是选中状态. 下面是核心代码已经标红. $('#txtTree').combotree({ m ...

  5. jquery TypeError: &#39;undefined&#39; is not a function (evaluating &#39;elem.nodeName.toLowerCase()&#39;) [jquery.js:1904]错误原因

    今天,某个环境报了个js错误,TypeError: 'undefined' is not a function (evaluating 'elem.nodeName.toLowerCase()') [ ...

  6. Android 4.3正式发布:四大新功能一览

    在旧金山举行的新品发布会上,Google正式发布了Android 4.3,代号仍为“Jelly Bean”.此次更新并没有太大改变,只是紧跟4.1.4.2步伐, 新增了低功耗蓝牙.多用户登录等一系列功 ...

  7. unity, ugui input field

    ugui Input Field,获取输入的字符串. 错误方法: string content=inputField.FindChild("Text").text; 这样得到的是输 ...

  8. ubuntu源码安装R语言

    下载后解压完,进入开始配置: ./configure --enable-R-shlib 报错: configure: error: con--with-readline=yes (default) a ...

  9. SQLite: sqlite_master

    SQLite数据库中一个特殊的名叫 SQLITE_MASTER 上执行一个SELECT查询以获得所有表的索引.每一个 SQLite 数据库都有一个叫 SQLITE_MASTER 的表, 它定义数据库的 ...

  10. QScriptEngine

    其实你有好多没有介绍 比如qt文字 我一直很迷惑qt的文字的长宽 qt文字的字间距 等等这些东西还有QProcess QProcess可能是qt调用c#的唯一方法了QScript要比你想象的重要,一个 ...