切換語言為:簡體

Vue3.5新增的useId到底有啥用?

  • 爱糖宝
  • 2024-10-25
  • 2035
  • 0
  • 0

0. 啥是useId

Vue 3.5中新增的useId函式主要用於生成唯一的ID,這個ID在同一個Vue應用中是唯一的,並且每次呼叫useId都會生成不同的ID。這個功能在處理列表渲染、表單元素和無障礙屬性時非常有用,因為它可以確保每個元素都有一個唯一的識別符號。

useId的實現原理相對簡單。它透過訪問Vue例項的ids屬性來生成ID,這個屬性是一個數組,其中包含了用於生成ID的字首和自增數字。每次呼叫useId時,都會取出當前的數字值,然後進行自增操作。這意味著在同一頁面上的多個Vue應用例項可以透過配置app.config.idPrefix來避免ID衝突,因為每個應用例項都會維護自己的ID生成序列。

1. 實現原始碼

export function useId(): string {
  const i = getCurrentInstance()
  if (i) {
    return (i.appContext.config.idPrefix || 'v') + '-' + i.ids[0] + i.ids[1]++
  } else if (__DEV__) {
    warn(
      `useId() is called when there is no active component ` +
        `instance to be associated with.`,
    )
  }
  return ''
}

  • i.appContext.config.idPrefix:這是從當前元件例項中獲取的一個配置屬性,用於定義生成ID的字首。如果這個字首存在,它將被使用;如果不存在,預設使用 'v'

  • i.ids[0]:這是當前元件例項上的 ids 陣列的第一個元素,它是一個字串,通常為空字串,用於生成ID的一部分。

  • i.ids[1]++:這是 ids 陣列的第二個元素,它是一個數字,用於生成ID的自增部分。這裏使用了後置自增運算子 ++,這意味著它會返回當前值然後自增。每次呼叫 useId 時,這個數字都會增加,確保生成的ID是唯一的。

2.設定ID字首

如果不想使用預設的字首'v'的話,可以透過app.config.idPrefix進行設定。

const app = createApp(App)

app.config.idPrefix = 'vid'

3.使用場景

3-1. 表單元素的唯一標識

在表單中,<label> 標籤需要透過 for 屬性與對應的 <input> 標籤的 id 屬性相匹配,以實現點選標籤時輸入框獲得焦點的功能。使用 useId 可以為每個 <input> 元素生成一個唯一的 id,確保這一功能的正常工作。例如:

<label :for="id">Do you like Vue 3.5?</label>
  <input type="checkbox" :id="id" />

const id = useId()

3-2. 列表渲染中的唯一鍵

在渲染列表時,每一項通常需要一個唯一的鍵(key),以幫助 Vue 追蹤每個節點的身份,從而進行高效的 DOM 更新。如果你的列表資料沒有唯一key的話,那麼useId 可以為列表中的每個專案生成一個唯一的鍵。

<ul>
    <li v-for="item in items" :key="item.id">
      {{ item.text }}({{ item.id }})
    </li>
  </ul>

const items = Array.from({ length: 10}, (v, k) => { 
    return {
        text: `Text ${k}`,
        id: useId()
    }
})

上述程式碼渲染結果如下:

Vue3.5新增的useId到底有啥用?

3-3. 服務端渲染(SSR)中避免 ID 衝突

在服務端渲染(SSR)的應用中,頁面的HTML內容是在伺服器上生成的,然後傳送給客戶端瀏覽器。在客戶端,瀏覽器會接收到這些HTML內容,並將其轉換成一個可互動的頁面。如果在伺服器端和客戶端生成的HTML中存在相同的ID,那麼在客戶端啟用(hydrate)時,就可能出現問題,因為客戶端可能會嘗試操作一個已經由伺服器端渲染的DOM元素,導致潛在的衝突或錯誤。

下面是一個使用useId來避免這種ID衝突的實際案例:

服務端程式碼 (server.js)

import { createSSRApp } from 'vue';
import { renderToString } from '@vue/server-renderer';
import App from './App.vue';

const app = createSSRApp(App);

// 假設我們在這裏獲取了一些資料
const data = fetchData();

renderToString(app).then(html => {
  // 將服務端渲染的HTML傳送給客戶端
  sendToClient(html);
});

客戶端程式碼 (client.js)

import { createSSRApp } from 'vue';
import App from './App.vue';

const app = createSSRApp(App);

// 客戶端啟用,將服務端渲染的HTML轉換成可互動的頁面
hydrateApp(app);

在這個案例中,無論是服務端還是客戶端,我們都使用了createSSRApp(App)來建立應用例項。如果我們在App.vue中使用了useId來生成ID,那麼這些ID將在服務端渲染時生成一次,並在客戶端啟用時再次使用相同的ID。

App.vue 元件

<template>
  <div>
    <input :id="inputId" type="text" />
    <label :for="inputId">Enter text:</label>
  </div>
</template>

<script setup>
import { useId } from 'vue';

const inputId = useId();
</script>

App.vue元件中,我們使用了useId來為<input>元素生成一個唯一的ID。這個ID在服務端渲染時生成,幷包含在傳送給客戶端的HTML中。當客戶端接收到這個HTML並開始啟用過程時,由於useId生成的ID在服務端和客戶端是相同的,所以客戶端可以正確地將<label>元素關聯到<input>元素,而不會出現ID衝突的問題。

如果沒有使用useId,而是使用了Math.random()Date.now()來生成ID,那麼服務端和客戶端可能會生成不同的ID,導致客戶端在啟用時無法正確地將<label><input>關聯起來,因為它們具有不同的ID。這可能會導致表單元素的行為異常,例如點選<label>時,<input>無法獲得焦點。

3-4. 元件庫中的 ID 生成

在使用 Element Plus 等元件庫進行 SSR 開發時,爲了避免 hydration 錯誤,需要確保伺服器端和客戶端生成相同的 ID。透過在 Vue 中注入 ID_injection_key,可以確保 Element Plus 生成的 ID 在 SSR 中是唯一的。

// src/main.js
import { createApp } from 'vue'
import { ID_INJECTION_KEY } from 'element-plus'
import App from './App.vue'
const app = createApp(App)
app.provide(ID_INJECTION_KEY, {
  prefix: 1024,
  current: 0,
})


0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.