关于moment打包的那些事

moment
库,有没有发现引入moment之后,项目build完的文件大小明显大了不少,下面就来分析一下原因, 以及如何做优化。
一、问题定位
首先看下代码结构
import React from 'react'
import { render } from 'react-dom'
import moment from 'moment'
const App = () => {
const date = new Date()
return (
<div>
<h1>{date.toLocaleDateString()}</h1>
<h1>{moment().format('YYYY-MM-DD')}</h1>
</div>
)
}
render(<App/>, document.getElementById('app'))


-
上面两张图片分别是没用moment和用了moment编译的结果
-
只有几行代码, 编译出来的文件相差那么大。网上搜了一波才知道, 打包时把所有的locale都打包进去了, 初步解决方案是用webpack.IgnorePlugin来处理。
-
IgnorePlugin又是做了什么操作?
二、 IgnorePlugin 原理分析
// webpack.config.js
...
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) // 配置忽略规则
]
原理:在webpack编译阶段, 如果引入的文件路径匹配/^\.\/locale$/
,则会忽略这个文件, 也就不会被打包进去。
- 搜索
moment
包编译后的文件并未找到完全匹配/^\.\/locale$/
这个正则的引入语句,只有aliasedRequire('./locale/' + name)
这条语句和locale相关, 却又和正则匹配不上, 倒是在moment
的src源文件中有import ... from './locale'
。 但是在moment
的package.json
中main是指向编译后的文件并不是src文件,这就奇了怪了, 于是debug IgnorePlugin看了一下。

- 卧槽, 图中request真是
./locale
, 眼瞎了还是webpack的问题?按照dependencies的位置1853行查看moment编译后的文件, 定位到了确实是aliasedRequire('./locale/' + name)
, 怎么回事?

- 原来webpack在编译时,遇到
require('./locale/' + name)
此类表达式时,webpack 会查找目录'./locale/'
下符合正则表达式/^.*\.$/
的文件。由于 name 在编译时还是未知的,webpack 会将每个文件都作为模块引入到 bundle 中, 这就是为什么引入moment之后, 编译完的文件为什么会那么大的原因。
三、添加IgnorePlugin后, 需要设置locale怎么办?
- 在添加webpack.IgnorePlugin之后, 文件大小是减小了, 但是在设置
moment.locale('zh-cn')
之后, format之后的日期仍然是英文的,语言没有切换过来。 - 功能缺失肯定是不能接受的, 怎么办?怎么办?
- 在moment文档上也提供了解决方案,
moment-locales-webpack-plugin
new MomentLocalesPlugin({
localesToKeep: ['zh-cn'],
})
- moment默认locale是
en
,它必然会被打包进去, 如果需要配置其他语言,可以通过localesToKeep
来配置, 其他没用到的语言包也就不会被打包进去了。
四、 moment-locales-webpack-plugin原理分析
- 如果没有配置option, 用IgnorePlugin忽略所有语言包(
en
除外) - 如果设置option, 用 ContextReplacementPlugin插件设置webpack在编译阶段的查找规则,即查找指定的locale。
...
if (localesToKeep.length > 0) {
var regExpPatterns = localesToKeep.map(function(localeName) {
return localeName + '(\\.js)?';
});
return new ContextReplacementPlugin(
/moment[\/\\]locale/,
new RegExp('(' + regExpPatterns.join('|') + ')$') // 配置webpack编译阶段的查找规则, 即指定语言包
);
} else {
return new IgnorePlugin(/^\.\/locale$/, /moment$/);
}
...

到此结束。
水平有限,文中有错误之处,还望大佬指正。
转载自:https://juejin.cn/post/6844903987632685063