在vue中如何书写 SSR 友好的代码

文章目录

  • 前言
  • 服务端的响应性​
  • 组件生命周期钩子​
  • 访问平台特有 API​
  • 跨请求状态污染​
  • 激活不匹配​
  • 自定义指令​
  • teleports​
  • 后言

前言

hello world欢迎来到前端的新世界


😜当前文章系列专栏:vue.js
🐱‍👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢大家指出)🌹
💖感谢大家支持!您的观看就是作者创作的动力

服务端的响应性​

在 SSR 期间,每一个请求 URL 都会映射到我们应用中的一个期望状态。因为没有用户交互和 DOM 更新,所以响应性在服务端是不必要的。为了更好的性能,默认情况下响应性在 SSR 期间是禁用的。

组件生命周期钩子​

因为没有任何动态更新,所以像 onMounted或者 onUpdated 这样的生命周期钩子不会在 SSR 期间被调用,而只会在客户端运行。
你应该避免在 setup() 或者 <script setup> 的根作用域中使用会产生副作用且需要被清理的代码。这类副作用的常见例子是使用 setInterval 设置定时器。我们可能会在客户端特有的代码中设置定时器,然后在 onBeforeUnmount onUnmounted 中清除。然而,由于 unmount 钩子不会在 SSR 期间被调用,所以定时器会永远存在。为了避免这种情况,请将含有副作用的代码放到 onMounted 中。

访问平台特有 API​

通用代码不能访问平台特有的 API,如果你的代码直接使用了浏览器特有的全局变量,比如 window 或 document,他们会在 Node.js 运行时报错,反过来也一样。

对于在服务器和客户端之间共享,但使用了不同的平台 API 的任务,建议将平台特定的实现封装在一个通用的 API 中,或者使用能为你做这件事的库。例如你可以使用 node-fetch 在服务端和客户端使用相同的 fetch API。

对于浏览器特有的 API,通常的方法是在仅客户端特有的生命周期钩子中惰性地访问它们,例如 onMounted

请注意,如果一个第三方库编写时没有考虑到通用性,那么要将它集成到一个 SSR 应用中可能会很棘手。你或许可以通过模拟一些全局变量来让它工作,但这只是一种 hack 手段并且可能会影响到其他库的环境检测代码。

跨请求状态污染​

在状态管理一章中,我们介绍了一种使用响应式 API 的简单状态管理模式。而在 SSR 环境中,这种模式需要一些额外的调整。

上述模式在一个JavaScript模块的根作用域中声明共享的状态。这是一种单例模式——即在应用的整个生命周期中只有一个响应式对象的实例。这在纯客户端的 Vue 应用中是可以的,因为对于浏览器的每一个页面访问,应用模块都会重新初始化。

然而,在 SSR 环境下,应用模块通常只在服务器启动时初始化一次。同一个应用模块会在多个服务器请求之间被复用,而我们的单例状态对象也一样。如果我们用单个用户特定的数据对共享的单例状态进行修改,那么这个状态可能会意外地泄露给另一个用户的请求。我们把这种情况称为跨请求状态污染。

从技术上讲,我们可以在每个请求上重新初始化所有 JavaScript 模块,就像我们在浏览器中所做的那样。但是,初始化 JavaScript 模块的成本可能很高,因此这会显著影响服务器性能。

推荐的解决方案是在每个请求中为整个应用创建一个全新的实例,包括 router 和全局 store。然后,我们使用应用层级的 provide 方法来提供共享状态,并将其注入到需要它的组件中,而不是直接在组件中将其导入:

// app.js (在服务端和客户端间共享)
import { createSSRApp } from 'vue'
import { createStore } from './store.js'// 每次请求时调用
export function createApp() {const app = createSSRApp(/* ... */)// 对每个请求都创建新的 store 实例const store = createStore(/* ... */)// 提供应用级别的 storeapp.provide('store', store)// 也为激活过程暴露出 storereturn { app, store }
}

激活不匹配​

如果预渲染的 HTML 的 DOM 结构不符合客户端应用的期望,就会出现激活不匹配。最常见的激活不匹配是以下几种原因导致的:

  1. 组件模板中存在不符合规范的 HTML 结构,渲染后的 HTML 被浏览器原生的 HTML 解析行为纠正导致不匹配。举例来说,一个常见的错误是
    不能被放在

    中:

<p><div>hi</div></p>

如果我们在服务器渲染的 HTML 中出现这样的代码,当遇到

时,浏览器会结束第一个

,并解析为以下 DOM 结构:

