跳至主要内容

25 篇文章 含有標籤「vue」

檢視所有標籤

前端 i18n 入門教學與注意事項整理筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在現今的全球化應用中,網站或產品若希望觸及更多用戶,提供多語系支援幾乎是必須的功能。這就是所謂的國際化(Internationalization,簡稱 i18n),意即在程式設計階段預先做好結構設計,使系統能根據不同語言與地區的需求,自動載入對應的文案、格式與顯示方式。

本篇筆記將說明前端 i18n 的核心觀念、開發時常見的注意事項,以及如何透過實際程式碼實作一個簡單的多語系功能,協助你快速掌握前端 i18n 的基本功。


重點摘要:i18n 實作注意事項

  1. 避免硬編碼文字:所有顯示文字應抽離為 key-value 翻譯檔,便於日後維護與翻譯。
  2. 使用成熟 i18n 套件:例如 React 的 react-i18next、Vue 的 vue-i18n
  3. 結構化管理翻譯檔案:依功能模組分類翻譯內容,避免 key 混亂或重複。
  4. 支援變數插值與格式化:例如姓名、時間、數字等內容應透過參數傳遞給翻譯函數。
  5. 避免字串拼接組合句子:不同語言語序不同,拼接容易導致語意錯誤。
  6. 設計 UI 時預留文字空間:不同語言的字串長度可能差異很大。
  7. 處理 RTL 語言與排版:如阿拉伯語需設定 direction: rtl,必要時翻轉 UI。
  8. 提供語系切換機制與偵測:可從 navigator.language、URL、cookie 判斷語系。
  9. 設計 fallback 機制:若某語系未翻譯的 key,應自動 fallback 至預設語系。
  10. 翻譯流程建議自動化與工具化:搭配翻譯平台(如 Lokalise、Crowdin)管理翻譯流程與品質。

實作範例:使用 React + react-i18next 實現簡單的 i18n 功能

假設我們有一個需要支援中英文切換的 React 專案,以下將一步步實作基本功能。

步驟一:安裝相關套件

npm install i18next react-i18next i18next-browser-languagedetector

步驟二:建立翻譯檔案(放在 src/locales/

src/locales/en/translation.json

{
"greeting": "Hello, {{name}}!",
"home": {
"title": "Welcome to the homepage"
}
}

src/locales/zh/translation.json

{
"greeting": "哈囉,{{name}}!",
"home": {
"title": "歡迎來到首頁"
}
}

步驟三:初始化 i18n 設定(src/i18n.js)

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

import en from './locales/en/translation.json';
import zh from './locales/zh/translation.json';

i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
en: { translation: en },
zh: { translation: zh },
},
fallbackLng: 'en',
interpolation: {
escapeValue: false,
},
});

export default i18n;

步驟四:在應用入口引入 i18n 設定(例如 index.js)

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './i18n';

ReactDOM.render(<App />, document.getElementById('root'));

步驟五:在元件中使用翻譯

import React from 'react';
import { useTranslation } from 'react-i18next';

function HomePage() {
const { t, i18n } = useTranslation();

const changeLanguage = (lang) => {
i18n.changeLanguage(lang);
};

return (
<div>
<h1>{t('home.title')}</h1>
<p>{t('greeting', { name: 'KD' })}</p>
<button onClick={() => changeLanguage('en')}>English</button>
<button onClick={() => changeLanguage('zh')}>中文</button>
</div>
);
}

export default HomePage;

預期畫面輸出

使用者進入頁面,根據瀏覽器語言自動載入對應語系,或透過按鈕切換語言:

歡迎來到首頁
哈囉,KD!
[English] [中文]

總結

i18n 是每個想要「走向國際」的產品所必備的基礎建設之一。透過妥善設計的翻譯架構與工具整合,不僅能提升使用者體驗,也有助於日後擴展新市場與新語系。

建議開發者在專案初期就規劃好 i18n 架構,並搭配良好的團隊流程與翻譯管理工具,將繁瑣的翻譯作業系統化,避免日後重構的成本。

Vue ref 與 reactive 入門教學筆記 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

前言

在 Vue 3 中,refreactive 都是用來創建響應式資料的工具,但它們適用的情境略有不同。以下是清楚的比較與實際使用範例,幫助你理解什麼時候該用哪一個。


ref 使用情境

適用於:

  • 原始資料型別:如 NumberStringBooleanDate 等。
  • 你只需要追蹤單一值。
  • 當你需要某個變數傳遞到 <template> 或函式中。

語法:

import { ref } from 'vue'

const count = ref(0)
count.value++ // 需要用 .value 訪問或修改

範例:

<script setup>
import { ref } from 'vue'

