(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 插件传送门:
IgnorePluginContextReplacementPlugin
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');
...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.0moment.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可变性问题愈加严峻,这儿有个例子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);
}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:23formatRelative(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
本文来历:怎么运用 webpack 优化 moment.js
