關於moment打包的那些事

白晝流星發表於2019-11-05

關於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打包的那些事

  • 上面兩張圖片分別是沒用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'。 但是在momentpackage.json中main是指向編譯後的檔案並不是src檔案,這就奇了怪了, 於是debug IgnorePlugin看了一下。

關於moment打包的那些事

  • 臥槽, 圖中request真是./locale, 眼瞎了還是webpack的問題?按照dependencies的位置1853行檢視moment編譯後的檔案, 定位到了確實是 aliasedRequire('./locale/' + name), 怎麼回事?

關於moment打包的那些事

  • 原來webpack在編譯時,遇到require('./locale/' + name)此類表示式時,webpack 會查詢目錄 './locale/' 下符合正規表示式 /^.*\.$/的檔案。由於 name 在編譯時還是未知的,webpack 會將每個檔案都作為模組引入到 bundle 中, 這就是為什麼引入moment之後, 編譯完的檔案為什麼會那麼大的原因

三、新增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來配置, 其他沒用到的語言包也就不會被打包進去了。

四、 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$/);
}
...
複製程式碼

關於moment打包的那些事

到此結束。

水平有限,文中有錯誤之處,還望大佬指正。

相關文章