const message = ref('Hello')
const count = ref(0)

const updateMessage = () => {
message.value = 'Hi Vue 3'
}
</script>

reactive 使用情境

適用於:

  • 物件或陣列:需要操作多個屬性的資料結構。
  • 多層巢狀資料或你希望所有屬性都具有響應性。
  • 表單資料、設定物件等複雜狀態管理。

語法:

import { reactive } from 'vue'

const user = reactive({
name: 'Daniel',
age: 30
})

user.age++ // 直接修改即可,無需 .value

範例:

<script setup>
import { reactive } from 'vue'

const form = reactive({
username: '',
email: '',
isSubscribed: false
})

const submitForm = () => {
console.log(form.username, form.email, form.isSubscribed)
}
</script>

⚠️ ref 包物件 vs reactive

雖然你也可以這樣用:

const user = ref({ name: 'Daniel', age: 30 })

但要存取時就需要:

user.value.name = 'John'

reactive 則可以直接操作:

user.name = 'John'

所以若你有物件資料,通常選擇 reactive 更直覺。


建議使用方式整理:

類型建議使用方式
單一變數(數字、字串)ref
多欄位表單資料reactive
陣列、物件、巢狀結構reactive
要被 watchcomputed 的原始值ref

Vue 正確新增或修改物件屬性入門教學筆記 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

前言

在 Vue 2(Options API)中,this.$set 是用來在響應式系統中正確新增或修改物件屬性的方法。這對於動態新增屬性或修改陣列的指定索引值特別有用。


語法

this.$set(target, propertyName/index, value)
  • target:要修改的對象或陣列
  • propertyName(物件)或 index(陣列)
  • value:要設的值

為什麼需要 this.$set

Vue 2 的 reactivity(響應式系統)使用 Object.defineProperty,它無法偵測到新加的屬性或是直接對陣列用索引來賦值。

例如,下面的程式碼是不會觸發畫面更新的:

this.someObj.newKey = 'value'      // 不會觸發更新
this.someArray[1] = 'changed' // 不會觸發更新

必須改成:

this.$set(this.someObj, 'newKey', 'value')      // 會觸發更新
this.$set(this.someArray, 1, 'changed') // 會觸發更新

範例一:對物件新增屬性

data() {
return {
user: {
name: '小明'
}
}
},
mounted() {
this.$set(this.user, 'age', 30);
}

這樣才能讓 user.age 成為響應式屬性,更新時畫面才會重新渲染。


範例二:修改陣列中的值

data() {
return {
items: ['a', 'b', 'c']
}
},
methods: {
updateItem() {
this.$set(this.items, 1, 'changed');
}
}

Vue 3 呢?

在 Vue 3 裡,因為 reactivity 系統改用 Proxy,所以可以直接新增或修改屬性,不需要 this.$set 了:

this.someObj.newKey = 'value'   // Vue 3 沒問題
this.someArray[1] = 'changed' // Vue 3 沒問題

如果你正在寫 Vue 2 的 Tic Tac Toe 遊戲,當你要動態更新棋盤的某個格子時,這樣做就是對的:

this.$set(this.board[row], col, 'X');

這樣才能確保畫面能即時更新。

Vue Router 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

Vue Router 是 Vue.js 官方提供的前端路由解決方案,專為構建單頁應用(SPA, Single Page Application)而設計。它讓我們可以根據網址變化動態切換畫面而不重新載入頁面,是開發 Vue 應用不可或缺的工具之一。

本文將介紹 Vue Router 的基本概念、安裝方式、核心語法,並透過簡單實作幫助我們快速入門。


一、什麼是前端路由

在傳統網頁架構中,網址的改變會導致瀏覽器重新向伺服器請求一個新的 HTML 頁面。但在 SPA 中,整個網站的內容是透過 JavaScript 管理畫面切換,網址改變時並不會重新載入整個頁面,而是由「前端路由器」來處理畫面更新。

Vue Router 就是 Vue 的前端路由器。


二、安裝 Vue Router

在 Vue 3 專案中,可以透過以下指令安裝 Vue Router:

npm install vue-router@4

安裝完成後,在 src/router/index.js(或 router.ts)中建立路由設定。


三、基本使用範例

1. 建立路由元件

建立幾個簡單的元件:

// src/views/Home.vue
<template>
<h1>首頁</h1>
</template>

// src/views/About.vue
<template>
<h1>關於我們</h1>
</template>

2. 設定路由

// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
import About from "../views/About.vue";

const routes = [
{ path: "/", component: Home },
{ path: "/about", component: About },
];

const router = createRouter({
history: createWebHistory(),
routes,
});

export default router;

3. 在 main.js 掛載路由

