Contents
(1) Cleaning moment
locale file
By default, when you write var moment = require('moment')
code and pack it with webpack
, the size of the bundled file becomes heavy because webpack
bundles all Moment.js
all locale files (in Moment.js 2.18.1
, compressed KB
as 160
).
To strip unnecessary locales and bundle only used locales, add 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'], }), ], };
To optimize size, two webpack
plugin portals can also be used:
IgnorePlugin
ContextReplacementPlugin
IgnorePlugin
You can use IgnorePlugin
.
const webpack = require('webpack'); module.exports = { //... plugins: [ // Ignore all locale files of moment.js new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), ], };
And you can still load some locales in your code.
const moment = require('moment'); require('moment/locale/ja'); moment.locale('ja'); ...
Create React App and Next.js use this solution.
ContextReplacementPlugin
If you want to specify the include locale file in the webpack
configuration file, you can use 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/), ], };
In this case you don’t need to load the locale file in your code.
const moment = require('moment'); moment.locale('ja'); ...
Measurement
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
In our actual project, moment
is integrated in bricks
base component library, bricks
component library has cleaned the locale files at build time , but the actual use of moment
in the process of writing business code is very rare, and only a few pieces of api
moment.js
used. price in the product. At this point you may ask if we have tree shaking
? It can help us to automatically clear invalid codes, but it backfires, moment
is highly based on OOP API
(prototype chain-oriented programming), all api
are mounted on the prototype chain , resulting in the inability to use Webpack
the newly introduced Tree-shaking
code optimization technology, which cannot identify which codes are dead code
.
Moment still has the following problems
- It is highly based on
OOP API
which makes it unusabletree-shaking
causing huge packet size and performance issues; - Its variability will cause some moment calculation problems;
- The complex
OOP API
makesMoment
the variability problem even worse, here is an example https://github.com/moment/moment/blob/develop/src/test/moment/add_subtract .js#L244-L286 ; Moment
The performance is average, due to the complexAPI
makingMoment
compared with the nativeDate
has a huge performance overhead;
Moment variability
When I started using moment
, I assumed it followed the FP
principle and would return the same value every time the function was called:
var now = moment();
var yesterday = now.subtract(1, 'days');
var dayBeforeYesterday = now.subtract(2, 'days');
Of course, I didn’t get the results I expected, which caught me off guard.
Considering this pseudocode, I would expect the code to behave as follows:
var now = now; var yesterday = now - 1day; var dayBeforeYesterday = now - 2days;
But backfired, it ended up working like this, which strikes me as weird:
var now = now; var yesterday = now = now - 1day; var dayBeforeYesterday = now = now - 2days;
Moment
The variability of objects makes me use only .clone()
with caution.
var now = moment(); var yesterday = now.clone().subtract(1, 'days'); var dayBeforeYesterday = now.clone().subtract(2, 'days');
Moment
It’s easy to make these subtle mistakes with the process, and I think the FP
principle helps to minimize similar mistakes.
refer to:
How to make moment object immutable
Problems caused by moment object mutability
How to resolve mutability in moment.js?
Alternative
If you don’t use timezones, but only use some simple functions in moment.js
, this will lead to your application being introduced with many unused methods, which is extremely wasteful of performance and memory. Here it is recommended to use dayjs
, dayjs
very small volume, ultra-small compressed volume, only 2kb
or so, all changes Day.js
objects的API
操作都将返回一个新的实例(不可变性), Moment.js
API
, moment
Smooth transition to day.js
. date-fns
support Tree-shaking
code optimization technology, provide friendly functional programming ( FP
) functions (all pure functions), support function currying typescript
support– typescript
,它的不可变性能很好的弥补moment
59f3d384060a71bf6921c6425bd3a7d3—带来的问题, React,Sinon.js
和—0994891e9570de49600bff20808ad59a webpack
好基friends to use together.
moment.js
, day.js
, date-fns
Simple comparison:
name | size ( gzip ) | Support Tree-shaking | fame | api Number of methods | model | time zone support | Number of languages supported |
---|---|---|---|---|---|---|---|
Moment.js | 329K(69.6K) | No | 38k | high | OO | very good ( moment-timezone ) | 123 |
date-fns | 78.4k(13.4k) without tree-shaking | Yes | 13k | high | Functional | not yet supported | 32 |
dayjs | 6.5k(2.6k) without plugins | No | 14k | middle | OO | not yet supported | twenty three |
Project Practice
First check the application dependency Moment
situation, found Moment
integrated in bricks
basic component library, currently only the date component uses moment
The application does not use the date component, and there are no other dependencies Moment
is installed, and the business code uses Moment
There are few scenarios, only use calendar
and format
api
,程序对moment
e5bee237a0ba841927cb59016685b50c—依赖程度极低,因此完全可以将—f35d9b145f66465acdbbb3f24b901b16—从业务代码中移出, Moment
Zero lightweight day.js
, or the —a6bf9a3378b9e41f640d1e14ac966cce52— method that supports Tree-Shaking
date-fns
, in or natively implements the format
method.
综合考虑, date-fns
支持FP
、 Tree-Shaking
,具有不可变性,跟React
变性、 FP
原则的思想完美吻合, date-fns
2d62dcb86d86366e706ddc68c3aeee9a—替换掉bricks
集成的moment
模块, moment
的format
, calendar
method
(1) Because bricks
integrates moment
, it is necessary to exclude it from being packaged into the final product
// 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) Replace the format
and calendar
methods in the business code moment
fdecb7846e47fca780b1fea4979db324—
// 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
If you are using ESLint
, you can install a plugin plugin
to help you identify places in the codebase that you don’t have (may not need) Moment.js
Inadvertent installation introduced moment
.
Install this plugin…
npm install --save-dev eslint-plugin-you-dont-need-momentjs
…then update your config
"extends" : ["plugin:you-dont-need-momentjs/recommended"],
Comparison before and after optimization:
moment
build\static\js\2.7fde9c2a.chunk.js
17.52KB
, date-fns
build\static\js\3.4a62a5b9.chunk.js
8.76KB
, the overall volume reduction is close to 10KB
, the effect is not very obvious
Further analysis of the visual tree diagram found that the program has introduced dayjs
, but the program did not install it separately, execute npm list dayjs
, and found that only the packaged large-value business component library @casstime/mall-components
used it
Based on the above observations, it seems unnecessary to introduce date-fns
. It is better to use dayjs
directly. After long-term consideration, it is better to replace it with date-fns
.
Comparison before and after optimization:
moment
模块,并且没有添加其他模块,和业务组件@casstime/mall-components
共用dayjs
,产物体积17.5KB
(greater than last optimized 10KB
)