如何在你的Next.js应用程序中懒散地加载模块
能够直观地分析一个捆绑包是非常好的,因为我们可以非常容易地优化我们的应用程序。
假设我们需要在我们的博客文章中加载Moment库。运行。
来将其纳入项目中。
现在让我们模拟一下,我们在两个不同的路由上需要它:/blog 和/blog/[id] 。
我们在pages/blog/[id].js 中导入它。
import moment from 'moment'
...
const Post = props => {
return (
<div>
<h1>{props.post.title}</h1>
<p>Published on {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并不以轻量级著称(尤其是开箱后包含了所有的语言),但为了这个例子,我们假设我们必须使用它。
我们可以做的是把所有的Moment代码分开在一个单独的包里。
怎么做?我们不在组件层面上导入Moment,而是在getInitialProps 里面执行异步导入,并计算出要发送给组件的值。记住,我们不能在getInitialProps() 返回的对象里面返回复杂的对象,所以我们在里面计算出日期。
import posts from '../../posts.json'
const Post = props => {
return (
<div>
<h1>{props.post.title}</h1>
<p>Published on {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() 的特殊调用了吗?这是在动态导入中引用默认导出的需要(见https://v8.dev/features/dynamic-import)。
现在如果我们再次运行npm run analyze ,我们可以看到这个。

我们的/blog/[id] 捆绑文件又非常小了,因为Moment已经被移到它自己的捆绑文件中,由浏览器单独加载。