// src/main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";

createApp(App).use(router).mount("#app");

4. 使用 <router-view> 顯示頁面

<!-- src/App.vue -->
<template>
<div>
<h1>我的網站</h1>
<nav>
<router-link to="/">首頁</router-link>
|
<router-link to="/about">關於</router-link>
</nav>
<router-view></router-view>
</div>
</template>

<router-link> 是 Vue Router 提供的元件,用來建立不重新載入的內部連結。<router-view> 則是畫面會顯示對應元件的插槽。


四、動態路由參數

可使用 :id 的方式定義動態參數:

// router/index.js
import User from "../views/User.vue";

const routes = [{ path: "/user/:id", component: User }];

在元件中取得參數:

<!-- views/User.vue -->
<template>
<div>使用者 ID:{{ $route.params.id }}</div>
</template>

五、巢狀路由

當需要在某個頁面內部再切換子頁面時,可使用巢狀路由。

// router/index.js
import Dashboard from "../views/Dashboard.vue";
import Profile from "../views/Profile.vue";
import Settings from "../views/Settings.vue";

const routes = [
{
path: "/dashboard",
component: Dashboard,
children: [
{ path: "profile", component: Profile },
{ path: "settings", component: Settings },
],
},
];

Dashboard.vue 中放置 <router-view> 來呈現子路由內容:

<template>
<div>
<h2>儀表板</h2>
<router-link to="/dashboard/profile">個人資料</router-link>
<router-link to="/dashboard/settings">設定</router-link>
<router-view></router-view>
</div>
</template>

六、導覽守衛(Navigation Guard)

我們可以用來保護某些頁面,例如使用者未登入不得進入:

// router/index.js
const router = createRouter({...})

router.beforeEach((to, from, next) => {
const isLoggedIn = false // 假設未登入
if (to.path === '/dashboard' && !isLoggedIn) {
next('/') // 導回首頁
} else {
next()
}
})

七、路由模式(Hash vs History)

Vue Router 支援兩種模式:

  1. Hash 模式(預設): 網址含 #,如 /#/about,適用於靜態伺服器
  2. History 模式(推薦): 網址乾淨,需伺服器配合設定

選擇 History 模式時,需設定:

createRouter({
history: createWebHistory(),
routes,
});

若使用 Vite / Vue CLI,也需額外設定伺服器的 404 fallback。


八、程式化導航

我們也可以在程式中使用 $router 導覽:

this.$router.push("/about");

或者使用命名路由與參數:

this.$router.push({ name: "user", params: { id: 123 } });

九、總結

概念說明
<router-view>顯示當前路由對應的元件
<router-link>建立內部連結
$route取得當前路由資訊(如參數)
$router控制路由導航的方法
動態路由使用 :id 定義參數
巢狀路由路由中可包含子路由
導覽守衛控制進入頁面的權限
模式hashhistory 模式

結語

Vue Router 是構建 Vue SPA 必備的工具之一,掌握它能幫助我們設計更有結構、可導航的前端應用。在進階應用中,Vue Router 還支援命名路由、懶加載、滾動行為、過渡動畫等功能。

如果我們有興趣了解 Vue Router 與後端整合、登入驗證、或 Nuxt.js 中的路由自動生成,歡迎再提出更進一步的需求。

Element Plus 介紹入門教學筆記 | 學習筆記

· 閱讀時間約 6 分鐘
kdchang

前言

在現代前端開發中,UI 框架對於提升開發效率、提升用戶體驗具有至關重要的作用。Element Plus 是一個基於 Vue 3 的開源 UI 框架,它提供了豐富的組件,並且與 Vue 3 的 Composition API 完美兼容,能幫助開發者快速構建高質量的前端應用。

在筆記中,我們將使用 Vue 3 的 setup() 語法來講解如何在專案中使用 Element Plus,並展示如何搭配 Vue 3 的新特性來實現更加清晰和模組化的開發模式。

重點摘要

  • Element Plus:基於 Vue 3 的 UI 組件庫,提供了豐富的 UI 元件,支持 Composition API 和 TypeScript。
  • Vue 3 Setup:使用 Vue 3 的 Composition API 和 setup() 來構建組件邏輯,並將 UI 與邏輯分開。
  • 組件設置:展示如何在 setup() 中引入並使用 Element Plus 組件。
  • 全域配置:設置 Element Plus 的全域屬性,例如默認主題或組件大小。
  • 實際範例:通過簡單的範例,演示如何在 Vue 3 中使用 Element Plus 進行開發。

安裝與設置

安裝 Element Plus

在 Vue 3 專案中使用 Element Plus,首先需要安裝它。假設你的專案已經配置了 Vue 3,則可以通過以下命令安裝 Element Plus。

