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。这些函数用于在服务器端渲染过程中获取指令的属性

相关推荐
dy171723 分钟前
element-plus表格默认展开有子的数据
前端·javascript·vue.js
2501_915918414 小时前
Web 前端可视化开发工具对比 低代码平台、可视化搭建工具、前端可视化编辑器与在线可视化开发环境的实战分析
前端·低代码·ios·小程序·uni-app·编辑器·iphone
程序员的世界你不懂5 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
索迪迈科技5 小时前
网络请求库——Axios库深度解析
前端·网络·vue.js·北京百思可瑞教育·百思可瑞教育
gnip5 小时前
JavaScript二叉树相关概念
前端
attitude.x6 小时前
PyTorch 动态图的灵活性与实用技巧
前端·人工智能·深度学习
β添砖java6 小时前
CSS3核心技术
前端·css·css3
空山新雨(大队长)6 小时前
HTML第八课:HTML4和HTML5的区别
前端·html·html5
猫头虎-前端技术6 小时前
浏览器兼容性问题全解:CSS 前缀、Grid/Flex 布局兼容方案与跨浏览器调试技巧
前端·css·node.js·bootstrap·ecmascript·css3·媒体