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$/);
}
...
複製程式碼
到此結束。
水平有限,文中有錯誤之處,還望大佬指正。