npm install element-plus --save

或者使用 yarn

yarn add element-plus

設置 Vue 3 與 Element Plus

在 Vue 3 的專案中,通過 setup() 語法來設置 Element Plus,首先需要在 main.jsmain.ts 中引入 Element Plus 並配置樣式:

import { createApp } from 'vue';
import App from './App.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';

const app = createApp(App);
app.use(ElementPlus);
app.mount('#app');

這樣,Element Plus 就成功集成到你的 Vue 3 專案中,接下來可以開始使用其組件。

使用 Vue 3 Setup 語法進行開發

按鈕 (Button) 組件

在 Vue 3 中,setup() 是一個組件的初始化函數,所有的組件邏輯應該放在這個函數中。以下是如何在 setup() 中使用 Element Plus 的按鈕組件。

<template>
<el-button type="primary">主要按鈕</el-button>
<el-button type="success">成功按鈕</el-button>
<el-button type="danger">危險按鈕</el-button>
</template>

<script setup>
import { ElButton } from 'element-plus';
</script>

在此範例中,使用 import 引入 ElButton 組件,並在模板中直接使用。這樣,我們就能夠根據需要渲染出不同樣式的按鈕。Element Plus 的組件會自動按照設置渲染出對應的 UI。

對話框 (Dialog) 組件

Element Plus 提供了功能強大的對話框組件,能夠輕鬆實現彈窗效果。下面是如何在 setup() 中實現一個顯示對話框的功能:

<template>
<el-button @click="openDialog">顯示對話框</el-button>
<el-dialog :visible.sync="dialogVisible" title="對話框標題">
<p>這是一個對話框示例。</p>
</el-dialog>
</template>

<script setup>
import { ref } from 'vue';
import { ElButton, ElDialog } from 'element-plus';

const dialogVisible = ref(false);

const openDialog = () => {
dialogVisible.value = true;
};
</script>

在這個範例中,使用 ref() 創建了 dialogVisible 這個響應式變數,並且通過按鈕的點擊事件來控制對話框的顯示和隱藏。sync 修飾符用來將對話框的顯示狀態與 dialogVisible 變數同步。

表單 (Form) 組件

Element Plus 的表單組件支持高效的表單驗證,以下範例展示如何在 setup() 中使用表單組件進行資料提交。

<template>
<el-form :model="form" ref="formRef" label-width="120px">
<el-form-item
label="名稱"
prop="name"
:rules="[{ required: true, message: '請輸入名稱', trigger: 'blur' }]"
>
<el-input v-model="form.name" placeholder="請輸入名稱"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
</el-form-item>
</el-form>
</template>

<script setup>
import { ref } from 'vue';
import { ElButton, ElForm, ElFormItem, ElInput } from 'element-plus';

const form = ref({
name: '',
});

const submitForm = () => {
// 表單提交處理
console.log(form.value.name);
};
</script>

在這個範例中,使用 ref() 創建表單的資料模型,並且為名稱欄位設置了必填的驗證規則。當點擊提交按鈕時,將會輸出表單中的名稱欄位值。

表格 (Table) 組件

Element Plus 的表格組件支持展示大量數據,並且提供排序、過濾等功能。以下是如何使用 setup() 語法來渲染表格:

<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column label="姓名" prop="name"></el-table-column>
<el-table-column label="年齡" prop="age"></el-table-column>
<el-table-column label="地址" prop="address"></el-table-column>
</el-table>
</template>

<script setup>
import { ref } from 'vue';
import { ElTable, ElTableColumn } from 'element-plus';

const tableData = ref([
{ name: '王小明', age: 25, address: '台北市' },
{ name: '李大華', age: 30, address: '高雄市' },
{ name: '張三', age: 28, address: '台中市' },
]);
</script>

這個範例展示了如何使用 ref() 創建表格的數據源,並使用 ElTableElTableColumn 來渲染表格。

全域配置

Element Plus 提供了全域配置的能力,允許你在項目中設置統一的組件配置。例如,設置所有按鈕的默認大小或主題色彩等。

import { createApp } from 'vue';
import App from './App.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';

const app = createApp(App);

app.use(ElementPlus, {
size: 'small', // 設定所有組件的大小為 small
zIndex: 3000, // 設定 z-index
});

app.mount('#app');

這樣設置之後,你的所有組件將會默認使用小號尺寸,並且對話框等組件的 z-index 也會被統一設置。

總結

Element Plus 是一個強大且易於使用的 UI 框架,與 Vue 3 的 Composition API 完美集成。通過 setup() 語法,開發者能夠更加簡潔且模組化地編寫組件邏輯,從而提升開發效率。無論是常見的按鈕、對話框,還是複雜的表格、表單,Element Plus 都能提供強大的支持。希望本文能幫助你快速上手 Element Plus,並利用它來構建高效、現代化的前端應用。

Vuex 入門教學筆記:集中式狀態管理實作入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在 Vue.js 開發中,當應用程式變得複雜,元件之間需要共享的狀態越來越多時,僅靠 props 與 events 傳遞資料會變得混亂與難以維護。這時,我們就需要一個集中式的狀態管理方案,而 Vuex 正是官方為 Vue 提供的解決方案。

Vuex 是一個專為 Vue 應用開發的狀態管理模式。它將應用中所有的狀態集中管理,並以可預測的方式更新,便於追蹤與維護。


一、Vuex 是什麼?

Vuex 基於 Flux 架構 設計,核心概念如下:

  • State:集中管理的資料來源(全域狀態)
  • Getter:從 state 派生出來的資料(類似 computed)
  • Mutation:唯一可以同步改變 state 的方法
  • Action:處理非同步操作並提交 mutation
  • Module:將 store 拆分為模組化結構

二、安裝與設定 Vuex

以 Vue 3 專案為例,先安裝 Vuex:

npm install vuex@4

建立 store

// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
state() {
return {
count: 0,
};
},
getters: {
doubleCount(state) {
return state.count * 2;
},
},
mutations: {
increment(state) {
state.count++;
},
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit("increment");
}, 1000);
},
},
});

export default store;

三、在 Vue 中註冊 Vuex

// main.js
import { createApp } from "vue";
import App from "./App.vue";
import store from "./store";

createApp(App).use(store).mount("#app");

四、在元件中使用 Vuex

1. 讀取 State 和 Getter

<template>
<div>
<p>Count:{{ count }}</p>
<p>Double:{{ doubleCount }}</p>
</div>
</template>

<script>
import { computed } from "vue";
import { useStore } from "vuex";

export default {
setup() {
const store = useStore();
const count = computed(() => store.state.count);
const doubleCount = computed(() => store.getters.doubleCount);
return { count, doubleCount };
},
};
</script>

2. 呼叫 Mutation 和 Action

<template>
<button @click="increment">+1</button>
<button @click="incrementAsync">+1(非同步)</button>
</template>

<script>
import { useStore } from "vuex";

export default {
setup() {
const store = useStore();
const increment = () => store.commit("increment");
const incrementAsync = () => store.dispatch("incrementAsync");
return { increment, incrementAsync };
},
};
</script>

五、模組化 Vuex Store

當我們的應用變大,state 增加時,可將 store 拆分成多個模組。

// src/store/modules/counter.js
export default {
namespaced: true,
state: () => ({
count: 0,
}),
mutations: {
increment(state) {
state.count++;
},
},
};
// src/store/index.js
import { createStore } from "vuex";
import counter from "./modules/counter";

export default createStore({
modules: {
counter,
},
});

在元件中使用時要記得模組的命名空間:

store.commit("counter/increment");

六、Vuex 與 Composition API 的搭配

Vuex 4 支援 Vue 3 的 Composition API,我們可以透過 useStore() 搭配 computed() 來存取或操作資料。這樣的使用方式更模組化,也能更輕鬆撰寫邏輯可重用的自定義 hooks。

// composables/useCounter.js
import { computed } from "vue";
import { useStore } from "vuex";

export default function useCounter() {
const store = useStore();
const count = computed(() => store.state.counter.count);
const increment = () => store.commit("counter/increment");
return { count, increment };
}

七、Vuex 與非同步操作的實務應用

Vuex 的 Action 適合處理 API 呼叫,例如取得後端資料:

// store/modules/todos.js
export default {
namespaced: true,
state: () => ({
list: [],
loading: false,
}),
mutations: {
setLoading(state, flag) {
state.loading = flag;
},
setTodos(state, todos) {
state.list = todos;
},
},
actions: {
async fetchTodos({ commit }) {
commit("setLoading", true);
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
const data = await res.json();
commit("setTodos", data.slice(0, 5));
commit("setLoading", false);
},
},
};

八、Vuex 的限制與未來

Vuex 提供完整的狀態追蹤與結構化設計,適合大型應用。不過,它的學習曲線略高,對於小型專案可能顯得冗長。Vue 團隊也在 Vue 3 推出後推薦使用 Pinia 作為新的官方狀態管理方案,擁有更輕量的語法與更佳的 TypeScript 支援。

但 Vuex 在大型專案、多人協作、需要嚴格管理資料流程的場景下仍然非常實用與穩定。


九、總結與建議

