# vuepress-plugin-dehydrate
修改你的 VuePress 生成的 HTML 文件。
# 安装
npm install -D vuepress-plugin-dehydrate
# 使用
// .vuepress/config.js
module.exports = {
plugins: [
'vuepress-plugin-dehydrate',
{
// 禁用 SSR
noSSR: '404.html',
// 移除 scripts
noScript: [
// 支持 glob patterns
'foo/*.html',
'**/static.html',
],
},
],
}
# 配置项
# noSSR
- 类型:
string | string[]
- 默认值:
'404.html'
要禁用 SSR 的页面列表,支持 glob patterns。
# noScript
- 类型:
string | string[]
- 默认值:
[]
要移除 script 的页面列表,支持 glob patterns。
# globOptions
- 类型:
object
- 默认值:
{}
提供给 fast-glob 的选项。
# noEmptyLine
- 类型:
boolean
- 默认值:
true
是否删除 HTML 中多余的空行。
# 为什么需要这个插件
# SSR 错配
VuePress 会尝试从未找到的页面 /foo
重定向到 /foo.html
或 /foo/
。有时我们也希望根据当前语言自动将 /
重定向 /
到 /zh/
或者 /en/
。
VuePress 在绝大部分情况下都能正常工作,但是如果页面在在 Vue 载入之前就发生了重定向,页面预渲染的部分将会与重定向的目标页面结构不统一,导致名为 SSR 错配 的问题。
# 一个 SSR 错配的例子
你向浏览器请求 /foo
页面,但服务器并没有找到对应的文件,因此返回了一个 NotFound
页面,即 /404.html
。而注册在 handleRedirectForCleanUrls
中的 beforeEach
钩子将会把路由重定向到 /foo.html
。注意此时 DOM 仍然没有被改变,但是 VDOM 已经被替换成了 /foo.html
中的样子。一般来说,/404.html
不会包含导航栏或者侧边栏,但是此时的 VDOM 仍然会尝试将它们注入到 DOM 中。这就使得 DOM 与 VDOM 发生了错配,导致了报错并阻止了 Vue 的进一步运行。这就是 SSR 错配的结果。
参考:
# 使用“纯粹”的 HTML
我们有时也想摆脱各种 preload, prefetch 脚本,得到一个更加简单的 HTML 页面。我们可以将 Vue 和 markdown 仅作为组织网站结构的工具,不需要任何运行时的 JS 代码,当想要 CSS 时也可以简单地通过在 <head>
中加一个 <link>
实现。
参考:
# 解决方案
这或许令人惊奇,但是解决这两个问题的方式是类似的。VuePress 使用 ssrTemplate
作为 SSR 渲染的模板。我们要做的只是删去其中的一部分。对于第一个问题,由于所有发生 SSR 错配的重定向都发生在 404 页面,我们只需要禁用该页面的 SSR 即可。由于 404 页面一般都较为简单并且不太需要针对搜索引擎进行优化,这种解决方案是可行的。第二个问题则相反,需要我们不是删除 SSR 预渲染的部分,而是保留它们,删去用于注入 Vue 的脚本即可。这个插件同时提供了这两方面的能力,可以使我们有效地控制每一个页面。
# 示例
一个正常情况下生成的 HTML 文件会长这样:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>VuePress</title>
<meta name="description" content="" />
<link rel="preload" href="/assets/css/0.styles.53e4595a.css" as="style" />
<link rel="preload" href="/assets/js/app.3fe70f2e.js" as="script" />
<link rel="preload" href="/assets/js/3.db027a4f.js" as="script" />
<link rel="preload" href="/assets/js/5.222ba868.js" as="script" />
<link rel="prefetch" href="/assets/js/2.418462f5.js" />
<link rel="prefetch" href="/assets/js/4.f6ba1d08.js" />
<link rel="prefetch" href="/assets/js/6.d7ad24ac.js" />
<link rel="stylesheet" href="/assets/css/0.styles.53e4595a.css" />
</head>
<body>
<div id="app" data-server-rendered="true">
<div class="content default"><p>readme</p></div>
<div class="global-ui"></div>
</div>
<script src="/assets/js/app.3fe70f2e.js" defer></script>
<script src="/assets/js/3.db027a4f.js" defer></script>
<script src="/assets/js/5.222ba868.js" defer></script>
</body>
</html>
当我们使用 noSSR
模式时,会生成这样的 HTML:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>VuePress</title>
<meta name="description" content="" />
<link rel="preload" href="/assets/css/0.styles.53e4595a.css" as="style" />
<link rel="preload" href="/assets/js/app.3fe70f2e.js" as="script" />
<link rel="preload" href="/assets/js/3.db027a4f.js" as="script" />
<link rel="preload" href="/assets/js/5.222ba868.js" as="script" />
<link rel="prefetch" href="/assets/js/2.418462f5.js" />
<link rel="prefetch" href="/assets/js/4.f6ba1d08.js" />
<link rel="prefetch" href="/assets/js/6.d7ad24ac.js" />
<link rel="stylesheet" href="/assets/css/0.styles.53e4595a.css" />
</head>
<body>
<div id="app"><!-- 注意这里 --></div>
<script src="/assets/js/app.3fe70f2e.js" defer></script>
<script src="/assets/js/3.db027a4f.js" defer></script>
<script src="/assets/js/5.222ba868.js" defer></script>
</body>
</html>
当我们使用 noScript
模式时,会生成这样的 HTML:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>VuePress</title>
<meta name="description" content="" />
<!-- 注意这里 -->
<link rel="stylesheet" href="/assets/css/0.styles.53e4595a.css" />
</head>
<body>
<div id="app">
<div class="content default"><p>readme</p></div>
<div class="global-ui"></div>
</div>
<!-- 注意这里 -->
</body>
</html>