成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊一聊 Vue-SSR 激活失敗(Vue hydration fails)

開發 項目管理
服務端渲染有很多好處,特別是當像 Nuxt.js 或 GridSome 這樣的網站,無論是使用動態 SSR 還是生成靜態網站,開發 Vue-SSR 應用程序都是一件輕而易舉的事。

認識 Vue-SSR 激活失敗

對于 SSR 服務端渲染這個概念稍有經驗的開發應該都不陌生,官方文檔 Vue SSR 指南 對于什么是服務端渲染、為什么使用服務端渲染以及什么時候使用服務端渲染已經說的很清楚了,結合一張經典的構建過程總結關于 SSR 的基本知識。

1. 基本常識

什么是服務端渲染?

  • 客戶端渲染是在瀏覽器中輸出 Vue 組件,進行生成 DOM 和操作 DOM,并渲染為 HTML 頁面展示;
  • 服務端渲染是將同一個組件渲染為服務器端的 HTML 字符串,將它們直接發送到瀏覽器展示;
  • Vue SSR 則將這些服務端渲染的靜態 HTML "激活"為客戶端上完全可交互的 HTML 頁面,服務端與客戶端渲染的 HTML 混合,體現了 Vue SSR 的一大特性--同構。

為什么使用 SSR?

  • 首屏渲染速度快;
  • SEO,服務端渲染的頁面內容可以被搜索引擎爬蟲獲取;

什么場景下使用 SSR?

SSR 服務端渲染的優勢主要在于首屏渲染與 SEO,那為什么不直接全面推廣使用呢?主要考慮到存在以下劣勢:

  • 代碼復雜度增加 - 為了實現前面提到的同構,應用代碼中需要兼容服務端和客戶端兩種運行情況,那么原先只支持瀏覽器環境運行的 API 方法必須增加特殊處理才能在服務端渲染程序中運行;
  • 涉及構建設置和部署有更多要求 - 完全靜態單頁面應用程序 (SPA) 可以部署在任何靜態文件服務器上,而服務器渲染應用程序,需要處于 Node.js server 運行環境;
  • 更多的服務器負載 - 在 Node.js 中渲染完整的應用程序,

所以是否可以使用服務端渲染 SSR,需要開發者考慮投入產出比,如果應用系統的大多數頁面都不需要 SEO,且首屏時間基本可以滿足需求,使用 SSR 就沒有必要了。結合上面的第 2 點, SSR 的使用場景:

  • 對首屏渲染時間要求高,且盡可能只把首屏內容放到服務端渲染;
  • 對 SEO 要求高的內容。

2. 主要痛點問題

服務端渲染有很多好處,特別是當像 Nuxt.js 或 GridSome 這樣的網站,無論是使用動態 SSR 還是生成靜態網站,開發 Vue-SSR 應用程序都是一件輕而易舉的事。但從另一方面來講,由于同構帶來的代碼復雜性與 node 端未知錯誤,降低了系統應用的穩定性與可靠性,并不推薦在非必要場景下使用 SSR 開發。盡管在官方指南的指導下,以及在前人踩坑后提供了變通的解決方案,多數錯誤都可以避開或者解決,但仍有未知錯誤導致的報錯致使我們無從下手。

比如我曾在 SSR 服務端渲染中遇到了一個 Bug,排查了整整 2 個小時,定位為客戶端渲染失敗,但導致客戶端渲染失敗的原因又有很多。

Error: Error while mounting app: HierarchyRequestError: Failed to execute 'appendChild' on 'Node':
This node type does not support this method. at some-file.js:1

在 SSR 應用中不斷踩坑地經歷讓我意識到:大多數時候遇到的棘手錯誤都是 Vue 客戶端激活失敗導致的。

3. 什么是 Vue 客戶端激活失敗(client-side hydration)客戶端激活

官方指南對 Vue 客戶端激活(client-side hydration)定義很清晰:

所謂客戶端激活,指的是 Vue 在瀏覽器端接管由服務端發送的靜態 HTML,使其變為由 Vue 管理的動態 DOM 的過程。

這里 hydration,又可以翻譯為“注入”,可以了理解為將在客戶端生成的虛擬 DOM 結構注入到服務端渲染出的 HTML 中使這些靜態的 HTML 變為動態的。從上面的構建過程圖我們也可以看到,這里的激活發生在客戶端構建完成的包到渲染為 HTML 的過程中。

