2025年8月14日 星期四

[Vue.js] SVG 引入與 Typescript 型別設定

❌錯誤示範

如何在vue專案裡使用 svg 檔案,最直覺的做法:

<template>
    <svg>
        <use href="./assets/penguin-svgrepo-com.svg" width="50%" height="50%" />
    </svg>
</template>

開發階段只要注意路徑正確,就能正常顯示。

但編譯到正式環境時,這段 <use> 標籤使用了 href="data:image/svg+xml,... 來嵌入 SVG 圖片,但這種用法在 <use> 上是無效的,因此圖示不會正確顯示。


改用圖片 <img> 來處理或許可以解決:

<img src="data:image/svg+xml,<svg ...>..." />

雖然這樣瀏覽器就會把它當成圖片正常顯示,但無法用 CSS 改 fillstroke,可調整性較差。


✔️解決方法
1. 安裝 vite-svg-loader 及設定
npm install -D vite-svg-loader

接著,vite.config.ts也要增加設定:

import svgLoader from 'vite-svg-loader';

export default {
    plugins: [
        svgLoader(),
    ],
    // ...
};

2. 新增型別宣告

假設專案並不是使用 Typescript,可以省略這個步驟。

新增 vite-env.d.ts,其主要用途是為 Vite 專案提供型別宣告。它能確保你的 TypeScript 專案能夠正確識別和處理 Vite 特有的功能,而不會報錯。

簡單來說,它就像一個翻譯官,告訴 TypeScript 編譯器:「嘿,Vite 會處理這些特殊檔案,請不要報錯,它們的型別是長這樣。」

// src/vite-env.d.ts
declare module '*.svg' {
    const content: string

    export default content
}

你也可以使用 env.d.ts。但 vite-env.d.ts 的命名方式更明確地表示它是為 Vite 專案服務的。

建議放在 src/ 資料夾的根目錄,Vite 會自動尋找資料夾中的 env.d.tsvite-env.d.ts 檔案,並將其視為專案的型別宣告。

如果你將它放在其他位置,你需要在 tsconfig.jsonincludefiles 欄位中手動指定它的路徑,確保 TypeScript 編譯器能夠找到它。

3. 使用 svg

最後只要調整引用的方法,可以把 svg 當作一個 component 來使用。

<template>
    <Penguin />
</template>

<script setup lang="ts">
import Penguin from '@/assets/penguin.svg'
</script>

這樣一來原本支援的 attr 都可以使用,例如:

<Penguin width="200" height="200" />

參考資料:


2025年7月27日 星期日

[Recat] JSX 常見語法雷區

1. JSX 中 HTML 標籤必須自閉合

HTML 中 input 是 void element,本來不需要閉合標籤。

// ✅ 正確
<input type="text" />

// ❌ 錯誤
<input type="text" >  // JSX 會報錯

-----
2. class 要改寫成 className

JSX 是 JavaScript,不允許使用 JS 關鍵字 class

// ✅ 正確
<div className="box" />

// ❌ 錯誤
<div class="box" />  // React 不認得

-----
3. for 要改寫成 htmlFor

表單 label 的 for 在 JSX 中是保留字,要改成 htmlFor

<label htmlFor="email">Email</label>

-----
4. JS 表達式要放在 {}

Vue.js template 則使用是雙括號 {{}}

const name = "Annie";

// ✅ 正確
<p>Hello, {name}</p>
<h1>{name.toUpperCase()}</h1>

// ❌ 錯誤
<p>Hello, {{name}}</p>

-----
5. style 需用物件寫法,且值要加引號或轉成 string

style 物件的 key 是 camelCase(如: fontSize),不同於 HTML/CSS 的 kebab-case(如: font-size

// ✅ 正確
<div style={{ color: 'red', fontSize: '16px' }} />

// ❌ 錯誤
<div style="color: red; font-size: 16px;" />  // HTML 寫法不支援

雙括號讓解讀困難,因此也把物件先儲存到一個變數中再傳給 style,效果完全一樣。

const config = { color: 'red', fontSize: '16px' };
const element = <div style={config} />

-----
6. JSX 裡只能回傳單一根元素

必須使用 Fragment

// ✅ 正確(使用 <></> Fragment)
return (
  <>
    <Header />
    <Content />
  </>
);

// ❌ 錯誤(JSX 中不能 return 兩個平行元素)
return (
  <Header />
  <Content />
);

-----
7. 事件寫法是駝峰命名,值是函式

非常容易與 HTML 原本的寫法混淆

// ✅ 正確
<button onClick={handleClick}>Click</button>

// ❌ 錯誤
<button onclick="handleClick()">Click</button>

-----
8. 條件渲染

在 JSX 中,沒有像 Vue.js 的 v-if 那樣的指令語法,但可以用 JavaScript 表達式來實現相同的功能,或是三元運算或邏輯 (&&) 來達到條件渲染。

{isLoading && <Loading />}
{isLoggedIn ? <Dashboard /> : <Login />}

-----
9. 條件式為 0 時,通常是非預期的顯示結果

像這樣 isLoading 為 boolean,True 顯示 Loading。

{isLoading && <Loading />}

'', null, False 代表否定、不會顯示任何內容,0 雖然也歸類成否定,對 jsx 卻是可以正確顯示在畫面上的值,務必要讓條件輸出成 boolean 來避免問題。

// ✅ 正確
{item.length > 0 && <List item={item} />}

// ❌ 錯誤
{item.length && <List item={item} />} // 得到的結果是 {0}

-----
10. 列表渲染

JSX 本質上就是 JavaScript + HTML 的混合語法,所有邏輯請交給 JS 處理,Vue.js 的 v-for 可以對應到 .map(),同樣要設定 key 屬性,其值必須唯一。

{items.map((item, index) => (
  <li key={index}>{item}</li>
))}

Vue 支援 JSX 的寫法,但可以直接使用使用 HTML attributes classfor