<p></p>
<div>hi</div>
<p></p>
  1. 渲染所用的数据中包含随机生成的值。由于同一个应用会在服务端和客户端执行两次,每次执行生成的随机数都不能保证相同。避免随机数不匹配有两种选择:

    • 利用 v-if + onMounted 让需要用到随机数的模板只在客户端渲染。你所用的上层框架可能也会提供简化这个用例的内置 API,比如 VitePress 的 组件。

    • 使用一个能够接受随机种子的随机数生成库,并确保服务端和客户端使用同样的随机数种子 (比如把种子包含在序列化的状态中,然后在客户端取回)。

  2. 服务端和客户端的时区不一致。有时候我们可能会想要把一个时间转换为用户的当地时间,但在服务端的时区跟用户的时区可能并不一致,我们也并不能可靠的在服务端预先知道用户的时区。这种情况下,当地时间的转换也应该作为纯客户端逻辑去执行。

当 Vue 遇到激活不匹配时,它将尝试自动恢复并调整预渲染的 DOM 以匹配客户端的状态。这将导致一些渲染性能的损失,因为需要丢弃不匹配的节点并渲染新的节点,但大多数情况下,应用应该会如预期一样继续工作。尽管如此,最好还是在开发过程中发现并避免激活不匹配。

自定义指令​

因为大多数的自定义指令都包含了对 DOM 的直接操作,所以它们会在 SSR 时被忽略。但如果你想要自己控制一个自定义指令在 SSR 时应该如何被渲染 (即应该在渲染的元素上添加哪些 attribute),你可以使用 getSSRProps 指令钩子:

