Vue3 v-html / Nuxt3 v-html / Css 深度選擇器

Vue v-html

最近在接公司前後台資訊,其中在編輯商品時,是使用CKEditor5這個套件進行編輯的,存在資料庫的內容就會是 HTML 標籤語言,如下:

1
'<p><a href="/">商品描述商品描述</a></p>'

在做 Vue 前端時,就不能使用{{}}大括號在模板語言中直接使用,因為字串中包含實體字符,至於關於實體字符可以參考這篇

那這邊馬上來實際運用一下:
父元件會傳 productDetail 這個 props,定義好 props 及預設值後,我用一個 computed 先進行處理,CKEditor5 在嵌入 iframe 影片時,要做一些處理,因為 CKEditor5 轉換出來的 HTML 在前端顯示不出來。
data_processEmbedTag就是處理後的字串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script setup lang="ts">
interface IProps {
productDetail?: string;
}
const props = withDefaults(defineProps<IProps>(), {
productDetail: '',
});
console.log(props.productDetail);
// translate ck editor5 youtube iframe
const data_processEmbedTag = computed(
() =>
props.productDetail?.replaceAll('oembed', 'iframe').replaceAll('url', 'src').replaceAll('watch?v=', 'embed/') ?? ''
);
</script>

接下來就可以使用v-html綁進去 vue 模板語言裡面的 DOM 元素了:

1
2
3
4
5
<template>
<div class="flex flex-col">
<div v-html="data_processEmbedTag"></div>
</div>
</template>

這是data_processEmbedTag的內容:

這是v-html呈現的內容:

綁進去後你會發現,v-html 裡面所有的元素都吃不到 css,只有存進去資料庫的 inline style 會作用而已。所以這時候你就要使用 vue 的 css 的深度選擇器。

Vue 的 css 的深度選擇器

先說說為什麼要使用到深度選擇器:
Vue 在選染模板語言時,都會賦予元素一個 Hash 屬性,所以在瀏覽器的開發人員工具(F12)中常常會看到data-v-hash的元素,但是使用v-html渲染出來的不會有這樣的 Hash 屬性。
再來因為 Vue 在寫 css 樣式,可以使用關鍵字<style scoped>,可是使你在子物件裡定義的樣式,不會向外汙染全域的樣式,所以在正常情況會使用scoped關鍵字。但這時你寫了一個 style,就會只渲染在同一個 Hash 的原件,沒有 Hash 的是吃不到你設定的樣式的,例如:v-html產生出來的 DOM 元素。
元件在開發人員工具呈現的樣子:

1
<div data-v-bc5f04ec="">...</div>

那冠有scoped的樣式就會這樣呈現:

1
2
3
4
5
table[data-v-bc5f04ec],
th[data-v-bc5f04ec],
td[data-v-bc5f04ec] {
@apply border-1 border-border-color;
}

所以這樣是沒有辦法發揮樣式的作用的。所以就套上深度選擇器吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// deep selector change v-html style
:deep(*) {
@apply text-sm tracking-wide;
table,
th,
td {
@apply border-1 border-border-color;
}
}
:deep(figure.media) {
iframe {
@apply w-full;
height: var(--global-iframe-height);
}
}
:deep(iframe) {
@apply w-full;
height: var(--global-iframe-height);
}

套用上深度選擇器後,就可以正常呈現了:

深度選擇器其他寫法

深度選擇器其實還有其他寫法:
寫法一:::v-deep

1
2
3
4
5
::v-deep table,
::v-deep th,
::v-deep td {
@apply border-1 border-border-color;
}

這種寫法在 Vue3 會被警告:

1
[@vue/compiler-sfc] ::v-deep usage as a combinator has been deprecated. Use :deep(<inner-selector>) instead.

寫法二:>>>

1
2
3
4
5
>>> table,
>>> th,
>>> td {
@apply border-1 border-border-color;
}

寫法三:/deep/

1
2
3
4
5
/deep/ table,
/deep/ th,
/deep/ td {
@apply border-1 border-border-color;
}

寫法二、三寫法在 Vue3 也會被警告:

1
[@vue/compiler-sfc] the >>> and /deep/ combinators have been deprecated. Use :deep() instead.

結論

所以是 Vue3 的狀態下,還是使用:deep(<inner-selector>)的寫法吧!