共计 5026 个字符,预计需要花费 13 分钟才能阅读完成。
(1)荡涤 moment
语言环境文件
默认状况下,当您编写 var moment = require('moment')
代码并应用 webpack
打包时,捆绑文件的大小会变得很重,因为 webpack
会捆绑 所有Moment.js
所有语言环境文件(在 Moment.js 2.18.1
中,压缩后的 KB
为 160
)。
要去除不必要的语言环境并仅捆绑应用的语言环境,请增加moment-locales-webpack-plugin
:
// webpack.config.js
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
module.exports = {
plugins: [
// To strip all locales except“en”new MomentLocalesPlugin(),
// Or: To strip all locales except“en”,“es-us”and“ru”// (“en”is built into Moment and can’t be removed)
new MomentLocalesPlugin({localesToKeep: ['es-us', 'ru'],
}),
],
};
为了优化大小,还能够应用两个 webpack
插件传送门:
IgnorePlugin
ContextReplacementPlugin
IgnorePlugin
您能够应用IgnorePlugin
.
const webpack = require('webpack');
module.exports = {
//...
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
};
而且您依然能够在代码中加载一些语言环境。
const moment = require('moment');
require('moment/locale/ja');
moment.locale('ja');
...
Create React App 和 Next.js 应用这个解决方案。
ContextReplacementPlugin
如果要在 webpack
配置文件中指定蕴含语言环境文件,能够应用ContextReplacementPlugin
.
const webpack = require('webpack');
module.exports = {
//...
plugins: [
// load `moment/locale/ja.js` and `moment/locale/it.js`
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /ja|it/),
],
};
在这种状况下,您不须要在代码中加载语言环境文件。
const moment = require('moment');
moment.locale('ja');
...
测量
webpack: v3.10.0
moment.js: v2.20.1
File size |
Gzipped |
|
---|---|---|
Default |
266 kB |
69 kB |
w/ IgnorePlugin |
68.1 kB |
22.6 kB |
w/ ContextReplacementPlugin |
68.3 kB |
22.6 kB |
How to optimize moment.js with webpack
(2)unused code
在咱们的理论我的项目中,moment
被集成在 bricks
根底组件库中,bricks
组件库在构建时曾经对语言环境文件做了荡涤解决,然而在业务代码编写过程中理论应用 moment
的场景特地少,只用到了几个系统的 api
,却要承当引入整个moment.js
构建到产物中的代价。这时你可能要问到咱们不是有 tree shaking
吗?它能够帮咱们主动革除有效代码,但大失所望,moment
高度基于 OOP API
(面向原型链编程),所有api
都挂载到原型链上,导致无奈应用 Webpack
新引入的 Tree-shaking
代码优化技术,无奈辨认哪些代码是dead code
。
Moment 还存在如下一些问题
- 它高度基于
OOP API
,这使得它无奈应用tree-shaking
,从而导致微小的包大小和性能问题; - 它的可变性将导致一些时刻计算问题;
- 简单的
OOP API
使得Moment
可变性问题更加重大,这儿有个例子 https://github.com/moment/mom…; Moment
性能个别,因为简单的API
使得Moment
与原生Date
相比有着微小的性能开销;
Moment.js 领有一些问题
Moment 可变性
当我开始应用 moment
时,我假如它遵循 FP
准则,并且每次调用函数时都会返回雷同的值:
var now = moment();
var yesterday = now.subtract(1, 'days');
var dayBeforeYesterday = now.subtract(2, 'days');
当然,我没有失去我冀望的后果,这让我措手不及。
思考这个伪代码,我冀望如下代码行为形式:
var now = now;
var yesterday = now - 1day;
var dayBeforeYesterday = now - 2days;
但大失所望,它最终像这样工作,这让我感觉很奇怪:
var now = now;
var yesterday = now = now - 1day;
var dayBeforeYesterday = now = now - 2days;
Moment
对象的可变性使得我只能小心翼翼应用.clone()
。
var now = moment();
var yesterday = now.clone().subtract(1, 'days');
var dayBeforeYesterday = now.clone().subtract(2, 'days');
Moment
应用过程很容易呈现这些轻微的谬误,我认为 FP
准则有助于最大限度地缩小相似谬误。
参考:
怎么使 moment 对象不可变
moment 对象可变性造成的问题
如何解决 moment.js 中的可变性?
替换计划
如果您没有应用时区,而只应用了 moment.js
中的一些简略函数,这会导致你的应用程序被引入了很多没应用的办法,这是极其节约性能和内存的。在这里举荐应用 dayjs
, dayjs
体积十分小,超小的压缩体积,仅仅有 2kb
左右,所有更改 Day.js
对象的 API
操作都将返回一个新的实例(不可变性),和 Moment.js
有着雷同的 API
和模式,因而很容易从 moment
平滑过渡到 day.js
。date-fns
反对 Tree-shaking
代码优化技术,提供敌对的 functional programming (FP
) 函数(都是纯函数),反对函数柯里化,反对 typescript
,它的不可变性能很好的补救moment
带来的问题,因而它很适宜与 React,Sinon.js
和webpack
等好基友一起应用。
moment.js
、day.js
、date-fns
简略比拟:
名字 | 大小(gzip ) |
反对Tree-shaking |
名气 | api 办法数 |
模式 | 时区反对 | 反对的语言数 |
---|---|---|---|---|---|---|---|
Moment.js | 329K(69.6K) |
No | 38k |
高 | OO |
十分好(moment-timezone ) |
123 |
date-fns | 78.4k(13.4k) without tree-shaking |
Yes | 13k |
高 | Functional |
还不反对 | 32 |
dayjs | 6.5k(2.6k) without plugins |
No | 14k |
中 | OO |
还不反对 | 23 |
Moment.js 的替换计划
我的项目实际
首先排查应用程序依赖 Moment
状况,发现 Moment
集成在 bricks
根底组件库中,只有日期组件应用了 moment
,以后应用程序没有应用日期组件,而且也没有其余依赖Moment
的模块被装置,业务代码应用 Moment
的场景也很少,仅应用 calendar
和format
两个 api
,程序对moment
依赖水平极低,因而齐全能够将 Moment
从业务代码中移出,引入更加零轻量级 day.js
,或者反对Tree-Shaking
的date-fns
,在或者原生实现 format
办法。
综合思考,鉴于 date-fns
反对 FP
、Tree-Shaking
,具备不可变性,跟React
状态不可变性、FP
准则的思维完满吻合,抉择应用 date-fns
替换掉 bricks
集成的 moment
模块,替换 moment
的format
、calendar
办法
(1)因为 bricks
集成了moment
,须要先排除其被打包到最终产物中
// config-overrides.js
/** 革除 bricks 组件集成的 moment,不让其参加打包 */
const eliminateMomentOfBrs = (config) => {config.plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /moment/}));
}
module.exports = function override(config, env) {eliminateMomentOfBrs(config);
}
(2)替换业务代码中 moment
的format
、calendar
办法
// moment.js
moment(time).format('MM 月 DD 日'); // 09 月 02 日
// date-fns
import {format} from 'date-fns';
format(time, 'MM 月 dd 日'); // 09 月 02 日
// moment.js
moment(time).calendar(null, {sameDay: '[今日]HH:mm',
nextDay: '[明日]HH:mm',
nextWeek: 'M 月 D 日 HH:mm',
lastDay: 'M 月 D 日 HH:mm',
lastWeek: 'M 月 D 日 HH:mm',
sameElse: 'M 月 D 日 HH:mm',
}); // // 8 月 27 日 09:23
// date-fns
import {format, formatRelative} from "date-fns";
import {zhCN} from "date-fns/esm/locale";
const formatRelativeLocale = {
lastWeek: "M 月 d 日 HH:mm",
yesterday: "M 月 d 日 HH:mm",
today: "[今日]HH:mm",
tomorrow: "[明日]HH:mm",
nextWeek: "M 月 d 日 HH:mm",
other: "M 月 d 日 HH:mm"
};
const locale = {
...zhCN,
formatRelative: (token) => formatRelativeLocale[token]
};
formatRelative(time, new Date(), {locale}); // 8 月 27 日 09:23
如果你正在应用 ESLint
, 你能够装置一个插件 plugin
来帮忙你辨认代码库中你没有(可能不须要)Moment.js
的中央,避免同学不经意装置引入moment
。
装置这个插件 …
npm install --save-dev eslint-plugin-you-dont-need-momentjs
… 而后更新你的配置
"extends" : ["plugin:you-dont-need-momentjs/recommended"],
优化前后比照:
移出 moment
使得 build\static\js\2.7fde9c2a.chunk.js
体积缩小 17.52KB
,但增加date-fns
使得 build\static\js\3.4a62a5b9.chunk.js
减少8.76KB
,整体体积缩小靠近10KB
,成果不是很显著
对可视化树状图进一步剖析发现程序中曾经引入了 dayjs
,但程序并没有独自装置它,执行npm list dayjs
,发现只有封装大额业务组件库@casstime/mall-components
应用了它
基于以上察看,引入 date-fns
看来是没有必要的,间接应用 dayjs
即可,短暂思考,对立换成 date-fns
比拟好。
优化前后比照:
再次调整优化后干掉了整个 moment
模块,并且没有增加其余模块,和业务组件 @casstime/mall-components
共用dayjs
,产物体积整体缩小17.5KB
(大于上次优化的10KB
)
Day.js