vuejs设计与实现 18 服务端渲染

Vue

1. 同构渲染



同构渲染

  • 客户端渲染(render.render):Vue.js可以用于构建客户端应用程序,组件的代码在浏览器中运行,并输出DOM元素;
  • 服务端渲染:同时,Vue.js还可以在Node.js环境中运行,它可以将同样的组件渲染为字符串并发送给浏览器;
  • 同构渲染(render.hydrate):Vue.js不仅能够独立进行客户端渲染CSR或服务端渲染SSR,还能将二者融合,形成所谓的同构渲染。
  1. 传统服务端渲染的流程:
    1. 用户通过浏览器请求站点;
    1. 服务器请求API获取数据;
    1. 接口返回数据给服务器;
    1. 服务器根据模板和获取的数据拼接处最终的HTML字符串;
    1. 服务器将HTML字符串发送给浏览器,浏览器解析HTML内容并返回;
  • 缺点:任何一个微小的操作都可能导致页面刷新。在服务端完成页面渲染,会消耗更多的服务端资源;
  1. SPA,客户端渲染
  • CSR是在浏览器中完成模板与数据的融合,并渲染出最终的HTML页面;客户端向服务器或CDN发送请求,获取静态的HTML页面;此时获取的HTML页面通常是空页面,在HTML页面中,会包含<style><link><script>等标签;虽然HTML页面是空白的,但浏览器仍会解析HTML内容,由于HTML页面中存在<link rel="stylesheet"><script>标签,所以浏览器会加载HTML中引用的资源;如app.js,接着服务器或CDN会将相应的资源返回给浏览器,浏览器对CSS和JavaScript进行解释和执行,页面的渲染任务是由JavaScript来完成的,所以当JavaScript被解释和执行后,才会渲染出页面内容,即白屏结束。但初始渲染出来的内容通常是一个骨架,因为还没有请求API获取数据。客户端通过AJAX请求API获取数据,一旦接口返回数据,客户端就会完成动态内容的渲染,并程序完整的页面。
  • 当用户再次点击跳转到其他页面时,浏览器并不会真正的进行跳转动作,即不会进行刷新,而是通过前端路由的方式动态地渲染页面,这对用户的交互体验会非常友好。
  • CSR缺点:产生白屏问题;对SEO不友好;
  1. 同构渲染:分为首次渲染(首次访问或刷新页面)和非首次渲染
  • 首次渲染与SSR工作流程一致,即当首次访问或刷新页面时,整个页面的内容是在服务端完成渲染的,浏览器最终得到是渲染好的HTML页面。但该页面是纯静态的,意味着用户还不能与页面进行交互,因为整个程序的脚本还没加载和执行。该静态的HTML页面也会包含<link><script>等标签;同构渲染产生的HTML页面与SSR产生的HTML页面有一点最大的不同,即前者会包含当前页面所需要的初始化数据,即服务器通过API请求的数据会被序列化为字符串,并拼接到静态的HTML中,最后一起发送给浏览器;这是为了后续的激活操作;
  • 假设浏览器已经接收到初次渲染的静态HTML页面,接下来浏览器会解析渲染该页面,在解析过程中,浏览器会发现HTML代码中存在link、script标签,于是从CDN或服务器获取相应资源,当js资源加载完毕,会进行激活操作(vuejs中的hydration)。激活操作包含两部分内容:(1)Vue.js在当前页面已经渲染的DOM元素以及Vue.js组件所渲染的虚拟DOM之间建立联系;(2)Vue.js从HTML页面中提取由服务端序列化后发送过来的数据,用以初始化整个Vue.js应用程序。
  • 激活完成后,整个应用程序已经完全被Vue.js接管为CSR应用程序了;后续操作都会按照CSR应用程序的流程来执行;如果刷新页面,仍然会进行服务端渲染,然后再进行激活,如此反复。
  • SEO友好;占用服务端资源中等;用户体验较好;
  • 同构:同样一套代码既可以在服务端运行,也可以在客户端运行;如Vue.js编写一个组件,该组件既可以在服务端运行,被渲染为HTML字符串,也可以在客户端运行,就像普通CSR应用程序一样;
  1. 将虚拟DOM渲染为HTML字符串
  • 如何在服务端将虚拟DOM渲染为HTML字符串?
  • renderElementVNode函数,接收用来描述普通标签的虚拟节点作为参数,并返回渲染后的HTML字符串;本质上是字符串的拼接;考虑节点是否是自闭和标签,属性是否合法,属性值转义,子节点类型多样如Fragment、组件、函数式组件、文本等,标签的文本子节点也需要进行HTML转义;class可以是字符串、对象或数组,将不同类型的数据结构序列化成字符串表示;&转义为&amp;,<转义为&lt;,>转义为&gt;,"转义为&quot;,'转义为&#39;
  1. 将组件渲染为HTML字符串
  • 如何将组件类型的虚拟节点渲染为HTML字符串?
  • 执行组件的渲染函数取得对应的虚拟DOM,再将该虚拟DOM渲染为HTML字符串,并作为renderComponentVNode函数的返回值即可;
  • 客户端渲染时,组件的初始化流程:beforeCreate =》初始化data、props等 =》创建组件实例 =》setup执行 =》created =》设置render effect完成渲染;
  • 服务端渲染时,组件的初始化流程:beforeCreate =》初始化data、props等,非响应式数据 =》创建组件实例 =》setup执行 =》created
  • 服务端渲染时,组件的初始化流程与客户端渲染时组件初始化流程基本一致,有2点不同:
    1. 服务端渲染的是应用的当前快照,不存在数据变更以后重新渲染的情况(beforeUpdate/updated都不会执行),因此所有数据都无须是响应式的,基于此,可以减少服务端渲染过程中创建响应式数据对象的开销;
    1. 服务端渲染只需要获取组件要渲染的subTree即可,无须调用渲染器完成真实DOM的创建,因此在服务端渲染时可以忽略“设置render effect完成渲染”这一步(beforeMount、mounted都不会执行)。
  • 在服务端渲染时,无须使用reactive函数为data数据创建响应式版本,且props数据页无须是浅响应的。
  1. 客户端激活原理
  • 对于同构渲染来说,组件的代码会在服务端和客户端分别执行一次。
  • 在服务端,组件会被渲染为静态HTML字符串,然后发送给浏览器,浏览器再把这段纯静态HTML渲染出来。这意味着,此时页面中已经存在对应的DOM元素。同时,该组件还会被打包到一个JavaScript文件中,并在客户端被吓着到浏览器中解释并执行。
  • 当组件的代码在客户端执行时,因为DOM已经存在,不会再次创建DOM,但组件代码在客户端运行时,仍然需要做两件重要的事:1.在页面中的DOM元素与虚拟节点对象之间建立联系;2.为页面中的DOM元素添加事件绑定;
  • 一个虚拟节点被挂载后,为了保证更新程序能正确运行,需要通过该虚拟节点的vnode.el属性存储对真实DOM对象的引用;同构渲染页一样,为了应用程序在后续更新过程中能正确运行,我们需要在页面中已经存在的DOM对象与虚拟节点对象之间建立正确的联系。另外,在服务端渲染的过程中,会忽略虚拟节点中与事件相关的props,当组件代码在客户端运行时,需要将这些事件正确地绑定到元素上。这两个步骤就体现了客户端激活的意义。
  • 当渲染副作用执行挂载操作时,优先检查虚拟节点的vnode.el属性是否已经存在,如果存在,则意味着无须进行权限的挂载,只需要激活即可,否则需要进行全新的挂载。组件的激活操作需要在真实DOM与subTree之间进行。
  1. 编写同构的代码
  • 同构一词指的是,一份代码既可以在服务端运行,又可以在客户端运行。
  • 注意组件的生命周期;beforeUpdate、updated、beforeMount、mounted、beforeUnmount、unmounted等生命周期钩子函数不会在服务端执行;
  • 使用跨平台API,如选择支持跨平台的库,如Axios库作为网络请求库;
  • 特定端的实现,有些功能模块需要区分客户端和服务端实现,如cookie,客户端使用document.cookie读取,服务端需要根据请求头获取;import.meta.env.SSR
  • 避免交叉请求引起的状态污染;为每一个请求创建一个独立的应用实例,对于模块,避免使用模块级的全局变量;
  • 仅在客户端渲染组件中的部分内容,如自行封装ClientOnly组件,被该组件包裹的内容仅在客户端才会被渲染;






参考&感谢各路大神

1. vue.js设计与实现-霍春阳

posted @ 2024-07-24 22:22  安静的嘶吼  阅读(8)  评论(0编辑  收藏  举报