如何在你的Next.js应用程序中懒散地加载模块

52 阅读2分钟

如何在你的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已经被移到它自己的捆绑文件中,由浏览器单独加载。