vue3 vitessr源码解析

vuessr关键流程

流程有了我们再来看下一些过程中的点

vite启动

在 dev 下,我们需要 SSR 提供和非 SSR 模式一致的极速的 HMR 体验。

下面例子是一个启动 vite SSR dev Node.js 端的用例。主要用到了 ssrLoadModule SSR 运行的 API。

javascript 复制代码
import express from 'express'
import { createServer } from 'vite'
const app = express()
const vite = createServer({
  root,
  server: {
    middlewareMode: true,
    watch: {
      // During tests we edit the files too fast and sometimes chokidar
      // misses change events, so enforce polling for consistency
      usePolling: true,
      interval: 100
    },
    hmr: {
      port: hmrPort
    }
  },
  appType: 'custom'
})
app.use(vite.middlewares)
app.use('*', async (req, res) => {
  try {
    let template = fs.readFileSync(resolve('index.html'), 'utf-8')
    template = await vite.transformIndexHtml(url, template)
    const render = (await vite.ssrLoadModule('/src/entry-server.js')).render

    const [appHtml, preloadLinks] = await render(url, manifest)

    const html = template
      .replace(`<!--preload-links-->`, preloadLinks)
      .replace(`<!--app-html-->`, appHtml)

    res
      .status(200)
      .set({ 'Content-Type': 'text/html' })
      .end(html)
  } catch (e) {
    res.status(500).end(e.stack)
  }
})

ssrLoadModule

这个 API 的作用是在 Node.js 环境下加载模块及其依赖,并且在 Vite 同一个 Node.js 环境下执行,并且让这个模块经过 Vite 所有插件 SSR 模式转换,让同一份代码在 SSR 模式下也支持 Vite 提供的语法糖。

下面介绍下这个方法的 happy path。

  • ensureEntryFromUrl,解析加载的 URL,并在模块图上创建这个模块。
  • ssrTransform,将所有的 esm import, export 都转换成 vite_ssr_* 函数。
  • 执行模块。使用 AsyncFunction 提供 vite_ssr_* 相关函数,对原来的 import 语句将会执行ssrLoadModule继续加载,对 export 语句则在 ssrModule 对象上添加返回对象的引用。

❓ 循环依赖问题

  • 因为模块对象在加载之前就已经注册到模块图上了,如果这个模块也正在初始化就会直接返回这个模块ssrModule引用。
  • 如果加载中的模块还是被再次调用ssrLoadModule去加载,也是直接从模块图上取这个模块ssrModule引用,避免模块二次加载。

vite 在加载模块的时候,避免模块进行二次加载,循环依赖获取到的是这个模块没有初始化完成的ssrModule引用。

❓ 为什么快

SSR dev 处理逻辑和纯 dev 处理逻辑是一致的。SSR 还是根据运行时需要加载的模块进行实时编译然后放到 Node.js 环境下执行,纯 dev 环境根据浏览器加载模块进行请求 vite 然后实时编译返回到浏览器执行。他们都还是保持着 vite 细颗粒度的更新和编译模块,所以在 hmr 的场景下还是很快。

vue ssr虚拟 dom相关

vue-server-renderer 包提供了 createBundleRenderer 的 api,可以传入编译打包后的 bundle 代码来创建一个 renderer。renderer 有 renderToString 和 renderToStream 的 api。

内部会通过 vue.createVNode 来执行 bundle 的代码,产生 Vue 实例,之后把 Vue 实例的 vdom 渲染成 html 字符串。返回这个 html 字符串就实现了 SSR

javascript 复制代码
async function renderToString(input, context = {}) {
    // 输入的内容是一个虚拟节点(vnode),则会将其包装在一个带有上下文的应用程序中,并再次调用renderToString函数进行处理
    if (isVNode(input)) {
        // raw vnode, wrap with app (for context)
        return renderToString(vue.createApp({ render: () => input }), context);
    }
    // rendering an app
    // 输入的内容是一个应用程序(app),则会创建一个虚拟节点,并将输入的组件和属性赋值给该虚拟节点。然后,将上下文信息提供给组件树中的所有组件,以便在渲染过程中使用。
    const vnode = vue.createVNode(input._component, input._props);
    vnode.appContext = input._context;
    // provide the ssr context to the tree
    input.provide(vue.ssrContextKey, context);
    // 函数会调用renderComponentVNode函数渲染该虚拟节点,并将结果作为一个缓冲区(buffer)返回。这个缓冲区需要经过unrollBuffer函数的处理,最终得到结果
    const buffer = await renderComponentVNode(vnode);
    const result = await unrollBuffer(buffer);
    // 函数会调用resolveTeleports函数解析组件中的传送门(teleports),并返回最终的结果。
    await resolveTeleports(context);
    return result;
}

SSR的运作过程V3

编译(集中在vite)

json 复制代码
"build:client": "vite build --ssrManifest --outDir dist/client",

在构建客户端代码的时候需要额外生成ssrManifestssrManifest的作用是告知 Node.js 端渲染这个模块下所有的依赖。相关代码

json 复制代码
"build:server": "vite build --ssr src/entry-server --outDir dist/server",

Vite 需要对 SSR 应用需要使用同一份代码构建在 Node.js 和浏览器运行的代码。以 Vue.js 插件为例,当 Vue 编译器接受到ssr: true参数后,就会将模版生成的代码从生成浏览器运行代码 转换成 拼接字符串。这个时候主要做的是代码聚合以及esmodule到cjs转换

初始化

@vue/server-renderer 中的 renderToString 方法通过调用 createRenderContext 函数创建渲染上下文,并将渲染上下文和渲染函数(通过 createApp().component.render 生成)传递给 ssrRender 函数进行渲染。

渲染

ssrRenderComponentssrRenderListssrRenderSuspense等。这些函数用于在服务器端渲染不同类型的组件和指令

ssrGetDirectivePropsssrGetDynamicModelProps。这些函数用于在服务器端渲染过程中获取指令的属性

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax