關於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

相關文章
相關標籤/搜索