客戶端激活失敗

服務端已經渲染好了 HTML,無需將其丟棄再重新創建所有的 DOM 元素,而是去激活這些渲染好的靜態 HTML,使他們成為動態的以能夠響應后續的數據變化。但如果 Vue 客戶端生成的虛擬 DOM 樹與服務端渲染的 DOM 結構無法匹配,就會發生客戶端激活失敗。

這里又分為開發模式和生產模式兩種情況:

在開發模式下,如果無法匹配,它將退出混合模式,丟棄現有的 DOM 并從頭開始渲染;

在生產模式下,此檢測會被跳過,以避免性能損耗。

這也就能說明為什么有些錯誤僅在生產模式下才會出現。

在排查錯誤問題的過程中,讀到(谷歌)了一篇關于 SSR 服務中客戶端激活失敗梳理較為細致的文章,列舉出了大多數可能出現的原因以及解決方案。

原文地址 - What to do when Vue hydration fails

二、正文 - What to do when Vue hydration fails

1. 什么是 Vue 激活失敗

第一次聽到"hydration"這個詞時,它對我來說非常抽象,我想不出它的意思。最終,我意識到它并不像一開始聽起來那么復雜:

hydration 是 Vue 轉換服務器端渲染的標記并使其具有動態反應性的過程,因此它可以反映來自 Vue 的動態更改。

如果 Vue 期望與服務端渲染的 HTML 不同的標記,則 hydration 將失敗(也稱為“Vue 將 hydration 丟棄”)。你可以在官方的 Vue SSR 文檔中閱讀更多相關內容。

2. 如何識別激活失敗

我們現在已經意識到激活是什么和在什么時候會失敗,但是我們作為開發者要如何發現激活沒有如預期一般工作呢?

有兩條錯誤消息肯定會指出激活失敗但都有限制條件。

1)第一條是僅在開發環境中出現:Parent:

Parent:  <div class="container"> client-hook-3.js:1:16358
Mismatching childNodes vs. VNodes: NodeList(3) [ p, p, p ] Array [ {} ]

[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content.
This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>.
Bailing hydration and performing full client-side render.

2)第二條錯誤消息是僅在生產環境下使用靜態生成站點時:

Error: Error while mounting app: HierarchyRequestError: Failed to execute 'appendChild' on 'Node':
This node type does not support this method. at some-file.js:1

眾所周知,激活僅在頁面首先由服務端渲染時發生,因此通常僅在你應用的初始化請求中。

當通過一個標簽導航時激活失敗是不可見的,僅能在硬重載時才能復現,這就使得在開發環境下發現激活失敗問題變得更加困難。

因此激活錯誤有時僅在階段系統或者更糟,僅在生產環境下被發現。在極少數情況下,甚至不會打印出錯誤而僅僅時某些組件停止工作。

3. 一般引發錯誤的原因

現在我們了解了如何發現激活錯誤,接下來將研究導致 Vue 激活錯誤的典型原因。當然這里不可能覆蓋所有可能的原因,因為這些錯誤原因差別很大,而且主要取決于代碼。

在以下章節中,每次提到服務端渲染,它就與兩種情況都相關(動態 SSR 和靜態站點生成),因為從技術上講,兩者都具有服務端渲染內容(除非另有說明)。

1)不合理的 HTML當激活失敗發生時,首先要檢查的應該是 HTML 是否合理,官方指南有這類踩坑提示,同時一般會有錯誤信息提示:


This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>

不幸的是,不合理的 HTML 通常不是激活失敗的原因。不過,你還是應該仔細檢查你的標記。

另外,你還要確保檢查縮小設置,因為過度的 HTML 縮小可能會導致無效的 HTML。

如果你有用戶生成的輸出或來自 CMS 的內容,需要驗證此內容也是有效的 HTML。

最后,第三方插件或服務也可能影響和操縱 HTML。后者的一個常見示例是 Cloudflare,當你啟用了它們的服務 -- 如 HTML 縮小,Rocket loader 或其他更改頁面內容的功能時。

這里有一個簡單的示例 codeandbox,其中包含無效的 HTML 并觸發了激活失敗。

2)修改 HTML 的腳本關于腳本:如果向 Vue 應用中插入第三方 JS 文件,也可以在 Vue 接收并激活來自服務器的 HTML 之前更改 HTML。

