Vue SSR原理

当搜索引擎的爬虫访问我们的站点时,如果只看到一句冷冰冰的 <div id="app"></div>,SEO 基本就凉了。Vue SSR(Server-Side Rendering,服务端渲染)正是为了解决这个问题:让首屏 HTML 在服务器上生成,既能被爬虫读懂,又能让用户以最短的时间看到内容。

一、两份入口文件,一个共享的"根"

Vue 项目的传统 SPA 只有一个 main.js,而 SSR 需要两个入口:

  • app.js

    这是"纯粹"的 Vue 根实例工厂,既不挂载 DOM,也不关心运行在哪个环境。它返回一个干净的 new Vue(),被客户端和服务端共同引用。

  • client-entry.js

    拿到 app.js 返回的实例后,直接 mount('#app'),把静态标记激活成可交互的 SPA。

  • server-entry.js

    在 Node 环境里执行,职责有三件:

    1. 调用 app.js 创建根实例;
    2. 根据请求 URL 做路由匹配,找到需要渲染的组件;
    3. 执行组件暴露的 asyncDatafetch,把数据预取到 Vuex store。

这样设计让"渲染"与"激活"解耦,同一份业务代码跑在两端。

二、Webpack 打出两份 Bundle

构建时,Webpack 会跑两次:

  • Client Bundle

    打包所有客户端代码,输出到 dist/client,浏览器下载后负责激活静态 HTML。

  • Server Bundle

    打包所有服务端代码,输出到 dist/server,Node 进程通过 vue-server-renderer 读取这份 Bundle,生成首屏 HTML。

两份 Bundle 都包含业务组件,但前者带浏览器运行时,后者只保留渲染逻辑,体积更小。

三、服务器收到请求,一条流水线干活

当用户或爬虫发来 GET /article/42

  1. Node 进程加载 server-entry.js,创建一个新的 Vue 实例。
  2. 路由匹配到 Article.vue,触发 asyncData 钩子,拉取文章详情并写入 store。
  3. vue-server-renderer 把组件树渲染成字符串,插入到模板中的 <!--vue-ssr-outlet--> 占位符里。
  4. 为了让客户端"无缝续命",服务器把 store 状态序列化成一段脚本:
html 复制代码
   <script>window.__INITIAL_STATE__ = {...}</script>
  1. 最终拼好的 HTML 响应给浏览器,首屏直出完成。

四、浏览器"激活"静态标记

浏览器拿到 HTML 后,做了三件事:

  1. 解析 DOM 并立即渲染,用户瞬间看到文章标题与正文。

  2. 加载 Client Bundle,执行 client-entry.js,创建同构的 Vue 实例。

  3. 通过 __INITIAL_STATE__ 恢复 store 数据,再调用 hydrate 而非 mount

    hydrate 会对比服务端返回的 DOM 与客户端虚拟 DOM,复用已有节点、绑定事件,把"死"的 HTML 激活成"活"的 SPA。

这一步叫 客户端激活(Client Hydration),只有 DOM 结构与数据完全一致才能成功,否则 Vue 会整段替换,带来性能损耗。

五、交互回归 SPA 常态

激活完成后,所有路由跳转、数据更新都由浏览器接管,退化为普通单页应用。

SSR 只在首屏出场一次,之后不再参与。这样既享受了 SEO 与首屏性能,又保留了 SPA 的流畅体验。

六、总结

VUE SSR 的核心思想是让服务器先跑一次组件的渲染函数,把结果 HTML 交给浏览器,浏览器再用同一份代码激活它。

相关推荐
小离a_a36 分钟前
使用原生css实现word目录样式,标题后面的...动态长度并始终在标题后方(生成点线)
前端·css
郭优秀的笔记1 小时前
抽奖程序web程序
前端·css·css3
布兰妮甜1 小时前
CSS Houdini 与 React 19 调度器:打造极致流畅的网页体验
前端·css·react.js·houdini
小小愿望2 小时前
ECharts 实战技巧:揭秘 X 轴末项标签 “莫名加粗” 之谜及破解之道
前端·echarts
小小愿望2 小时前
移动端浏览器中设置 100vh 却出现滚动条?
前端·javascript·css
fail_to_code2 小时前
请不要再只会回答宏任务和微任务了
前端
摸着石头过河的石头2 小时前
taro3.x-4.x路由拦截如何破?
前端·taro
lpfasd1232 小时前
开发Chrome/Edge插件基本流程
前端·chrome·edge
练习前端两年半3 小时前
🚀 Vue3 源码深度解析:Diff算法的五步优化策略与最长递增子序列的巧妙应用
前端·vue.js
烛阴3 小时前
TypeScript 接口入门:定义代码的契约与形态
前端·javascript·typescript