功能說明
state全域共享狀態資料
getters從 state 衍生計算的資料
mutations同步更新狀態的唯一方法
actions處理非同步並提交 mutation
modules將 store 拆分管理
Composition API搭配 useStore() 模組化使用

建議我們在小型應用中可以先用 propsemit 傳遞資料,等到資料流變複雜或頁面之間需頻繁共享狀態時,再引入 Vuex 管理。當然,若我們正開發大型後台系統、電子商務網站,Vuex 的集中式結構能大大提升可維護性與擴展性。

如欲進一步探索,建議查看 Vuex 官方文件、或試著實作一個待辦清單管理應用,實踐 Vuex 中的完整生命週期與流程。

Vue i18n 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

1. Vue I18n 簡介

Vue i18n 是 Vue.js 的國際化(Internationalization)解決方案,可用於管理應用程式的多語系內容。當開發需要支援不同語言的應用時,Vue i18n 提供了一個高效的方式來處理翻譯、數字格式、日期格式等。

1.1 為何使用 Vue I18n?

  • 動態切換語言:讓使用者能夠即時更改語言。
  • 簡化多語系管理:集中化翻譯內容,方便維護。
  • 格式化支援:內建日期、時間、數字等格式化功能。

2. 安裝 Vue I18n

Vue I18n 可透過 npm 安裝,適用於 Vue 3 應用。

npm install vue-i18n

3. 設定 Vue I18n

在 Vue 3 中,可以使用 createI18n 來設定 Vue I18n:

import { createApp } from 'vue';
import { createI18n } from 'vue-i18n';
import App from './App.vue';

const messages = {
en: {
welcome: 'Welcome to Vue I18n!'
},
zh: {
welcome: '歡迎使用 Vue I18n!'
}
};

const i18n = createI18n({
locale: 'en', // 預設語言
fallbackLocale: 'zh', // 備用語言
messages
});

const app = createApp(App);
app.use(i18n);
app.mount('#app');

4. 在組件中使用 Vue I18n

4.1 使用 $t 方法

在 Vue 組件中,可以透過 $t 方法來取得翻譯內容。

<template>
<h1>{{ $t('welcome') }}</h1>
</template>

4.2 切換語言

可以透過 this.$i18n.locale 來更改語言:

<template>
<div>
<button @click="changeLanguage('en')">English</button>
<button @click="changeLanguage('zh')">中文</button>
<p>{{ $t('welcome') }}</p>
</div>
</template>

<script>
export default {
methods: {
changeLanguage(lang) {
this.$i18n.locale = lang;
}
}
};
</script>

5. 使用外部 JSON 檔案管理翻譯內容

當專案的翻譯內容較多時,建議將翻譯字串拆分成獨立的 JSON 檔案。

建立語言檔案:

locales/en.json

{
"welcome": "Welcome to Vue I18n!"
}

locales/zh.json

{
"welcome": "歡迎使用 Vue I18n!"
}

修改 i18n 設定:

import { createI18n } from 'vue-i18n';
import en from './locales/en.json';
import zh from './locales/zh.json';

const i18n = createI18n({
locale: 'en',
fallbackLocale: 'zh',
messages: { en, zh }
});

6. 插值與變數

Vue I18n 支援在翻譯字串中插入變數。

const messages = {
en: {
greeting: 'Hello, {name}!'
},
zh: {
greeting: '你好, {name}!'
}
};

在組件中使用:

<p>{{ $t('greeting', { name: 'John' }) }}</p>

7. 訊息格式化與日期、數字處理

Vue I18n 內建 numberFormatsdatetimeFormats,可用於格式化數字與日期。

const i18n = createI18n({
locale: 'en',
fallbackLocale: 'zh',
messages,
numberFormats: {
en: {
currency: {
style: 'currency', currency: 'USD'
}
},
zh: {
currency: {
style: 'currency', currency: 'TWD'
}
}
}
});

在組件中使用:

<p>{{ $n(1000, 'currency') }}</p>

8. 延遲載入翻譯(Lazy Loading)

當應用程式的語言檔案過多時,建議使用動態載入(Lazy Loading)來優化效能。

import { createI18n } from 'vue-i18n';

const i18n = createI18n({
locale: 'en',
fallbackLocale: 'zh',
messages: {}
});

async function loadLocaleMessages(locale) {
const messages = await import(`./locales/${locale}.json`);
i18n.global.setLocaleMessage(locale, messages.default);
i18n.global.locale = locale;
}

9. 結論

Vue I18n 是 Vue.js 中強大的國際化解決方案,透過 $t 方法、插值變數、外部 JSON 檔案管理,以及數字與日期格式化功能,可以讓開發者輕鬆實作多語系應用。本篇筆記介紹了 Vue I18n 的基本使用方式,進一步學習可研究動態載入、多語系 SEO 以及與 Vue Router 的結合。