3)服務器和客戶端的狀態不同服務器和客戶端上狀態不一致是發生激活失敗最常見的原因。像往常一樣,不一致的原因千差萬別。

日期、時間戳和隨機化

當網站包含日期或者時間戳時,應盡可能小心并使其盡可能靜態,尤其在網站是靜態生成的情況下。如果客戶端評估像 new Date() 這樣的表達式,則該表達式可能會與在服務器上開發階段檢索相同日期時生成的日期不同。這也讓我對公司的“關于”頁面感到困惑,在該頁面上,我想根據當前分鐘對顯示的人員進行排序。

export const deterministicRotate = (arr) => {
if (arr.length <= 1) {
return arr
}
const rotations = (new Date()).getMinutes() % arr.length

return rotations ? arr : arr.reverse()
}

如果用戶打開頁面的時間很奇怪,則計劃將陣列反轉。當使用動態 SSR 時效果很好。但當切換到靜態生成的 JAMstack 站點時,該功能就會成為一個 Bug。你可以在一分鐘后點擊鏈接刷新,會發現名字和人正確的交換了,但圖片和原來一致。糟糕!這是由于服務器和客戶端時間不匹配導致的。在移除不確定性代碼后工作恢復正常。

  • 授權

不一致的另一個常見原因是用戶身份驗證。這適用于動態 SSR 和靜態站點生成。當僅在客戶端(例如,在 localStorage 中)上存儲身份驗證狀態時,服務器“不知道身份驗證”。這將不可避免地導致激活問題,因為登錄時服務器和客戶端信息根本不同。因此,如果服務器不知道正在靜態生成頁面的身份驗證狀態**,**則不應在服務器端呈現任何與身份驗證相關的組件。你可能想知道為什么它總是適用于靜態網站:因為當生成網站時,它是 HTML,而序列化的代碼是“無狀態的”。在構建階段,我們無法考慮“已登錄的用戶狀態”。這意味著必須從服務器上的渲染中排除所有與身份驗證相關的組件。

  • 其他原因

除了這兩種情況外,還有更多邊緣情況可能會打擊你并引起不一致。即使未在此處列出,我們也將解決激活錯誤!首先,我們應該將其范圍縮小到導致問題的 DOM 元素。

4. 解決激活失敗問題

1)發現導致激活失敗的元素我們可以使用您最喜愛的瀏覽器上的 devTools 縮小問題到一個特定的組件或者 DOM 元素。

  • 確保你在開發環境下
  • 打開開發調試工具
  • 觸發激活警告(通常通過重載網頁)
  • 展開[Vue Warn] The client side ...錯誤消息查看追蹤堆棧(取決于瀏覽器,也打開彈出的 VueJS 列表)
  • 點擊一個激活回調,將會打開 Vue 激活函數的源代碼
  • 現在,無論何時這個函數返回 false 都設置一個 debugger,在撰寫文本時,這種情況發生了三遍:
if (process.env.NODE_ENV !== 'production') {
if (!assertNodeMatch(elm, vnode, inVPre)) {
return false //HERE
}
}
if (process.env.NODE_ENV !== 'production' &&
typeof console !== 'undefined' &&
!hydrationBailed
) {
hydrationBailed = true;
console.warn('Parent: ', elm);
console.warn('server innerHTML: ', i);
console.warn('client innerHTML: ', elm.innerHTML);
}
return false //HERE
}
 if (process.env.NODE_ENV !== 'production' &&
typeof console !== 'undefined' &&
!hydrationBailed
) {
hydrationBailed = true;
console.warn('Parent: ', elm);
console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children);
}
return false //HERE
}

這同樣允許在激活失敗前檢查激活函數的參數。

  • 最后但同樣重要的是,讓激活錯誤復現,通常再次重載頁面是有可能的但有時更困難
  • 你現在看到觸發了我們的一個斷點,腳本停止執行了
  • 現在打開調試工具的控制臺,在激活失敗的地方寫一個元素去獲取 DOM 元素。使用 DOM 元素,你將能夠將激活錯誤追溯到你的 Vue 組件之一
  • 繼續執行下一步 PS:這是用戶 budden73對此 StackOverflow 答案改編后的問題定位順序。

