关于moment.js:如何使用-webpack-优化-momentjs

(1)荡涤moment语言环境文件

默认状况下,当您编写var moment = require('moment')代码并应用 webpack 打包时,捆绑文件的大小会变得很重,因为webpack 会捆绑所有Moment.js 所有语言环境文件(在 Moment.js 2.18.1 中,压缩后的 KB160)。

要去除不必要的语言环境并仅捆绑应用的语言环境,请增加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 插件传送门:

  1. IgnorePlugin
  2. 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.jsdate-fns反对Tree-shaking代码优化技术,提供敌对的 functional programming (FP) 函数(都是纯函数),反对函数柯里化,反对typescript,它的不可变性能很好的补救moment带来的问题,因而它很适宜与React,Sinon.jswebpack等好基友一起应用。

moment.jsday.jsdate-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的场景也很少,仅应用calendarformat两个api,程序对moment依赖水平极低,因而齐全能够将Moment从业务代码中移出,引入更加零轻量级day.js,或者反对Tree-Shakingdate-fns,在或者原生实现format办法。

综合思考,鉴于date-fns反对FPTree-Shaking,具备不可变性,跟React状态不可变性、FP准则的思维完满吻合,抉择应用date-fns替换掉bricks集成的moment模块,替换momentformatcalendar办法

(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)替换业务代码中momentformatcalendar办法

// 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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理