(1)荡涤moment
语言环境文件
默认状况下,当您编写var moment = require('moment')
代码并应用 webpack
打包时,捆绑文件的大小会变得很重,因为webpack
会捆绑所有Moment.js
所有语言环境文件(在 Moment.js 2.18.1
中,压缩后的 KB
为 160
)。
要去除不必要的语言环境并仅捆绑应用的语言环境,请增加moment-locales-webpack-plugin
:
// webpack.config.jsconst 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.jsmoment(time).format('MM月DD日'); // 09月02日// date-fnsimport { format } from 'date-fns';format(time, 'MM月dd日'); // 09月02日// moment.jsmoment(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-fnsimport { 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