prerender-spa-plugin 预渲染需显式声明所有静态路由(如['/', '/about']),不支持通配符;必须用 history 模式并配置服务端 fallback 到 index.html;需通过自定义事件触发截图时机;html 文件应设短缓存或禁用缓存。

prerender-spa-plugin 路由白名单必须显式声明
空壳 HTML 的根本问题,是爬虫只拿到 <div id="app"></div> 就结束了。预渲染不是自动扫描所有路由,它只处理你明确写进 routes 数组里的路径。
常见错误是只写了 ['/'],结果 About、Product 等页面依然空白;或者用通配符如 ['/*'] —— 这个插件不支持通配,会直接跳过未声明的路由。
- 必须列出所有需 SEO 的静态路由,比如
['/', '/about', '/products', '/contact'] - 带参数的路由(如
/product/:id)无法自动展开,得手动枚举具体路径,例如['/product/123', '/product/456'] - 如果路由依赖登录态或异步权限校验,预渲染阶段无法触发守卫逻辑,这类页面不适合放进去
history 模式 + 服务端 fallback 是硬性前提
插件生成的静态 HTML 文件,必须能被服务器直接返回。如果你用的是 hash 模式(#/about),Nginx 或 Apache 根本不会把请求转发给预渲染文件,而是全部落到 index.html,导致预渲染失效。
所以两件事缺一不可:
- 前端路由设为
mode: 'history',并确保vue-router配置了base(如部署在子路径下) - 服务端配置 fallback:所有非静态资源请求(如
/about)都返回对应路径下的预渲染 HTML;若该路径无文件,则 fallback 到index.html(供 SPA 正常运行) - Nginx 示例配置中,
try_files $uri $uri/ /about.html =404;这类写法是错的——它只会 fallback 到固定文件,应改为try_files $uri $uri/ /index.html;,再靠预渲染插件生成的/about/index.html实际响应
renderAfterDocumentEvent 触发时机容易误判
插件默认等页面 DOM 加载完就截图,但 Vue 组件里大量数据是异步获取的。如果没等 API 返回、v-if 展开、动态 title 注入完成就截,生成的 HTML 仍是空壳或缺关键文案。
正确做法是主动发一个自定义事件,告诉插件“真正的内容已就绪”:
- 在根组件
mounted()中调用document.dispatchEvent(new Event('render-trigger')) - 插件配置里设
renderAfterDocumentEvent: 'render-trigger' - 别用
DOMContentLoaded或load——它们早于 Vue 渲染完成 - 如果用了
async setup()或onMounted(() => { api.get().then(...).finally(() => dispatch) }),确保dispatch在数据真正挂载到模板后才执行
预渲染产物和真实 SPA 共存时的缓存陷阱
你部署了预渲染 HTML,但用户刷新 /about 时,浏览器可能从内存或磁盘缓存里直接读取旧版 /about/index.html,而里面嵌的 JS 还是老版本,导致功能异常或数据错乱。
这不是插件问题,而是部署层疏忽:
- 静态 HTML 文件本身不能加强缓存(如
Cache-Control: max-age=31536000),否则更新后爬虫和用户都看不到新内容 - 建议对预渲染 HTML 设置短缓存(
max-age=300)或禁用缓存(no-cache),让每次请求都走服务器判断 - 更稳妥的做法是在构建时给 HTML 文件名加哈希(如
/about/index.abc123.html),配合服务端重写规则,但这需要额外配置构建和 Nginx - 注意:预渲染 HTML 中引用的 JS/CSS 资源仍可强缓存,它们本来就应该稳定
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