2)確保你的 HTML 標記合理現在你發現了導致問題的代碼,你首先要做的事確保你的標記(也許來自一個 API)是合理的。像這樣的代碼<p><p>Text</p></p> 無效,因為一個p元素不允許在其中包含其他塊元素(如段落標簽)。但是注意,標記不允許像

這樣的標簽作為子元素,這些標記是 Vue 過渡的默認標記。你可以通過<Transition tag="div">進行改變。因為一個p元素不允許在其中包含其他塊元素(如段落標簽)。但是注意,標記不允許像

3)解決服務器與客戶端之間的不一致在 debugger 期間,你能夠從服務器看到結果和重新繪制客戶端側。如果存在不同,你可以看一看,你是如何獲取數據和你在服務端或客戶端渲染了什么。一個常見的問題是靜態網頁的認證,因為 HTML 在構建時生成的是無狀態的,因此不知道任何授權狀態,你應用的所有和授權有關的部分都應該在客戶端重新渲染。否則,在客戶端有授權狀態的用戶,因為登錄而期望從服務端獲取不同的 HTML。然后只剩下一個方案...

4)最終避免措施:最后一個解決激活錯誤的選擇是完全避免組件出現激活錯誤。這對于在靜態生成的頁面上與身份驗證相關的組件來說是必須的,有時對于交付你不能更改但必須嵌入的內容的組件(例如,來自第三方應用程序)也是必需的。正如我們在一開始了解到的,激活僅發生在組件被同時渲染在服務端和客戶端時。為了避免激活失敗,可以通過標簽避免重新渲染服務端組件。唯一的缺點:該組件不包含在服務器返回的 HTML 中,對 SEO 沒有幫助。

5. 總結

這篇文章結束了!現在你了解了更多關于 vue 激活失敗的知識:

  • 什么是激活以及它做了什么?
  • 激活怎么失敗的以及如何發現激活失敗?
  • 激活失敗的一般原因
  • 如何調試激活失敗及解決你的應用程序

我希望這篇文章很有見解,并且你學到了一兩件事。你是否遇到了此處未描述的激活錯誤原因,或者我錯過了一個常見原因?隨時在 Twitter 上或通過郵件給我發消息。而且像往常一樣 —— 如果您能宣傳并與同事分享博客文章,我將很高興。

三、踩坑小結

這篇博客講到了大多數我在服務端渲染中遇到的客戶端激活失敗報錯問題,甚至也包括了我沒有遇到的問題。當然這篇博客也有不足,比如這是 2020 年的文章,其中一些問題也許有了新的進展,當然可能也有許多如我一般的新手還在踩坑。最后整理一下我在開發期間學習踩坑所得經驗:

1. 同構

因為我們采用同構的目的是寫一份盡量通用的代碼, 讓它運行在兩端。所以我們需要熟悉不同端的運行環境, 至少要熟悉相關 API,Node.js 端是沒有瀏覽器對象的, 所以 window, document, DOM 操作都沒法執行。同理, 瀏覽器端是沒有 process 對象的,他們各自的 API 實現也有差別, 這點需要特別留意。還有比較麻煩的就是第三方庫的引入, 有時候你并不知道引入的庫能不能完全運行在 Node 端/瀏覽器端,如果它只能運行在純瀏覽器環境, 那可以在 created 階段之后引入和執行來避開 Node.js 下執行。

2. 數據預取問題

問題:使用 v-html 注入動態獲取的 HTML 內容的時候. 如果 HTML 內容有<script>

所包含的 JS 代碼, 會發現 script 中的事件綁定失效。

分析:其實在這里頁面被渲染了兩次, 第一次是發生在 SSR 直接交給瀏覽器的時候: 此時<script>完整被渲染在瀏覽器里, 其內容正常執行, 事件綁定也正常的綁定在了當時的 DOM 元素上。而第二次渲染時, 走的是 CSR: 在這時由于是以 v-html 的方式來渲染替換 HTML, 但是 v-html 實質上是 innerHTML 操作, 這樣<script>雖然會被替換上去, 但是其中的內容不會執行(innerHTML 為安全性考慮而設計)。所以經過這樣兩次渲染之后, 此時的 DOM 元素是第二次渲染時得到的, 而正常執行過的 JS 的事件綁定是綁在在第一次渲染出來的 DOM 元素上, 這樣就出現了雖然 DOM 存在,但是無法觸發該 DOM 上的事件的情況。

