Jamstack Rendering Patterns:演变
在Jamstack的早期,开发者大多将其用于静态网站,当他们需要在Web应用中进行服务器端渲染等更复杂的操作时,就会选择Vue和React等更多的前端框架。为网络应用添加动态功能的需求从未消失过,但这并没有让我们对Jamstack的欣赏减少。我们喜欢它提出的建议和它提供的价值。网页可以立即提供给用户,而开发者可以轻松地建立网站,并更快地部署它们。用户很高兴,开发者也很高兴;这是一个双赢的结果。
然后,静态网站生成器出现了,它使事情变得更好,在以前的静态网站流程中增加了一个构建过程,这意味着网站的所有资产都是由一个构建服务器(而不是在本地机器上)预先生成的,然后部署。这在改善Jamstack开发者的体验方面向前迈进了一步,从而使这种模式得到普及。开发者可以用Gatsby这样的静态网站生成器建立Jamstack网站,将项目推送到Github这样的版本控制系统,然后部署到Netlify这样的托管服务,Netlify提供了一个工作流程,当项目有更新时,会重新建立网站。
一切看起来都很好,我们也因此变得更好。
但是,像其他技术一样,随着对更复杂的功能的需求不断增长,Jamstack开始演变。作为一个 "静态网站",Jamstack网站所能做的事情是有限的,而且人们并没有对此保持沉默。突然间,Jamstack似乎是一个不完整的模型,不能被大规模使用。人们提出的担忧主要是围绕着无法执行服务器端操作和大型Jamstack网站的构建时间长度。这在Jamstack社区中并不常见,我们开始 "扩展 "Jamstack以解决这个新的挑战,而这并不是它最初要解决的问题。
Jamstack的动态功能
虽然Gatsby在我们构建和更新Jamstack网站的方式上取得了很大的进步,如增量构建等功能,但Next.js引入了服务器端渲染功能getServerSideProps() 。
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
同时也保持了良好的静态生成,getStaticProps():
// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>))}
</ul>)
}
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json(
return {
props: {
posts,
},
}
}
export default Blog
这给开发者提供了一个构建Jamstack网站的混合方法的想法。突然间,你可以建立Jamstack网站,可以用不同的渲染模式渲染不同的页面。例如,你的/about 页面可以是静态生成的,而你的/cart 页面是服务器端渲染的。 然而,构建时间长的问题仍然存在。但时间不长。
通过增量静态再生(ISR),Next.js还可以按需生成页面,并为后续请求进行缓存。这意味着,开发人员可以有一个拥有10,000个页面的网站,在构建时只生成100个页面。所有其他页面都将按需动态生成,并为后续请求进行缓存,有效地将对长时间构建时间的担忧降至最低:
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>))}
</ul>)
}
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
revalidate: 10, // In seconds
}
}
export async function getStaticPaths() {
const res = await fetch('https://.../posts', {limit: 100})
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
return { paths, fallback: 'blocking' }
}
export default Blog
分布式持久性渲染(DPR)
2021年4月,Netlify宣布了一种新的渲染模式,称为分布式持久化渲染。这个想法是要移除ISR的重新验证位,并使任何在初始构建后被渲染的页面成为该构建的永久部分。没有重新验证。如果你想在未来重新渲染该页面,你将需要触发一个新的构建。根据公告帖子,这种模式不会损害Jamstack的不可变的原子部署原则。
在宣布DPR的同时,Netlify还推出了按需构建器--一种特殊类型的无服务器功能,可以按需生成内容,在边缘缓存,并在所有框架中工作。这给其他所有静态网站生成器和元框架带来了类似ISR的功能:
const { builder } = require('@netlify/functions');
async function myfunction(event, context) {
// logic to generate the required content
}
exports.handler = builder(myfunction);
这为像Gatsby这样的静态网站生成器提供了更多的机会,他们可以通过自己对这个概念的调整,即延迟静态生成(DSG),参与到这个网页的交付模式中。Eleventy还发布了Eleventy无服务器插件,该插件基于DPR概念,为开发者支持这种渲染模式。
递延静态生成(DSG)
如前所述,Gatsby调整了DPR的概念,创建了一个自定义的DSG渲染模式,允许开发者推迟非关键页面,只在构建时生成必要的内容。与ISR一样,推迟的页面是按需生成并缓存的。所有对这些页面的后续请求都从缓存中提供:
// The rest of your page, including imports, page component & page query etc.
export async function config() {
// Optionally use GraphQL here
return ({ params }) => {
return {
defer: true,
}
}
}
这篇文章的目的是看一下Jamstack渲染模式的演变,我们从哪里开始,现在又在哪里。目前,我们已经离我们的起点很远了,这是有原因的。但是,我个人在想,我们是否应该坚持最初的Jamstack网站的想法?一个我们不需要担心动态功能的地方。这是2022年,在Jamstack网站和普通React应用之间有很多细微的差别。
综上所述
Jamstack已经从简单的生成和部署静态资产蜕变为利用高级渲染模式和无服务器功能处理高度动态的功能。
是的,Jamstack的渲染模式已经有了进步,而且至今仍在不断改进。但这些改进也因此给原本简单的流程增加了更多的复杂性。在继续扩展Jamstack以考虑更多的用例时,我们有可能使其过度复杂化。
但就像在其他领域一样,我们期望看到持续的改进。2021年,Astro、Slinkity和Remix等元框架的出现和主导地位--它们都试图减少向浏览器发送的JavaScript。
这似乎是网络在2022年的发展方向,我们希望看到生态系统中出现更多的解决方案,以显著改善Jamstack的体验。React服务器组件在React中的出现,Vite作为Webpack和Babel的更快替代品,Remix等使用的边缘计算,HTML流等等,都是有希望的解决方案,正在继续获得普及和采用。我们只能推测,随着这些技术渗透到现有的网络基础设施中,事情会变得更好、更有趣。可以说,Jamstack最好的日子还在我们面前。
构建高度优化的网站的现代网络趋势做法是少用JavaScript。这就是像Remix这样的技术占据主导地位的地方。观察Jamstack空间如何继续发展是很有趣的,我个人也很期待下一步会是什么。
如果你今天正在建立一个Jamstack网站,以下是你目前可用的渲染模式:
- 静态生成
页面在构建时被渲染一次。 - 服务器端渲染
页面在每个请求的基础上生成。 - 延迟渲染(ISR/DPR/DSG)
关键页面在构建时首先生成,非关键页面按需生成并进行缓存。