Vue v-cloak 入門教學筆記 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

前言

v-cloak 在 Vue 3 依然存在,作用與 Vue 2 相同:在 Vue 實例或組件掛載完成之前,避免模板中的插值語法(如 {{ message }})閃爍顯示給使用者。搭配 CSS 可在 Vue 掛載完成前隱藏元素,增進首屏體驗。


重點摘要

  • v-cloak 為特殊屬性,Vue 3 掛載完成會自動移除。
  • 必須搭配 CSS [v-cloak] { display: none; } 讓元素在掛載前隱藏。
  • 在 Vue 3,掛載方式改用 createApp
  • 可用於根組件或子組件的根元素。
  • 不會與 v-ifv-show 衝突。

Vue 3 實際範例

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<title>Vue 3 v-cloak 範例</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>{{ message }}</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
message: 'Vue 3 掛載完成!',
};
},
}).mount('#app');
</script>
</body>
</html>

Vue 3 單文件組件 (SFC) 範例

<template>
<div v-cloak>
<p>{{ message }}</p>
</div>
</template>

<script>
export default {
data() {
return {
message: '這是 Vue 3 SFC 範例',
};
},
};
</script>

<style>
[v-cloak] {
display: none;
}
</style>

總結

  • v-cloak 在 Vue 3 的核心概念不變,仍用於避免模板閃爍。
  • 需配合 CSS [v-cloak] 隱藏元素,等 Vue 掛載後自動移除。
  • 掛載方式使用 createApp,搭配 .mount()
  • 可應用於純 HTML 或組件模板中。

Vue pinia 保持 reactive 入門教學筆記 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

保持 reactive 寫法:


1️. 用 computed

const todos = computed(() => todoStore.todos)

優點

  • 保持 reactive
  • 簡單直接
  • 在 template 裡 todos 可以直接使用

缺點

  • 如果只是讀值,其實有點多餘

建議用於:在 template 需要用 v-for="todo in todos" 這種情況下。


2️. 直接使用 todoStore.todos 在 template

不額外宣告變數,直接寫:

<ul>
<li v-for="todo in todoStore.todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>

優點

  • 直接讀取 store,最簡單
  • 不需要 computed

缺點

  • 如果在 script 多處使用 todos,每次都要寫 todoStore.todos

建議用於:只在 template 需要使用 todos 的情況。


3️. 使用 storeToRefs

這是 Pinia 官方推薦的方式,可以一次把 state 解構成 reactive ref:

import { storeToRefs } from 'pinia'

const todoStore = useTodoStore()
const { todos } = storeToRefs(todoStore)

