编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

如何”有效“减小js包的体积(js放大缩小)

wxchong 2024-09-10 22:29:25 开源技术 10 ℃ 0 评论

因为平时涉及移动端和C端项目都比较少,所以也不太注意js包的大小。直到这个礼拜五,实在忍不了了——赫然发现一个React前端组件的bundle竟然有400k,这还是uglify过的,gzip之后还有100k。

于是上午花了点时间,研究了一下怎么能让js包小一点。

之所以说“有效减小”,是因为有些自动化的方法,其实没有多大用处。

比如tree shaking,故事是很美好,但是我的代码普遍是直接跑到node_modules下面的子目录里面去引需要的文件,除非依赖库设计不良,否则tree shaking其实帮不上太大忙。再比如babel-plugin-import,由于同样的原因,也帮不上太大的忙。

那什么是有用的呢?其实就是笨办法,一个包一个包的裁剪。

推荐两个工具:

webpack-bundle-analyzer

地址:www.npmjs.com

webpack.github.io

地址:http://webpack.github.io/analyse/#home

?

webpack-bundle-analyzer

这个工具,顾名思义,就是用来分析你用webpack打的包里面都有些什么东西,以及他们有多大。

当你在你的webpack.config.js里添加了插件,比如这样——

var path = require('path')
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
var config = {
 //...
 plugins: [
 new BundleAnalyzerPlugin({
 analyzerMode: 'static',
 reportFilename: 'BundleReport.html',
 logLevel: 'info'
 })
 ]
}
module.exports = config

webpack构建完成后你会得到一个BundleReport.html网页,在你打包的生成路径里面,同时浏览器会自动打开它。你看到的是这样的画面——

这个网页非常直观,以至于我不需要介绍它的用法。

通过分析各个模块的体积,我发现为了防止体积膨胀,有两个常用库要慎用:

  • lodashmoment

这两个库都是前端编程的主流工具,lodash提供了几乎是业界最好用的基础操作函数,moment提供了时间日期格式转换功能。

然而这两个库都有点问题:

首先是lodash,表面上模块划分相当好,似乎只引一个lodash/isString几乎不会增加体积,然而lodash的函数之间互相引用的情况是很复杂的,你觉得你只引了一个函数,实际上你可能引了20个。所以我的建议是如果你只使用isString这样的简单函数,可以去npm上找更单纯的实现,或者干脆用typeof代替(不用太担心,String对象其实很少见)。

然后是moment,这个库最讨厌的一点在于它的多语言包,是默认全量引入的,压缩前大约200k,这个体积要比它本身的实现代码还大不少。如果你没有国际化的需求(很少有人需要项目支持葡萄牙语吧),请小心使用moment。你可以使用极简的dateformate代替,也可以通过ContextReplacementPlugin插件把moment的语言包裁剪掉,配置代码这样写——

var webpack = require('webpack')
module.exports = {
 // ...
 plugins: [
 new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /de|fr|hu/)
 ]
}

类似的情况大家可以举一反三,就不赘述了。

大部分情况下,使用第一个工具已经足够把不想要的依赖裁减掉了,但是有一种情况是,你看到了依赖,却不知道是由谁引入的,这种时候就要用一下第二个工具:http://webpack.github.io/analyse/#home。

这是webpack官方给出的一个工具,用来分析js bundle中各个chunk之间的依赖关系,通过它你可以很清楚的看到到底是谁引入了你不想要的包。

这种情况一般发生于想裁掉依赖库的依赖库,或者你已经把包裁的很小了,然而你还想更小的时候。

比如说,你看到你的包引入了timers-browserfy,但是你明明没有使用它,为什么会引入呢?使用analyse工具,你就会发现原来是因为你在代码里用了setImmidiat这个函数,打包的时候自动给你打进去了。当然这种情况不止会引一个包,还有些process,global什么的,都会出现。

还有一个可能会占你的包很大体积的库,就是core-js,如果你发现你的bundle里有大片的core-js代码,却不知道为什么会引入,那八成是因为你的某个底层依赖库使用了babel-plugin-runtime-transform这个插件转译es6代码。这种时候如果你不希望引入(比如你已经使用了全局的babel-polyfill),你就需要想办法更换底层依赖,或者干脆自己编译一个版本。

analyse工具的使用方式和webpack-bundle-analyzer不太一样,不需要在webpack config里面配置,而是使用命令行生成一个stat.json文件,上传到http://webpack.github.io/analyse/#home这个网站上去(不用担心泄密,这是个静态网站,所谓的“上传”,其实就是浏览器本地FileReader直接读文件)。

npm相关的命令配置如下——

"scripts": {
 "dep-analyze": "webpack --color --config ./webpack/debug.js --profile --json > ./package/DependenceNetwork.json"
}

通过以上笨办法,我把

yusangeng/viscum

?

github.com

这个库的体积由40k缩减到7k,这是一个小型的类React前端框架,7k比preact还是大了点,但是比它之前还是强多了。

最后,如果想从7k再缩减,缩减到preact那么小(3k),该怎么办呢?只能改代码。

首先是尽量使用es5的语法写代码(似乎有点反潮流,不过你都要写3k的前端框架了,对你的水平来说潮流已经无所谓了),因为babel转译会添加一些代码,尤其是class语法和async/await语法,会被添加不少东西。比如下面这些——

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

这些代码使用runtime-transform应该可以以require的形式引入,不过我已经把runtime-transform干掉了,就不专门试验了。

剩下的就是手上功夫了,同一个功能,巧妙的实现会比quick & dirty的实现短小。比如一个事件收发mixin功能,不打草稿写120行,精心写出来可能40行就搞定。

原文地址:https://zhuanlan.zhihu.com/p/44095804

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表