如何在您的 Next.js 應用程式中實現懶加載模組

能夠視覺化分析捆包很棒,因為我們可以輕鬆優化我們的應用程式。

假設我們需要在我們的部落格文章中加載 Moment 庫。運行:

npm install moment

以將其包含在專案中。

現在讓我們模擬在/blog/blog/[id]這兩個不同路由中需要使用Moment的情況。

我們在pages/blog/[id].js中導入它:

import moment from 'moment'

...

const Post = props => {
 return (
 <div>
 <h1>{props.post.title}</h1>
 <p>發布於 {moment().format('dddd D MMMM YYYY')}</p>
 <p>{props.post.content}</p>
 </div>
 )
}

這只是作為示例添加了今天的日期。

這將導致 Moment.js 包含在部落格文章頁面的捆包中,您可以通過運行npm run analyze來看到:

看到現在在/blog/[id]中有一個紅色條目,這是我們添加Moment.js後的路由!

這導致檔案從1kB變為350kB,十分驚人。原因是Moment.js本身的大小為349kB。

客戶端束縛視覺化現在顯示的是更大的捆包是內容頁面,以前只是非常小。且它的99%是Moment.js。

每次載入部落格文章時,我們都需要將所有這些程式碼傳輸到客戶端,這並不理想。

修復的一種方法是尋找一個尺寸較小的庫,因為Moment.js因並不以輕量級聞名(尤其是在默認情況下包含了所有的locales),但為了範例的完整性,讓我們假設我們必須使用它。

相反,我們可以將所有Moment庫程式碼分離到單獨的捆包中。

如何實現呢?我們不再在組件層級上導入Moment,而是在getInitialProps中執行一個異步導入操作,然後計算要發送給組件的值。

請記住,我們無法在 getInitialProps() 回傳的對象中返回複雜對象,所以我們在其中計算了日期:

import posts from '../../posts.json'

const Post = props => {
 return (
 <div>
 <h1>{props.post.title}</h1>
 <p>發布於 {props.date}</p>
 <p>{props.post.content}</p>
 </div>
 )
}

Post.getInitialProps = async ({ query }) => {
 const moment = (await import('moment')).default()
 return {
 date: moment.format('dddd D MMMM YYYY'),
 post: posts[query.id]
 }
}

export default Post

看到await import後面的.default()特殊調用了嗎?這是為了在動態導入中引用默認導出來的。

現在如果我們再次運行npm run analyze,我們可以看到這個結果:

我們的/blog/[id]捆包大小再次很小,因為Moment已被移至它自己的捆包檔中,由瀏覽器單獨加載。