欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

项目构建分析和 webpack 优化实践

程序员文章站 2023-11-18 10:00:34
项目构建分析和 webpack 优化实践 最近接手在做一个chrom浏览器插件的项目,开发过程中发现项目打包的时间很长,足足有30多秒,这是让人很难接受的,而且构建的显示了几条包体积过大的提示信息: [image:073CB50B 06EB 4779 84FE D11087B12BD7 47140 ......

项目构建分析和 webpack 优化实践

最近接手在做一个chrom浏览器插件的项目,开发过程中发现项目打包的时间很长,足足有30多秒,这是让人很难接受的,而且构建的显示了几条包体积过大的提示信息:
[image:073cb50b-06eb-4779-84fe-d11087b12bd7-47140-0000087e666f3c39/1967fdc4-f9fa-44f3-922e-5406a46415fb.png]

可以看到,打包后有三个包超过了建议的体积,是什么导致了打包时间长和包的体积过大呢?下面通过一些具体方法来分析原因和解决这个问题。

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

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

npm install —save-dev webpack-bundle-analyzer

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

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

plugins: [
    ...,
    new bundleanalyzerplugin({
      analyzerport: 8081,
    }),
]

配置完成后再次运行构建 npm start,浏览器会自动打开 127.0.0.1:8001,在网页上可以看到每个依赖的详细信息。从图中可以找出影响体积的罪魁祸首有:

  • jquery
  • moment
  • xlsx
  • mammoth
  • html2canvas
  • dexie

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

减少 moment 大小

moment 在包中占用了 545k 的体积,查看分析图可以看到,库文件中主要是各种用于支持语言版本的的locale文件,但是项目中并不需要这部分功能,因此这部分数据是应该优化的。

[image:88a706f4-b93a-4e35-b0f1-1b01ca3f9e9b-47140-000008ad50a28b13/screen shot 2019-08-05 at 11.13.07 am.png]

查看项目中引入moment 的方式
import moment form ‘moment’;
这样会将整个 moment 包都导入到文件中,为了避免导入不必要的文件,可以这么写:

import moment from ‘moment/src/moment’

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

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

好了,重新启动服务进行打包,报错提示无法找到 ./locale,
[image:bbcbfa75-e5e1-4110-a22b-8d98c3d110bd-47140-00000da8c3052930/screen shot 2019-08-05 at 1.31.58 pm.png]

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

[image:795841eb-7214-4530-b2f1-7804b23de6c7-47934-0000adddaf534a3d/screen shot 2019-08-14 at 2.24.15 pm.png]
官方提供的解决方案是把 package.json 中的 moment 的版本改成 2.18.1,不过在 stack overflow 上找到了另一种解决方案,就是直接忽略这个报错:
[image:39119bd5-e067-47f0-b6fd-5760f4417818-47934-0000ae002ae61efb/69ae8969-0014-40e4-aa34-955676b4d3ad.png]

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

尝试一下:

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

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

import moment from 'moment'

//设置语言

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

moment.locale('zh-cn');

let r = moment().endof('day').fromnow();
console.log(r);

ok,这么一来能够显示中文,又把不必要的语言包都忽略打包了,重新构建一下,看一下体积有没有变化:
[image:b998dda8-62ad-4fd1-9953-a03bde08310d-47140-00000df75a4bfe04/4d8f9d51-4dae-4274-a3b2-73a2b6d50448.png]
可以看到包已经缩减到了1.67m,打包时间缩短了29s(减少了4s),对应的观察网页上的显示结果,moment 包的大小也从 545k 变成了 155.32k,小了很多,不是吗?
[image:de659edd-8ae4-4da8-932c-55d9ece12866-47140-000008b0769a7e9c/screen shot 2019-08-05 at 11.21.55 am.png]

使用 dllplugin 加快打包速度

在用 webpack 打包的时候,对于一些不经常更新的第三方库,比如 react,lodash,vue ,可以将这些库同项目代码分离开来,提前打包,从而每次只打包项目自身的代码,节省了打包时间。
如何使用 dllplugin | webpack 中文网 呢?
首先在 webpack 文件夹下新建 webpack.dll.conf.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

<script src=“./dist/static/dll.min.js”></script>

对应的,为了方便启动,在 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 中配置包含的库做一个索引,并写在 manifest 文件中,而引用 dll 的代码在打包的时候,只要读取这个 manifest 获取对应的库就可以了。

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

[image:c682b1b1-1c7e-4dc4-b999-39b5758d0564-47934-0000b256c82ee788/boundle info after.png]

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

[image:d11bd337-2369-4498-8d11-e12d087ce884-47934-0000b28971579a82/summary after.png]

总结

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

参考:
使用webpack的插件dllplugin加快打包速度 - 前端下午茶 - segmentfault 思否
dllplugin | webpack 中文网
dllplugin提升webpack打包速度 - 简书