关于moment打包的那些事

在项目中常常用到 moment库,有没有发现引入moment以后,项目build完的文件大小明显大了很多,下面就来分析一下缘由, 以及如何作优化。

1、问题定位

首先看下代码结构react

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编译的结果webpack

  • 只有几行代码, 编译出来的文件相差那么大。网上搜了一波才知道, 打包时把全部的locale都打包进去了, 初步解决方案是用webpack.IgnorePlugin来处理。web

  • IgnorePlugin又是作了什么操做?正则表达式

2、 IgnorePlugin 原理分析

// webpack.config.js
...
plugins: [
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) // 配置忽略规则
]
复制代码

原理:在webpack编译阶段, 若是引入的文件路径匹配/^\.\/locale$/,则会忽略这个文件, 也就不会被打包进去。json

  • 搜索moment包编译后的文件并未找到彻底匹配/^\.\/locale$/这个正则的引入语句,只有aliasedRequire('./locale/' + name)这条语句和locale相关, 却又和正则匹配不上, 却是在moment的src源文件中有import ... from './locale'。 可是在momentpackage.json中main是指向编译后的文件并非src文件,这就奇了怪了, 因而debug IgnorePlugin看了一下。

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

  • 原来webpack在编译时,遇到require('./locale/' + name)此类表达式时,webpack 会查找目录 './locale/' 下符合正则表达式 /^.*\.$/的文件。因为 name 在编译时仍是未知的,webpack 会将每一个文件都做为模块引入到 bundle 中, 这就是为何引入moment以后, 编译完的文件为何会那么大的缘由

3、添加IgnorePlugin后, 须要设置locale怎么办?

  1. 在添加webpack.IgnorePlugin以后, 文件大小是减少了, 可是在设置moment.locale('zh-cn')以后, format以后的日期仍然是英文的,语言没有切换过来。
  2. 功能缺失确定是不能接受的, 怎么办?怎么办?
  3. 在moment文档上也提供了解决方案, moment-locales-webpack-plugin
new MomentLocalesPlugin({
  localesToKeep: ['zh-cn'],
})
复制代码
  1. moment默认locale是en,它必然会被打包进去, 若是须要配置其余语言,能够经过localesToKeep来配置, 其余没用到的语言包也就不会被打包进去了。

4、 moment-locales-webpack-plugin原理分析

  1. 若是没有配置option, 用IgnorePlugin忽略全部语言包(en除外)
  2. 若是设置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$/);
}
...
复制代码

到此结束。bash

水平有限,文中有错误之处,还望大佬指正。app

相关文章
相关标签/搜索