解決方法: 將獲取到的 HTML 內容進行匹配, 剔出<script>內容, 無論第一次或第二次渲染, 只將內容交給 v-html 頁面, 然后單獨在生命周期 updated(頁面已將 HTML 內容渲染完成)中將<script>創建出來, 添加到頁面里自動執行, 實現綁定。并在下一次頁面重渲染(可能是頁面跳轉來到)時判斷<script>是否存在, 如果真則先刪去再添加, 這樣避免添加多余的<script>塊。

** 那么頁面為什么會渲染兩次呢? **

這是由于 Vue SSR 在初始頁面渲染完成后會有一次 hydration 過程, 在這個過程中會照常執行流程 mounted 等生命周期。此時會判定是否此時的組件渲染出的內容與 SSR 渲染得到的內容一致, 如果出現不一致就會單獨執行一次額外的 CSR(客戶端激活失敗), 以達到頁面被能正確地渲染。而因為我們使用了 v-html, 這個過程只有在 CSR 時才會被執行, 所以導致了兩次渲染出來的內容不一致, 觸發了 Vue SSR 的”補償渲染機制”, 進而執行了第二次渲染。

參考文章

  • [Vue SSR 指南] (https://ssr.vuejs.org/zh/guide/hydration.html)
  • [What to do when Vue hydration fails] (https://blog.lichter.io/posts/vue-hydration-error/)
  • [徹底理解服務端渲染 - SSR 原理 ] (https://github.com/yacan8/blog/issues/30)
  • [Vue.js 服務端渲染(SSR)不完全指北] (https://lqs469.com/Vue.js%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93(SSR)%E4%B8%8D%E5%AE%8C%E5%85%A8%E6%8C%87%E5%8C%97/)
  • [從頭開始,徹底理解服務端渲染原理(8 千字匯總長文)] (https://juejin.cn/post/6844903881390964744#heading-16)
責任編輯:姜華 來源: 微醫大前端技術
相關推薦

2021-02-15 15:36:20

Vue框架數組

2021-02-22 14:04:47

Vue框架項目

2025-02-18 00:00:05

vue后端權限

2018-06-07 13:17:12

契約測試單元測試API測試

2023-09-22 17:36:37

2021-01-28 22:31:33

分組密碼算法

2020-05-22 08:16:07

PONGPONXG-PON

2020-08-12 08:34:16

開發安全We

2022-11-26 00:00:06

裝飾者模式Component

2020-06-28 09:30:37

Linux內存操作系統

2022-10-08 11:33:56

邊緣計算云計算

2018-01-10 14:13:04

測試矩陣API測試

2019-12-17 10:06:18

CDMA高通4G

2022-03-08 16:10:38

Redis事務機制

2020-09-08 06:54:29

Java Gradle語言

2022-03-29 09:56:21

游戲版本運營

2021-01-01 09:01:05

前端組件化設計

2019-02-13 14:15:59

Linux版本Fedora

2021-08-04 09:32:05

Typescript 技巧Partial

2021-01-29 08:32:21

數據結構數組
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99re在线视频精品 | 毛片av免费在线观看 | 亚洲视频在线免费观看 | 97精品一区二区 | 国产三级在线观看播放 | av资源中文在线 | 久久久久国产精品午夜一区 | 中文字幕一区二区三区在线乱码 | 亚洲国产高清免费 | 成人在线电影在线观看 | 精品国产乱码久久久久久图片 | 欧美日韩国产精品一区二区 | 国产精品视频在线观看 | 国产精品免费观看 | 精品久久久久久亚洲国产800 | 黄色在线观看 | 久草视频在线播放 | 瑟瑟免费视频 | 日韩在线视频一区 | 亚洲视频免费观看 | 不卡av在线| 欧美激情久久久 | 色综合色综合网色综合 | 欧美h版| 男女免费视频网站 | 国产欧美精品一区二区色综合朱莉 | av男人的天堂av | 亚洲精品黄色 | 免费黄色片视频 | 日韩三区在线观看 | 亚洲手机视频在线 | 国精产品一品二品国精在线观看 | 免费视频二区 | 婷婷国产一区 | 色视频成人在线观看免 | 一级毛片免费看 | av影音| 欧美精品久久久久 | 亚洲免费影院 | 午夜丰满寂寞少妇精品 | 日韩一区二区三区av |