優點

  • todos 是 reactive
  • 取值時不會失去 reactive
  • 適合同時取多個 state(例如 const { todos, count } = storeToRefs(todoStore)

缺點

  • 需要額外 import storeToRefs
  • 初學者可能不熟悉

建議用於:component 中需要多個 state 且偏好解構寫法時。


寫法比較

寫法是否 reactive適用場景
computed 包一層script 內多次使用
直接 todoStore.todos只在 template 使用
storeToRefs多個 state 解構需要時

建議

  • 如果只需要一個 state:computed 或直接用 todoStore.todos
  • 如果需要多個 state:storeToRefs

範例(使用 storeToRefs

<script setup>
import { useTodoStore } from '../stores/todoStore'
import { storeToRefs } from 'pinia'

const todoStore = useTodoStore()
const { todos } = storeToRefs(todoStore)

function addTodo() { ... }
function removeTodo(id) { ... }
function toggleComplete(id) { ... }
</script>

template 裡直接使用 todos,效果與 computed 相同。


總結
三種寫法都可行,主要差異在語法風格與使用場景。若不想意外解構成非 reactive 值,使用 storeToRefs 是最安全的。

Vue nextTick 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在使用 Vue 進行開發時,常常會遇到「資料已經更新,但畫面上的 DOM 尚未更新完成」的情況。這是因為 Vue 採用了 非同步更新策略 (asynchronous update strategy),它會先收集多次資料變動,再一次性地進行 DOM 更新,以提升效能。但這也意味著,如果我們想在資料變更後立即操作更新後的 DOM,往往會發現 DOM 還停留在舊的狀態。

為了解決這個問題,Vue 提供了 nextTick 這個 API。它允許我們在 DOM 更新完成後,執行特定的 callback function 或程式碼,確保操作的 DOM 是最新的。本文將介紹 nextTick 的基本概念、使用情境與實際範例,幫助你掌握這個常用的技巧。


重點摘要

  • Vue 更新策略

    • Vue 採用「批次非同步更新」,會延遲 DOM 實際渲染,等所有同步程式碼執行後再統一更新。
    • 好處:避免重複渲染、提升效能。
    • 挑戰:在資料變更後立刻訪問 DOM,可能還是舊狀態。
  • nextTick 的作用

    • 確保在下一次 DOM 更新循環結束後再執行指定 callback。

    • 語法:

      import { nextTick } from 'vue';

      nextTick(() => {
      // DOM 已更新完成
      });
  • 常見使用情境

    1. 操作最新 DOM 元素:例如要取得更新後元素的高度、寬度或位置。
    2. 與第三方函式庫整合:某些 UI 套件需要等 DOM 更新後再初始化。
    3. 強制等待渲染完成再執行程式:避免非同步更新造成的狀態錯誤。
  • 可搭配 async/await

    • nextTick 也能回傳 Promise,讓程式碼更直觀。
    • 適合在 setupmethods 中使用 await nextTick()

實際範例

範例一:取得更新後的 DOM

<template>
<div>
<button @click="addItem">新增項目</button>
<ul ref="list">
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
<p>目前清單高度:{{ listHeight }}px</p>
</div>
</template>

<script setup>
import { ref, nextTick } from 'vue';

const items = ref(['項目 1', '項目 2']);
const list = ref(null);
const listHeight = ref(0);

function addItem() {
items.value.push(`項目 ${items.value.length + 1}`);
// 此時 DOM 尚未更新,listHeight 還是舊的
nextTick(() => {
listHeight.value = list.value.offsetHeight;
});
}
</script>

說明:

  • 點擊按鈕時,會往清單新增項目。
  • items 更新後,Vue 還沒立刻修改 DOM。
  • 使用 nextTick,等到 DOM 重新渲染後,才能正確取得最新高度。

範例二:搭配 async/await 使用

<template>
<div>
<button @click="toggleBox">切換方塊</button>
<div v-if="show" ref="box" class="box"></div>
</div>
</template>

<script setup>
import { ref, nextTick } from 'vue';

const show = ref(false);
const box = ref(null);

async function toggleBox() {
show.value = !show.value;
await nextTick();
if (box.value) {
console.log('方塊尺寸:', box.value.offsetWidth, box.value.offsetHeight);
}
}
</script>

<style scoped>
.box {
width: 200px;
height: 100px;
background-color: lightblue;
}
</style>

說明:

  • 使用 await nextTick(),讓程式碼讀起來更直覺。
  • v-if 切換後,等 DOM 更新完成再訪問 box,避免 null 或舊的狀態。

範例三:計算新增元素更新後高度

<template>
<div>
<button @click="addItem">新增項目</button>
<ul ref="list">
<li v-for="n in items" :key="n">項目 {{ n }}</li>
</ul>
</div>
</template>

<script setup>
import { ref, nextTick } from 'vue';

const items = ref([1, 2]);
const list = ref(null);

async function addItem() {
items.value.push(items.value.length + 1);

console.log('立即讀取高度:', list.value.offsetHeight);

await nextTick();
console.log('nextTick 後高度:', list.value.offsetHeight);
}
</script>

說明:

  • 立即讀取高度 → 還是舊值
  • nextTick 後高度 → 才是更新後的 DOM 高度

範例四:與第三方套件整合

假設要在 Vue 中使用某個需要 DOM 初始化的套件(例如輪播圖 Swiper):

<template>
<div>
<button @click="initSlider">初始化輪播</button>
<div ref="slider">
<div class="slide" v-for="n in 3" :key="n">Slide {{ n }}</div>
</div>
</div>
</template>

<script setup>
import { ref, nextTick } from 'vue';
// 假設已安裝 Swiper
import Swiper from 'swiper';

const slider = ref(null);
let swiperInstance = null;

async function initSlider() {
await nextTick();
if (slider.value && !swiperInstance) {
swiperInstance = new Swiper(slider.value, {
loop: true,
autoplay: true,
});
}
}
</script>

說明:

  • 先確保 DOM 結構已經渲染完成,才交給第三方套件初始化。
  • 若省略 nextTick,可能會因為 DOM 還沒出現而報錯。

總結

  • Vue 採用非同步批次更新策略,能提升效能,但會導致資料更新後 DOM 還未立即反映。
  • nextTick 可用來等待 DOM 更新完成後再執行特定程式。
  • 使用情境包括:取得最新 DOM、整合第三方套件、避免狀態錯誤。
  • 可以透過 callback functionasync/await 使用,後者更直覺。
  • 熟悉 nextTick 能幫助我們更精確掌握 Vue 的渲染時機,寫出更穩定的互動邏輯。