如何防止moment.js用webpack加载locale

How to prevent moment.js from loading locales with webpack?

本文关键字:webpack 加载 locale js 何防止 moment      更新时间:2023-09-26

当您使用webpack时,是否有任何方法可以阻止moment.js加载所有区域设置(我只需要英语)?我正在查看源代码,似乎如果定义了hasModule,这是针对webpack的,那么它总是尝试require()每个locale。我很确定这需要一个拉请求来修复。但是我们有什么办法可以用webpack配置来解决这个问题吗?

这是我加载momentjs的webpack配置:

resolve: {
            alias: {
                moment: path.join(__dirname, "src/lib/bower/moment/moment.js")
            },
        },

然后在任何我需要它的地方,我只要做require('moment')。这是可行的,但它会在我的包中添加大约250 kB的不需要的语言文件。我还使用了较低版本的momentjs和gulp。

如果这不能通过webpack配置来修复,这里有一个链接到加载区域设置的函数。我尝试将&& module.exports.loadLocales添加到if语句,但我猜webpack实际上并没有以这种方式工作。不管怎样,它都是 requires。我认为它现在使用正则表达式,所以我真的不知道你会怎么去修复它。

代码require('./locale/' + name)可以使用locale目录中的每个文件。所以webpack将每个文件作为模块包含在bundle中。它无法知道您使用的是哪种语言。

ContextReplacementPluginIgnorePlugin这两个插件可以帮助webpack了解哪些模块应该包含在你的包中。

require('./locale/' + name)被称为上下文(一个包含表达式的要求)。webpack从这段代码片段推断出一些信息:一个目录和一个正则表达式。这里:directory = ".../moment/locale" regular expression = /^.*$/。所以默认情况下,locale目录下的每个文件都包含在内。

ContextReplacementPlugin允许覆盖推断的信息,即提供一个新的正则表达式(选择您想要包含的语言)。

另一种方法是忽略IgnorePlugin的要求。

下面是一个例子:

var webpack = require("webpack");
module.exports = {
  // ...
  plugins: [
    new webpack.ContextReplacementPlugin(/moment['/'']locale$/, /de|fr|hu/)
    // new webpack.IgnorePlugin(/^'.'/locale$/, /moment$/)
  ]
};

在我们的项目中,我包括这样的时刻:import moment from 'moment/src/moment';,这似乎做的技巧。我们对moment的使用非常简单,所以我不确定是否会有与SDK不一致的地方。我认为这是有效的,因为WebPack不知道如何静态地找到区域设置文件,所以你会得到一个警告(这很容易通过在moment/src/lib/locale/locale添加一个空文件夹来隐藏),但没有包含区域设置。

UPDATE: 2021
您可能还需要检出许多其他库:

  • https://date-fns.org
  • https://github.com/iamkun/dayjs

原答案:
似乎适当的模块化moment库将永远不会出现,然而,我只是结束了使用https://github.com/ksloan/moment-mini像import * as moment from 'moment-mini';

作为时刻2.18,所有的区域都与核心库捆绑在一起(见这个GitHub问题)。

传递给IgnorePlugin的resourceRegExp参数不是针对已解析的文件名或导入或需要的绝对模块名进行测试,而是针对传递给需要或导入的源代码中的字符串进行测试。例如,如果你试图排除node_modules/moment/locale/*.js,这将不起作用:

new webpack.IgnorePlugin({ resourceRegExp: /moment'/locale'// });

更确切地说,因为moment导入使用以下代码:

require('./locale/' + name);

你的第一个regexp必须匹配那个'。/地区/字符串。然后使用第二个contextRegExp参数选择发生导入的特定目录。以下命令将导致忽略这些区域设置文件:

plugins:[
    new webpack.IgnorePlugin({
      resourceRegExp: /^'.'/locale$/,
      contextRegExp: /moment$/,
    }),
]

表示"任何要求语句匹配"。/locale'从任何以'moment'结尾的目录将被忽略。

根据Adam McCrmick的回答,你很接近了,把你的别名改成:

resolve: {
    alias: {
        moment: 'moment/src/moment'
    },
},

使用webpack2和最新版本的moment,您可以:

import {fn as moment} from 'moment'

然后在webpack.config.js你做:

resolve: {
    packageMains: ['jsnext:main', 'main']
}

在NPM安装程序中使用postinstall脚本是另一个解决方案。

您可以在包中添加一行。json 文件:

{
  "scripts": {
    ...
    "postinstall": "find node_modules/moment/locale -type f -not -name 'en-gb.js' -not -name 'pl.js' -printf '%p''n' | xargs rm"
    ...
  }
}

结果不需要的区域设置将在npm install完成安装包后立即删除。

在我的例子中,只有en-gbpl区域将保留在bundle中。

如果您已经有postinstall脚本,您可以添加脚本到现有的命令:

{
  "scripts": {
    ...
    "postinstall": "previous_command && find node_modules/moment/locale -type f -not -name 'en-gb.js' -not -name 'pl.js' -printf '%p''n' | xargs rm"
    ...
  }
}

对于web,如果您打算使用很少的moment区域,您可以选择此解决方案。使用externals配置。此外,我们还需要将moment库和特定的locale脚本添加到HTML模板中。

优点:

  • 增加webpack的编译速度。
  • 减少包的大小
  • 不需要require('moment/locale/zh-tw')语句(V.S. webpack.IgnorePlugin溶液)

缺点:

  • 每个语言环境脚本必须手动添加。
  • 区域设置脚本总是从CDN下载,即使你不在你的代码中使用它们。这意味着有更多的HTTP请求。

下面的示例只使用momentzh-tw区域设置。

webpack.config.js:

const HTMLWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  externals: {
    moment: 'moment'
  },
  plugins: [
    new HTMLWebpackPlugin({
      template: './src/index.html'
    }),
  ]
};

src/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/locale/zh-tw.min.js"></script>
</body>
</html>

src/index.js:

import moment from 'moment';
moment.locale('zh-tw');
console.log(moment().fromNow())