解決 Vue 渲染太慢而導致看到 Mustache (雙花括號)的問題

前言

有時候我們會因為網路太慢導致 Vue 的渲染速度太慢,而導致看到 Mustache (雙花括號)的問題,這個狀況其實並不是很好看,所以這一篇就記錄一下如何解決吧。

什麼原因導致會看到 Mustache (雙花括號)

其實這個問題很有趣,在正常狀況下,你很難會看到以下這張圖

Vue 尚未 Instance 導致看到 Mustache

那麼這個問題是怎麼發生的呢?其主要原因是網路太慢導致 Vue 的 JavaScript 檔案還沒有下載完成導致的。

因為在一般的狀況下 HTML 檔案通常會優先被下載完畢,因此你會先看到 HTML 的內容,當我們瀏覽器開始渲染 HTML 檔案時,就會由上往下開始依序渲染,當遇到 <script> 標籤時,就會開始下載 JavaScript 檔案,JavaScript 檔案下載完成後,就會開始執行 JavaScript 檔案,而這就是一個簡單的渲染流程。

如何實現問題

其實實現方式很簡單,底下我這邊有一個 CodePen 的範例,你可以試著將網路速度調慢,然後觀察一下。

以 Firefox 來講,限速位置底下圖片所示

網路

下拉後你可以選取網路的速度

速度

你只需要選擇 2G Good 或是 3G Good 就可以並重新整理該 CodePen 畫面就可以看到 Mustache (雙花括號)的問題了。

如果是 Chrome 的話,也是相同位置

網路

所以為了解決這問題,我們就要使用 Vue 的 v-cloak 指令來解決這個問題。

v-cloak 指令

其實解決方式非常簡單,只需要在標籤上補上 v-cloak

1
2
3
4
5
6
7
<div id="app">
<p v-cloak>
我的名字是:{{ myName }}
</p>
<br>
<input type="text" v-model="myName">
</div>

接著撰寫一點點的 CSS

1
2
3
[v-cloak] {
display: none;
}

這樣子就可以解決這個問題

但是你會發現使用這個方式稍微有一點蠢,因為你會因此被迫還要替一個簡單的文字補上 HTML 標籤,如 p 標籤,這樣子就會讓你的 HTML 程式碼變得很醜,因此你是可以將 v-cloak 放在 #app 上面,這樣子就可以達到同樣的效果。

那麼你可能會想說…

v-cloak 是 Vue 的指令嗎?前面不是說是因為 Vue 還沒加載完畢才導致渲染很慢嗎?那為什麼可以使用 v-cloak?」

沒有錯,這是 Vue 的指令之一,但是其實核心原理只是單純使用到了 CSS 的 display: none 屬性,這個屬性是可以讓你的元素不顯示在畫面上,接著使用 CSS 選取器的 [v-cloak] 來選取所有有 v-cloak 的 HTML 元素,並將其 display 設為 none,當 Vue 加載完畢後,Vue 就會自動移除 v-cloak,這樣子畫面就會正常顯示,因此你可以說是 Vue 的指令,但是其實是使用 CSS 的 display: none 屬性來達到這個效果,只是你的名稱必須為 v-cloak 而不是 v-hide 或是其他的名稱。

而關於 v-cloak 其實在 Vue 官方文件上面有提到,底下我也截取部分說明

用于隐藏尚未完成编译的 DOM 模板。
当使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。
v-cloak 会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像 [v-cloak] { display: none } 这样的 CSS 规则,它可以在组件编译完毕前隐藏原始模板。
直到编译完成前,<div> 将不可见。

如果你腦袋正在想著是不是使用 v-if 可不可以解決這問題的話,我只能跟你說,不可以,因為 v-if 要能夠被使用也要等 Vue 的加載完畢,所以你是無法使用 v-if 來解決這個問題的。

優化 v-cloak

雖然我們可以使用 display: none 來達到 v-cloak 的效果,但是這樣子會導致畫面白一塊,在使用者體驗上是非常不好的,因此你可以考慮撰寫一點點的 CSS Loading 效果,讓使用者在等待 Vue 的加載時,至少可以看到一點點的東西,這樣子使用者體驗會比較好,這邊我也提供一下簡單版的 CSS Loading 效果

1
2
3
4
5
6
<div id="app" v-cloak>
我的名字是:{{ myName }}
<br>
<input type="text" v-model="myName">
</div>
<div id="loader"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[v-cloak] + #loader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
z-index: 9999;
}

[v-cloak] + #loader:before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 50px;
height: 50px;
border-radius: 50%;
border: 3px solid #ccc;
border-top-color: #333;
animation: spin 1s linear infinite;
}

@keyframes spin {
to {
transform: rotate(360deg);
}
}

這樣子你就可以得到一個簡單的 Loading 效果囉。

參考文獻

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你可以也可以請我「喝一杯咖啡(Donate)」。

Buy Me A Coffee Buy Me A Coffee

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