网站首页 > 开源技术 正文
因为平时涉及移动端和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
猜你喜欢
- 2024-09-10 Facebook发新版React,这些新功能让程序员直呼强大!
- 2024-09-10 React Native 网络层分析(react-native-router-flux)
- 2024-09-10 Brisk-Admin基于vue3+element-plus+vite开箱即用后台前端框架
- 2024-09-10 前端性能优化,你想知道的都在这里
- 2024-09-10 AngularJS2 教程—JavaScript 环境配置—架构大全第一章
- 2024-09-10 「免费开源」基于Vue和Quasar的动态表关系管理(六)
- 2024-09-10 2018年前端流行哪些技术?(推荐收藏)
- 2024-09-10 你还在用 for 循环遍历集合?试试 JavaScript 新增的 7 种方法吧!
- 2024-09-10 Vue开发中常用的ES6新特性(vue支持es几)
- 2024-09-10 在线编辑excel功能一次完整体验历程,以及可以避免的坑
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)