十二、Next.js,延迟加载组件
Next.js是一个新的通用JavaScript框架,它为基于React和服务器的Web应用提供了一个新的可选方案。
Next.js目前已经开源,https://zeit.co/blog/next
我们在应用程序使用了很多的React组件。这里将包括在我们主的 JavaScript 包 (app.js) 或页面的 JavaScript 包之一。有时这些组件都相当大。
例如,这是一个非常简单的博客站点,我们用Next.js构建基于markdown。
Paste_Image.png它还支持语法高亮。对于这个,我们使用的是 react-highlight,,它是一个相当大的模块。
实际上,react-highlight 模块本身很小,但最highlight.js模块在里面是很笨重的。
下面是我们如何使用它。
import Highlight from 'react-highlight'
// some other code
<div>
<Highlight innerHTML>
{marked(blogPostMarkdown)}
</Highlight>
</div>
// some other code
由于几乎所有页面都使用了它,所以在我们的主JavaScript包中包含了所有的内容。
但我们不需要在每个页面上使用高亮显示的语法。只有在博客文章中有一个代码样例时才需要它。
因此,如果我们能够在我们需要的时候加载 react-highlight,这将大大减少初始应用程序包,这将帮助我们的应用加载更快。
这是我们接下来要做的事情用Next.js(适用3.0版本以后)。
让我们开始吧。
安装
让我们下载我们将在本课程中使用的示例博客应用程序:
git clone https://github.com/arunoda/learnnextjs-demo.git
cd learnnextjs-demo
git checkout markdown-blog
然后你就可以运行这个应用了:
npm install
npm run dev
现在访问http://localhost:3000并尝试应用程序。
让我们来分析
在我们做其他事情之前,让我们分析一下我们的应用程序的JavaScript包。
为此,只需运行以下命令:
npm run analyze
在命令完成之后,您可以在web浏览器中看到结果。
highlight.js的包在哪里呢?
分析结果
正如你所见,它被包含在公共包 commons.js中。这是因为在我们的博客应用中几乎每一页都使用它。
Paste_Image.png在这个结果页面中,我们甚至看不到react-highlight模块。这是因为它很小。该模块里面使用的highlight.js,它是一个重的模块。
Hello 动态组件
在这个应用程序中,我们将markdown独立渲染时一个React高阶组件(HOC)。
这是位于 lib/with-post.js。以下是该文件的内容:
import React from 'react'
import MyLayout from '../components/MyLayout'
import marked from 'marked'
import Highlight from 'react-highlight'
marked.setOptions({
gfm: true,
tables: true,
breaks: true
})
export default function WithPost (options) {
return class PostPage extends React.Component {
render () {
return (
<MyLayout>
<h1>{options.title}</h1>
<div>
<Highlight innerHTML>
{marked(options.content)}
</Highlight>
</div>
</MyLayout>
)
}
}
}
你可以访问 pages/p/hello-nextjs.js然后看它是怎样使用这个HOC的。
现在,我们正在尝试将上面的Highlight 组件转换为动态组件。动态组件由Next.js dynamic imports 提供能力。
最重要的是,这些组件只有在应用程序即将呈现时才会加载。
创建动态组件很简单。您可以使用next/dynamic实用工具。下面是如何将Highlight 转换为动态组件的方法。
//import Highlight from 'react-highlight'
import dynamic from 'next/dynamic'
const Highlight = dynamic(import('react-highlight'))
这就是你要做的。
在lib/with-post.js中应用上述代码更改。
然后试着重新开始你的应用程序并访问http://localhost:3000/p/hello-nextjs。你将能够看到被加载的作为一个单独的捆绑包。
Paste_Image.png现在尝试访问http://localhost:3000/p/learn-nextjs看看是否加载react-highlight包?
它总是加载
如您所见,甚至react-highlight包加载页面http://localhost:3000/p/learn-nextjs没有任何代码样本。
Paste_Image.png它加载了react-highlight,因为我们要求它每次都加载。
你可以通过查看lib/with-post.js来了解这一点。以下是相关代码块:
export default function WithPost (options) {
return class PostPage extends React.Component {
render () {
return (
<MyLayout>
<h1>{options.title}</h1>
<div>
<Highlight innerHTML>
{marked(options.content)}
</Highlight>
</div>
</MyLayout>
)
}
}
}
我们经常使用Highlight 组件 options.content 有否任何代码或不。
仅在需要时加载
现在,我们只在需要的时候才使用Highlight 组件。
这是修改后的 WithPost HOC ,它只在需要时使用Highlight 组件。
export default function WithPost (options) {
return class PostPage extends React.Component {
renderMarkdown () {
// If a code snippet contains in the markdown content
// then use Highlight component
if (/~~~/.test(options.content)) {
return (
<div>
<Highlight innerHTML>
{marked(options.content)}
</Highlight>
</div>
)
}
// If not, simply render the generated HTML from markdown
return (
<div
dangerouslySetInnerHTML={{__html: marked(options.content)}}
/>
)
}
render () {
return (
<MyLayout>
<h1>{options.title}</h1>
{ this.renderMarkdown() }
</MyLayout>
)
}
}
}
尝试将上述更改应用到lib/with-post.js并重新启动你的应用。
然后访问http://localhost:3000/p/learn-next.js。这款应用不会下载“react-highlight”。
但是如果你访问http://localhost:3000/p/hello-nextjs时,它将下载包。
现在我们有一个简单的任务。在生产模式下运行你的应用:
npm run build
npm run start
在Chrome中打开一个空白页面,并在dev控制台中选择“Network Inspect”。
然后访问http://localhost:3000 /。
页面加载后,单击“Hello Next.js”标题。
正如你已经看到的,这款应用会在需要的时候,在客户端上下载“react-highlight”功能。这就是动态组件的全部目的。当它加载时,它将显示一个加载组件。
如果需要,还可以设置一个自定义加载组件。custom loading component
直接访问
如果您直接访问包含代码片段的页面(比如http://localhost:3000/p/hello-nextjs)这是一个特例。然后,该应用程序将在服务器端加载组件,并包含初始HTML的包。
您可以通过查看上述页面的HTML内容来验证这一点。
Paste_Image.png最后
惰性加载动态组件对于高性能应用程序来说非常重要,它允许您在需要的时候加载组件。如果你做的很明智,你可以减少需要下载的JavaScript的数量,让你的应用加载更快。
Next.js在默认情况下支持这些动态组件的服务器端呈现(SSR)。因此,使用动态组件不会丢失任何东西。
我们只使用了动态组件的基本功能。您可以通过参考文档了解更多关于它们的信息。 documentation.