const myDirective = {mounted(el, binding) {// 客户端实现:// 直接更新 DOMel.id = binding.value},getSSRProps(binding) {// 服务端实现:// 返回需要渲染的 prop// getSSRProps 只接收一个 binding 参数return {id: binding.value}}
}

teleports​

在 SSR 的过程中 Teleport 需要特殊处理。如果渲染的应用包含 Teleport,那么其传送的内容将不会包含在主应用渲染出的字符串中。在大多数情况下,更推荐的方案是在客户端挂载时条件式地渲染 Teleport

如果你需要激活 Teleport 内容,它们会暴露在服务端渲染上下文对象的 teleports 属性下:

const ctx = {}
const html = await renderToString(app, ctx)console.log(ctx.teleports) // { '#teleported': 'teleported content' }

跟主应用的 HTML 一样,你需要自己将 Teleport 对应的 HTML 嵌入到最终页面上的正确位置处。

后言

创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/190415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

i++和++i的区别

i和i的区别 一、基本概念 两者的作用都是自增加1。 单独拿出来说的话&#xff0c;i和i&#xff0c;效果都是一样的&#xff0c;就是ii1 public static void main(String[] args) {int i 0;i;System.out.println(i);}public static void main(String[] args) {int i 0;i;Sys…

【开源】基于JAVA语言的校园电商物流云平台

项目编号&#xff1a; S 034 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S034&#xff0c;文末获取源码。} 项目编号&#xff1a;S034&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 商品数据模块2.3 快…

FH Admin Shiro反序列化漏洞复现

0x01 产品简介 FH Admin 是一款 java 快速开发平台。 0x02 漏洞概述 FH Admin CMS 存在 shiro 反序列化漏洞&#xff0c;该漏洞源于软件存在硬编码的 shiro-key&#xff0c;攻击者可利用该 key 生成恶意的序列化数据&#xff0c;在服务器上执行任意代码&#xff0c;执行系统命…

python自动化第二篇——合并ppt

简述 python合并ppt的方法有很多&#xff0c;但网上常说的python-pptx的方法&#xff0c;我用不了&#xff0c;这里我用了一个python-office的库。但又两个缺点&#xff0c;第一个生成的文档在你的用户名下的文档里&#xff0c;第二个是名字随机。 import office import os im…

华为OD机试真题-查找接口成功率最优时间段-2023年OD统一考试(C卷)

题目描述: 服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示,数组中每个元素都是单位时间内失败率数值,数组中的数值为0~100的整数,给定一个数值(minAverageLost)表示某个时间段内平均失败率容忍值,即平均失败率小于等于minA…

vue3-vite-ts:编写Rollup插件并使用 / 优化构建过程

一、vue3-vite-ts项目&#xff0c;编写Rollup插件并使用的意义 在使用Vue3 Vite TypeScript这种技术栈时&#xff0c;可以使用Rollup插件来优化构建过程&#xff0c;例如使用rollup-plugin-typescript2插件来编译TypeScript代码&#xff0c;使用rollup-plugin-vue插件来处理…

【开源】基于Vue+SpringBoot的康复中心管理系统

项目编号&#xff1a; S 056 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S056&#xff0c;文末获取源码。} 项目编号&#xff1a;S056&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 普通用户模块2.2 护工模块2.3 管理员…

版本控制系统Git学习笔记-Git服务器

文章目录 概述一、协议1.1 本地协议1.2 HTTP协议1.3 SSH协议1.4 Git协议 二、在服务器上搭建 Git 四智武童 一月一&#xff0c;捡花衣。二月二&#xff0c;练大字。三月三&#xff0c;穿新衣。四月四&#xff0c;去考试。考一个状元郎&#xff0c;坐著马车平天下。 概述 Git访…

OpenStack-train版安装之安装Keystone(认证服务)、Glance(镜像服务)、Placement

安装Keystone&#xff08;认证服务&#xff09;、Glance&#xff08;镜像服务&#xff09;、Placement 安装Keystone&#xff08;认证服务&#xff09;安装Glance&#xff08;镜像服务&#xff09;安装Placement 安装Keystone&#xff08;认证服务&#xff09; 数据库创建、创…

WebSocket 实战:构建高效的实时应用

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

使用Spark写入数据到数据库表

项目场景&#xff1a; 使用Spark写入数据到数据库表 问题描述 Column "20231201" not found in schema Some(StructType(StructField(sdate,IntegerType,false),StructField(date_time,StringType,true),StructField(num,LongType,false),StructField(table_code,S…

数据结构详解各种算法

1、设有两个整型顺序表L1&#xff0c;L2&#xff0c;其元素值递增有序存放&#xff0c;请定义该顺序表的元素类型及表类型,设计以下自定义函数&#xff1a; &#xff08;1&#xff09;录入顺序表中所有元素的值。 &#xff08;2&#xff09;将顺序表L1&#xff0c;L2合并为到…

LeetCode 8 字符串转整数

题目描述 字符串转换整数 (atoi) 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09;。 函数 myAtoi(string s) 的算法如下&#xff1a; 读入字符串并丢弃无用的前导空格检查下一…

解决woocommerce产品方面遇到的小问题记录

问题1.通过自定义代码在woocommerce的任意一个产品的价格下面&#xff0c;加上一段文字 id&#xff0c;换成你自己的产品id&#xff0c;div里面的文字换成你自己的自定义文字&#xff0c;代码是加在function.php里面的哦 //添加文字产品价格下面 function insert_custom_cont…

SpringMVC利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值

在我们进行Java的Web应用开发时&#xff0c;如何写更少的代码&#xff0c;做更多的事情。如何让开发更容易上手&#xff0c;更专注于业务层面&#xff0c;不需要太关心底层的实现。这里就分享一些我平时在搭建基础框架时候的一些心得体验。 统一处理返回值 在web应用中&#x…

集成开发环境 PyCharm 的安装【侯小啾python领航班系列(二)】

集成开发环境PyCharm的安装【侯小啾python领航计划系列(二)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

《YOLOv5原创自研》专栏介绍 CSDN独家改进创新实战专栏目录

YOLOv5原创自研 https://blog.csdn.net/m0_63774211/category_12511931.html &#x1f4a1;&#x1f4a1;&#x1f4a1;全网独家首发创新&#xff08;原创&#xff09;&#xff0c;适合paper &#xff01;&#xff01;&#xff01; &#x1f4a1;&#x1f4a1;&#x1f4a1;…

Vue3中的组合式API的详细教程和介绍

文章目录 前言介绍组合式 API 基础setup 组件选项 带 ref 的响应式变量生命周期钩子注册内部 setupwatch 响应式更改独立的 computed 属性后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;vue.js &#x1f431;‍&#x1f453;博主在前端…

PID控制

在PID控制中&#xff0c;输出通常是一个控制量&#xff0c;而不是直接的PWM占空比。输出的具体含义可以根据具体的系统和应用而变化。在这段代码中&#xff0c;PID控制器的输出是 output_calc。 而 CCR_duty 是控制施肥系统的PWM占空比&#xff0c;这是通过PID控制的输出和曲线…

西南科技大学(数据结构A)期末自测练习五

一、选择题&#xff08;每空 1 分&#xff0c;共 5 分&#xff09; 1、下面关于图的叙述中&#xff0c;正确的是&#xff08; &#xff09;。 (1)&#xff0e;回路是简单路径 (2)&#xff0e;存稀疏矩阵&#xff0c;用邻接矩阵比邻接表更省空间 (3)&#xff0e;若有像图中存在…