关于 nuxt generate 和 prerender-spa-plugin 的比较
TL; DR
我放弃了 nuxt generate 转而使用 prerender-spa-plugin,因为后者才是真正的预渲染。
想要单页应用的体验,又想要搜索引擎友好,还想省点计算资源,那就搞个预渲染⑧。
传闻基于 vue.js 的 nuxt.js 无论是做服务端渲染还是预渲染都特别方便,所以我有点感兴趣——一句 nuxt generate
就能完成预渲染,懒人必备。
以本网站的 repository 为例子,我们康康 nuxt 预渲染的效果。
首先是渲染后的 dist
目录的结构,大概是这样:
│ index.html
│
├─about
│ index.html
│
├─article
│ │ index.html
│ │
│ ├─0
│ │ index.html
│ │
│ └─1
│ index.html
看着还行,然后打开网站首页对应的 html 文件(省略了 style 标签),出大问题:
<!doctype html>
<html>
<head>
<title>solidays</title>
<meta data-n-head="1" charset="utf-8">
<meta data-n-head="1" name="viewport" content="width=device-width,initial-scale=1">
<meta data-n-head="1" data-hid="description" name="description" content="Solidays site">
<link data-n-head="1" rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="preload" href="/_nuxt/87c5ca9e89ccc22c5ca6.js" as="script">
<link rel="preload" href="/_nuxt/617d5b8dda09eb86232c.js" as="script">
<link rel="preload" href="/_nuxt/b67b01560b96c9cde957.js" as="script">
</head>
<body>
<div id="__nuxt">
<script>window.addEventListener("error", function () { var e = document.getElementById("nuxt-loading"); e && (e.className += " error") })</script>
<div id="nuxt-loading" aria-live="polite" role="status">
<div>Loading...</div>
</div>
</div>
<script type="text/javascript" src="/_nuxt/87c5ca9e89ccc22c5ca6.js"></script>
<script type="text/javascript" src="/_nuxt/617d5b8dda09eb86232c.js"></script>
<script type="text/javascript" src="/_nuxt/b67b01560b96c9cde957.js"></script>
</body>
</html>
所以说预渲染就渲染出了个 Loading...
而已,首页里并没有涉及到异步数据,却啥也没渲染出来,实在很一般。
结论就是 nuxt generate
是可以生成一个静态站点的,生成的站点不需要像传统的单页应用一样配置 fallback 的页面(因为它已经把所有路由情况都用相应的目录表示出来了),所以对托管的服务器没啥要求。但我觉得这个预渲染的程度也太低了(相应的,也许速度会很快),当然,也可能是我食用方法不对。
另一个可用于 vue.js 预渲染的技术是 prerender-spa-plugin,作者是 vue 核心团队的成员。
prerender-spa-plugin 预渲染出来目录结构跟 nuxt 的是一样的:
│ index.html
│
├─about
│ index.html
│
├─article
│ │ index.html
│ │
│ ├─0
│ │ index.html
│ │
│ └─1
│ index.html
但是打开首页对应的 html 文件,正常多了,页面上的元素都被预渲染了出来。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="icon" href="/favicon.ico">
<title>solidays</title>
<link href="/css/about.0f584ee5.css" rel="prefetch">
<link href="/css/chunk-28e366f6.b9ab4f5a.css" rel="prefetch">
<link href="/css/chunk-6941575a.2572d488.css" rel="prefetch">
<link href="/js/about.6337d6a7.js" rel="prefetch">
<link href="/js/chunk-28e366f6.773b388b.js" rel="prefetch">
<link href="/js/chunk-6941575a.441841bb.js" rel="prefetch">
<link href="/css/app.9449ebb6.css" rel="preload" as="style">
<link href="/js/app.7fed66b8.js" rel="preload" as="script">
<link href="/js/chunk-vendors.3dec5244.js" rel="preload" as="script">
<link href="/css/app.9449ebb6.css" rel="stylesheet">
</head>
<body><noscript><strong>We're sorry but solidays doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong></noscript>
<div id="app">
<div data-v-18813c80="" class="root">
<div data-v-18813c80="" class="container">
<h1 data-v-18813c80="" class="title"> solidays </h1>
<div data-v-18813c80="" class="categories"><a data-v-18813c80="" href="/article" class=""> 文章 </a><a
data-v-18813c80="" href="/about" class=""> 关于 </a><a data-v-18813c80=""
href="https://www.solidays.tk"> 旧站 </a></div>
</div>
<div data-v-18813c80="" class="footer"><a data-v-18813c80="" href="https://世界1流大学.com">世界1流大学.com</a><a
data-v-18813c80="" href="https://github.com/ReGetALife/solidays">源码仓库</a></div>
</div>
</div>
<script src="/js/chunk-vendors.3dec5244.js"></script>
<script src="/js/app.7fed66b8.js"></script>
</body>
</html>
但是打开某篇文章对应的 html 文件,发现元素并没有被渲染出来,因为文章都是异步请求获取的,文章还没拿到已经完成了渲染,自然不会有内容。所以只需设置适当的延时即可:
new PrerenderSpaPlugin({
staticDir: path.join(__dirname, 'dist'),
// prerender routes
routes,
renderer: new Renderer({
// time to fetch md file
renderAfterTime: 500,
headless: true
})
})
于是,所有页面的所有元素都被渲染了出来,很棒棒。
prerender-spa-plugin 提供了两个渲染器:puppeteer 和 jsdom ,前者是一个无头 chrome 浏览器,比较可靠但耗内存,后者很快但不可靠。所以使用 puppeteer 时,就像打开了心爱的 chrome 渲染完了页面再保存下来,这才叫预渲染啊。
Metadata 和 SEO 啥的暂时放下,最近挺忙的 : )