跳至主要内容

78 篇文章 含有標籤「frontend」

檢視所有標籤

Nuxt.js 入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

Nuxt.js 是一個基於 Vue.js 的漸進式框架,專為構建伺服器端渲染(SSR)和靜態站點生成(SSG)應用程式而設計。它提供開發者一個強大的開發體驗,並簡化 Vue.js 應用的架構與設定,適合 SEO 優化、效能最佳化以及提升開發效率。

Nuxt.js 的核心特性包括:

  1. 伺服器端渲染(SSR):增強 SEO 並提升初始載入速度。
  2. 靜態站點生成(SSG):透過預先生成 HTML 提供更快的載入時間。
  3. 自動路由:基於 pages 目錄的檔案自動建立對應的路由,無需額外配置 Vue Router。
  4. 模組系統:支援大量 Nuxt 模組(如 TailwindCSS、PWA、Auth 等)來快速擴展功能。
  5. 組態簡單:預設優化 Vue.js 應用的結構與設定,減少繁瑣的配置工作。
  6. 組件自動載入:Nuxt 可自動載入 components 目錄內的 Vue 組件,減少 import 的需求。

二、安裝與初始化 Nuxt.js

1. 使用 Nuxt CLI 安裝(推薦方式)

Nuxt 提供官方 CLI 工具 nuxi 來建立新專案。

npx nuxi init my-nuxt-app
cd my-nuxt-app
npm install

上述指令會自動建立一個 my-nuxt-app 專案,並下載 Nuxt 相關相依套件。

2. 使用 create-nuxt-app 安裝(舊版方式)

如果要使用較舊的安裝方式,也可以透過 create-nuxt-app 指令來建立專案:

npx create-nuxt-app my-nuxt-app

此方法會提供互動式選單,讓開發者選擇 UI 框架(TailwindCSS、Bootstrap)、插件(Axios、PWA)以及 Nuxt 模式(SSR 或 SSG)。

3. 啟動開發伺服器

安裝完成後,可以執行以下指令來啟動開發環境:

npm run dev

預設會啟動本機伺服器 http://localhost:3000,可在瀏覽器中打開檢視。


三、專案結構

Nuxt.js 採用約定式(Convention over Configuration)架構,專案目錄結構如下:

my-nuxt-app/
│── assets/ # 未編譯的靜態資源,如 CSS、圖片
│── components/ # Vue 組件(自動載入)
│── layouts/ # 頁面佈局
│── pages/ # 自動建立的路由頁面
│── plugins/ # Nuxt 插件,如 Vue 插件或第三方庫
│── public/ # 靜態資源,可直接透過 URL 存取
│── server/ # 伺服器端 API(Nuxt 3)
│── store/ # Vuex 狀態管理(Nuxt 2,Nuxt 3 改用 `pinia`)
│── nuxt.config.ts # Nuxt 設定檔
│── package.json # npm 套件設定

四、路由與頁面

1. 自動建立路由

Nuxt.js 會根據 pages/ 目錄內的 Vue 檔案自動產生對應的路由。例如,在 pages/index.vue 建立首頁:

<template>
<div>
<h1>歡迎來到 Nuxt.js</h1>
</div>
</template>

若在 pages/about.vue 建立新的 Vue 檔案,則 http://localhost:3000/about 會自動對應到該頁面。

2. 動態路由

可以使用 _ 命名的方式建立動態路由。例如,在 pages/blog/_id.vue

<template>
<div>
<h1>文章 ID: {{ route.params.id }}</h1>
</div>
</template>

<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
</script>

訪問 http://localhost:3000/blog/123,頁面將顯示 文章 ID: 123


五、Nuxt 組件與佈局

1. 自動載入組件

components/ 內的 Vue 檔案會自動載入,例如建立 components/Navbar.vue

<template>
<nav class="bg-blue-500 p-4 text-white">
<h1>網站導覽列</h1>
</nav>
</template>

然後在 pages/index.vue 內直接使用 <Navbar />,無需 import

<template>
<div>
<Navbar />
<h1>首頁內容</h1>
</div>
</template>

2. 佈局(Layouts)

佈局是共享的頁面結構,可在 layouts/default.vue 內定義:

<template>
<div>
<Navbar />
<slot />
</div>
</template>

所有 pages/ 內的頁面會自動套用 default.vue 佈局。


六、Nuxt 伺服器端 API(Nuxt 3)

Nuxt 3 內建簡單的 API 伺服器,可在 server/api/hello.ts 新增 API:

export default defineEventHandler((event) => {
return { message: "Hello from Nuxt API" };
});

這樣就可以透過 http://localhost:3000/api/hello 訪問該 API。


七、Nuxt 資料獲取

1. useFetch() 獲取 API 資料

Nuxt 3 提供 useFetch 來處理 API 讀取,例如:

<template>
<div>
<h1>{{ data.message }}</h1>
</div>
</template>

<script setup>
const { data } = useFetch('/api/hello');
</script>

這會自動調用 server/api/hello.ts 並顯示回應內容。


八、部署 Nuxt 應用

1. 生成靜態站點

若要將 Nuxt 部署為靜態網站,可執行:

npm run build
npm run generate

這會在 dist/ 目錄內產生靜態 HTML 檔案,可直接部署到 Netlify 或 Vercel。

2. 部署至 Vercel

使用 Vercel CLI 部署:

npm install -g vercel
vercel

即可快速部署 Nuxt 應用。


九、結語

Nuxt.js 提供強大的功能來簡化 Vue.js 開發,透過自動路由、組件自動載入、伺服器 API 以及資料獲取等功能,大幅提升開發效率。對於需要 SEO 優化或靜態站點的專案而言,Nuxt 是一個非常適合的選擇。

Emotion CSS 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

1. Emotion CSS 簡介

Emotion 是一款強大的 CSS-in-JS 解決方案,提供高效能且靈活的樣式管理方式,適用於 React 應用。Emotion 支援兩種主要的使用方式:

  1. CSS Prop(使用 JSX 內嵌樣式)
  2. styled API(使用 styled 函式創建元件)

2. 安裝 Emotion

使用 npm 或 yarn 安裝 Emotion 核心套件:

npm install @emotion/react @emotion/styled

如果專案使用 Babel,建議安裝 Emotion 的 Babel 插件來提高效能:

npm install --save-dev @emotion/babel-plugin

並在 .babelrc 中添加:

{
"plugins": ["@emotion"]
}

3. CSS Prop 用法

CSS Prop 允許你直接在 JSX 中定義 CSS 樣式。

基本範例

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const style = css`
background-color: lightblue;
padding: 20px;
border-radius: 8px;
`;

function App() {
return <div css={style}>Hello, Emotion!</div>;
}

export default App;

傳遞 props 變更樣式

const dynamicStyle = (color) => css`
background-color: ${color};
padding: 20px;
border-radius: 8px;
`;

function App({ color = 'lightblue' }) {
return <div css={dynamicStyle(color)}>Dynamic Emotion</div>;
}

4. styled API 用法

styled API 讓我們能夠創建具備 CSS 樣式的 React 元件。

基本範例

import styled from '@emotion/styled';

const Button = styled.button`
background-color: #3498db;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #2980b9;
}
`;

function App() {
return <Button>Click Me</Button>;
}

export default App;

傳遞 props 變更樣式

const Button = styled.button`
background-color: ${(props) => props.bg || '#3498db'};
color: ${(props) => props.color || 'white'};
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: ${(props) => (props.bg ? darken(0.1, props.bg) : '#2980b9')};
}
`;

function App() {
return <Button bg="tomato">Custom Button</Button>;
}

5. 全域樣式與 Theme

使用 Global 樣式

import { Global, css } from '@emotion/react';

const globalStyles = css`
body {
margin: 0;
font-family: Arial, sans-serif;
}
`;

function App() {
return (
<>
<Global styles={globalStyles} />
<div>Hello, Global Emotion!</div>
</>
);
}

使用 Theme

import { ThemeProvider } from '@emotion/react';
import styled from '@emotion/styled';

const theme = {
colors: {
primary: '#3498db',
secondary: '#2ecc71'
}
};

const ThemedButton = styled.button`
background-color: ${(props) => props.theme.colors.primary};
color: white;
padding: 10px 20px;
border-radius: 5px;
`;

function App() {
return (
<ThemeProvider theme={theme}>
<ThemedButton>Themed Button</ThemedButton>
</ThemeProvider>
);
}

6. 總結

Emotion 是一款靈活且高效的 CSS-in-JS 解決方案,適用於 React 應用。它提供 css prop 以及 styled API 來幫助開發者管理樣式,並支援全域樣式與主題設定。這篇教學筆記涵蓋了基礎概念與常見用法,希望對你的開發過程有所幫助。

Tailwind CSS 入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

Tailwind CSS 是一款以「工具類別優先」(Utility-First)為核心設計理念的 CSS 框架,與傳統的 CSS 框架(如 Bootstrap、Bulma)不同,它不提供預設的 UI 元件,而是提供大量的樣式工具類別,讓開發者可以快速組合來建構 UI,而不需要額外撰寫自訂 CSS。

相較於傳統 CSS 框架,Tailwind CSS 具有以下幾個主要優勢:

  1. 開發效率高:只需使用類別組合即可完成設計,無需撰寫額外 CSS。
  2. 高度可客製化:可以透過設定檔調整顏色、字型、間距等設計。
  3. 內建響應式支援:透過 sm: md: lg: xl: 等前綴輕鬆定義不同裝置的樣式。
  4. 一致性強:開發團隊可以統一使用 Tailwind CSS 定義的設計樣式,減少 UI 風格不一致的問題。
  5. 效能優化:可透過 PurgeCSS 移除未使用的樣式,確保最小化 CSS 檔案大小,提高網站載入速度。

二、安裝 Tailwind CSS

Tailwind CSS 可以透過多種方式安裝,以下介紹幾種常見的方法:

1. 使用 CDN(適合快速測試)

如果只是想快速體驗 Tailwind CSS,可以直接在 HTML 檔案中加入以下 <link>

<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.0.0/dist/tailwind.min.css" rel="stylesheet">

這種方式適合小型專案或測試 Tailwind CSS 的功能,但不適合正式開發,因為無法使用客製化設定。

2. 透過 npm 安裝(推薦方式)

在正式開發環境中,建議使用 npm 或 yarn 來安裝 Tailwind CSS,以便進行客製化設定。

安裝 Tailwind CSS

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

執行上述指令後,會在專案根目錄中產生 tailwind.config.js 設定檔,可用來調整 Tailwind 的預設樣式。

設定 Tailwind CSS

開啟 tailwind.config.js,找到 content 設定,確保 Tailwind 只處理專案內的相關檔案,例如:

module.exports = {
content: ["./src/**/*.{html,js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};

這樣 Tailwind CSS 就只會掃描 src 資料夾內的 HTML、JavaScript 和 TypeScript 檔案,避免產生不必要的 CSS。

引入 Tailwind 樣式

src/index.cssstyles.css 檔案中加入以下內容,讓 Tailwind CSS 套用基礎樣式:

@tailwind base;
@tailwind components;
@tailwind utilities;

最後,在開發環境中執行 Tailwind 編譯指令:

npx tailwindcss -i ./src/index.css -o ./dist/output.css --watch

這會自動監聽檔案變化,並輸出 Tailwind CSS 的樣式到 output.css


三、Tailwind CSS 常用類別

Tailwind CSS 提供大量的工具類別,以下介紹幾個常用的類別和應用範例。

1. 文字樣式

Tailwind 允許透過類別來快速調整字體大小、顏色、行距等樣式,例如:

<p class="text-lg font-bold text-gray-700 leading-relaxed">
這是一段示範文字
</p>

其中:

  • text-lg:字體大小
  • font-bold:加粗
  • text-gray-700:深灰色文字
  • leading-relaxed:行距較寬

2. 背景與邊框

可以使用背景色與邊框樣式來設計 UI,示例如下:

<div class="bg-blue-500 text-white p-4 rounded-lg shadow-lg">
Tailwind CSS 範例
</div>
  • bg-blue-500:藍色背景
  • text-white:白色文字
  • p-4:內距 16px
  • rounded-lg:圓角
  • shadow-lg:大型陰影

3. Flexbox 佈局

Tailwind CSS 提供完整的 Flexbox 工具類別,讓開發者能夠快速進行佈局:

<div class="flex items-center justify-between p-4 bg-gray-200">
<div>左側內容</div>
<div>右側內容</div>
</div>
  • flex:啟用 Flexbox
  • items-center:垂直置中
  • justify-between:左右對齊

4. Grid 佈局

使用 Grid 來建立多欄式佈局:

<div class="grid grid-cols-3 gap-4">
<div class="bg-red-200 p-4">1</div>
<div class="bg-green-200 p-4">2</div>
<div class="bg-blue-200 p-4">3</div>
</div>
  • grid:啟用 Grid
  • grid-cols-3:建立三欄
  • gap-4:欄間距

5. 響應式設計

Tailwind 內建響應式前綴,適用於不同裝置:

<div class="text-base md:text-lg lg:text-xl xl:text-2xl">
響應式字體大小
</div>
  • text-base:手機(預設)
  • md:text-lg:平板(min-width: 768px
  • lg:text-xl:筆電(min-width: 1024px
  • xl:text-2xl:桌機(min-width: 1280px

四、Tailwind CSS 進階功能

1. 自訂樣式

可以透過 tailwind.config.js 來擴充自訂樣式,例如新增顏色:

module.exports = {
theme: {
extend: {
colors: {
primary: "#1E40AF",
secondary: "#9333EA",
},
},
},
};

這樣就可以在 HTML 中使用 text-primarybg-secondary 來套用顏色。

2. 使用 @apply 簡化樣式

在 CSS 檔案中使用 @apply 來重複使用 Tailwind 類別:

.btn {
@apply bg-blue-500 text-white px-4 py-2 rounded;
}

然後在 HTML 中只需寫:

<button class="btn">按鈕</button>

五、總結

Tailwind CSS 透過工具類別的方式大幅提升開發效率,並提供響應式設計、靈活佈局與高度客製化的功能。對於前端開發者而言,熟悉 Tailwind CSS 可以讓 UI 設計更加直觀、高效,並減少對自訂 CSS 的依賴,進而提升開發維護性。

CSS Animation 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

在現代網頁開發中,動畫是一種強而有力的表現手法,能提升使用者體驗,使介面更加生動、有趣。相比 JavaScript,CSS Animation 提供一種更簡潔、性能更佳的動畫解法,尤其適合製作 UI 中的小型動畫效果。

本篇教學筆記將介紹 CSS Animation 的基本語法、常見屬性與一個實際範例,幫助你快速上手。


為什麼使用 CSS Animation?

CSS Animation 的優點:

  1. 簡單易用:使用純 CSS 撰寫動畫,不需額外 JavaScript。
  2. 效能佳:瀏覽器針對 CSS Animation 做過優化,效能較好。
  3. 語意清楚:樣式與行為分離,程式碼結構清晰。

基本語法

CSS 動畫的基本概念分為兩大部分:

  1. @keyframes:定義動畫的每個階段(狀態)
  2. 動畫屬性(如 animation-nameanimation-duration 等):設定動畫的名稱、時間、重複次數等

1. 定義動畫步驟:@keyframes

@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

也可以使用百分比表示中間過程:

@keyframes moveBox {
0% {
transform: translateX(0);
}
50% {
transform: translateX(100px);
}
100% {
transform: translateX(0);
}
}

2. 套用動畫屬性

.box {
animation-name: fadeIn;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}

也可以使用簡寫語法:

animation: fadeIn 1s ease-in-out 0s 1 forwards;

常見屬性說明

屬性說明
animation-name對應的 @keyframes 名稱
animation-duration動畫持續時間(如 2s500ms
animation-delay動畫延遲時間
animation-iteration-count播放次數(數字或 infinite
animation-direction播放方向(如 normalreversealternate
animation-timing-function動畫速度曲線(如 lineareaseease-in-outcubic-bezier
animation-fill-mode動畫結束後元素狀態(如 noneforwardsbackwardsboth

實際範例:淡入移動效果

HTML

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Animation 入門範例</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="animated-box">Hello CSS Animation</div>
</body>
</html>

CSS(style.css)

body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}

.animated-box {
padding: 20px 40px;
background-color: #4a90e2;
color: white;
font-size: 24px;
border-radius: 8px;
opacity: 0;
transform: translateY(30px);
animation: fadeSlideIn 1s ease-out forwards;
}

@keyframes fadeSlideIn {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

效果說明

當頁面載入時,.animated-box 元素會從下方淡入移動到原位。這種效果常用於元件出現時的動畫,例如:卡片載入、提示訊息出現等。


進階補充

多個動畫階段

可以使用百分比定義多個狀態:

@keyframes bounce {
0% { transform: translateY(0); }
30% { transform: translateY(-50px); }
50% { transform: translateY(0); }
70% { transform: translateY(-25px); }
100% { transform: translateY(0); }
}

搭配 hover 做互動動畫

.button {
transition: transform 0.3s ease;
}

.button:hover {
transform: scale(1.1);
}

雖然這是使用 transition,但若需要更多控制,就可以改用 animation


總結

CSS Animation 提供一套簡潔的方式,能有效為網頁增添動態效果。適合使用在進場動畫、提示動畫、狀態變化等場合。若需求單純、動畫步驟不複雜,建議優先使用 CSS Animation 來取代 JavaScript,提升效能與維護性。

如果你剛開始學前端,建議從觀察現有網站的動畫效果出發,試著模仿並自己實作,會對 CSS Animation 有更深的理解。下一步你可以學習 transitiontransformcubic-bezier,讓動畫更自然、細緻。

Monorepo 入門教學筆記:使用 npm Workspaces 管理多專案 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

一、什麼是 Monorepo?

Monorepo(Monolithic Repository) 是一種將多個相互關聯的專案(如前端、後端、共用函式庫等)統一放在同一個 Git 倉庫中進行版本管理的架構策略。這種做法可提升跨專案開發效率、共用程式碼的一致性,並簡化 CI/CD 流程。

相較於 Multi-repo(每個專案獨立倉庫),Monorepo 具有以下幾項優勢:

優點

  • 統一依賴管理:使用者可於 root 層集中管理所有套件與版本。
  • 共用程式碼方便:子專案間可以引用彼此模組,而不需額外發佈。
  • 版本一致性高:統一管理 Node.js 與 TypeScript 設定,避免各專案設定不一致。
  • 原子性變更:可一次提交多個模組的改動,有利於大型重構與追蹤問題。

缺點

  • 權限與管理更複雜:團隊需要良好的規範以避免耦合過深。
  • 初期學習門檻略高:需要理解 workspace 機制與設定方式。
  • 大型 repo 操作效能問題:隨專案規模增長,Git 操作與工具效能需特別注意。

二、何謂 npm Workspaces?

npm 7 開始,Node.js 官方就原生支援 Workspaces。它允許開發者在單一專案下,透過一個 package.json 管理多個子專案(workspace packages)。這讓 npm 也能像 Yarn 或 pnpm 一樣支援 monorepo。

特點:

  • 原生支援,不需額外工具
  • 使用方式簡單直觀
  • 適合小型與中型 monorepo 專案

三、建立 Monorepo 專案實例(使用 npm workspaces)

1. 初始化根目錄

mkdir my-monorepo
cd my-monorepo
npm init -y

修改根目錄下的 package.json 加入 workspaces 設定:

{
"name": "my-monorepo",
"version": "1.0.0",
"private": true,
"workspaces": ["packages/*"]
}

這表示所有子專案會放在 packages/ 目錄中,支援通配符 *


2. 建立三個子專案

  • packages/utils:共用函式庫
  • packages/backend:Node.js API
  • packages/frontend:前端應用(可用 React/Vite)
mkdir -p packages/utils
mkdir -p packages/backend
mkdir -p packages/frontend

初始化子專案:

cd packages/utils
npm init -y

cd ../backend
npm init -y

cd ../frontend
npm init -y

3. 撰寫共用模組(utils)

packages/utils/index.js

function greet(name) {
return `Hello, ${name}`;
}

module.exports = { greet };

packages/utils/package.json

{
"name": "@my/utils",
"version": "1.0.0",
"main": "index.js"
}

4. 使用共用模組於 backend

packages/backend/index.js

const { greet } = require("@my/utils");

console.log(greet("Backend"));

packages/backend/package.json 加入依賴:

{
"name": "@my/backend",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@my/utils": "1.0.0"
}
}

當你在 monorepo 根目錄執行:

npm install

npm 會自動建立 workspace 之間的 symbolic link,將 @my/utils 套件安裝給 @my/backend,無需手動 npm publish

執行 backend:

node packages/backend/index.js

輸出結果為:

Hello, Backend

5. 多專案腳本管理

在 root 的 package.json 中可設定統一指令:

{
"scripts": {
"start:backend": "npm --workspace @my/backend start",
"start:frontend": "npm --workspace @my/frontend start"
}
}

若在 @my/backendpackage.json 中定義了:

{
"scripts": {
"start": "node index.js"
}
}

則可於根目錄執行:

npm run start:backend

四、進階使用技巧

1. 同時執行所有 workspace 指令

npm run build --workspaces

這會同時執行所有子專案中名為 build 的 script。

2. 安裝依賴給特定子專案

npm install lodash --workspace @my/backend

等同於進到 packages/backend 下執行 npm install lodash

3. 使用 TypeScript 建構共用型別

若你使用 TypeScript,可將 packages/utils 改為 index.ts 並加上 types 欄位:

{
"name": "@my/utils",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts"
}

建議搭配 tsc -b 支援 project references,以改善大型專案建置效能。


五、適合使用 Monorepo 的情境

  • 前後端一體化開發(Fullstack)
  • 套件開發與組合應用(如微前端架構)
  • 共用型別與邏輯模組(utility / types / domain model)
  • 多人協作、模組劃分清晰的大型產品

六、總結

Monorepo 是一種有效提升多專案整合效率的開發模式。透過 npm Workspaces,你可以不用額外安裝任何工具,即可快速建立一個結構清晰的 monorepo。這種架構不僅適合大型產品團隊,也非常適合個人或小型團隊開發多模組系統時採用。

建議開發者從簡單的 monorepo 起步,並根據團隊需求逐步導入版本控制策略、模組邊界規範與自動化部署流程,以發揮 Monorepo 架構的最大價值。


若你有意將這份架構應用到 React、Next.js、NestJS、TypeScript、Lerna 等進階框架或部署情境,歡迎告訴我,我可以提供進一步的範例與最佳實踐建議。

React 與 Next.js 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

1. React 簡介

React 是由 Facebook(現 Meta)開發的 JavaScript 前端函式庫,主要用於構建 UI 元件。它採用組件化開發方式,並透過 Virtual DOM 提升效能。

2. Next.js 簡介

Next.js 是一個基於 React 的框架,提供伺服器端渲染(SSR)、靜態網站生成(SSG)等功能,讓開發者能夠更輕鬆地開發 SEO 友好的應用程式。

3. 安裝 Next.js

使用 create-next-app 初始化 Next.js 專案:

npx create-next-app@latest my-next-app
cd my-next-app
npm run dev

這將會啟動開發伺服器,預設運行於 http://localhost:3000

4. Next.js 核心概念

4.1 頁面(Pages)

Next.js 使用 pages/ 目錄來定義路由,每個 .js.tsx 文件會自動成為一個頁面。

範例:pages/index.js

export default function Home() {
return <h1>歡迎來到 Next.js!</h1>;
}

新增 pages/about.js

export default function About() {
return <h1>關於我們</h1>;
}

瀏覽 / 會載入 index.js,瀏覽 /about 會載入 about.js

使用 next/link 來建立導航連結:

import Link from "next/link";

export default function Navbar() {
return (
<nav>
<Link href="/">首頁</Link> | <Link href="/about">關於</Link>
</nav>
);
}

4.3 頁面中的 props

Next.js 支援 getServerSideProps(伺服器端渲染)和 getStaticProps(靜態生成)。

伺服器端渲染(SSR)

export async function getServerSideProps() {
const res = await fetch("https://api.example.com/data");
const data = await res.json();
return { props: { data } };
}

export default function Page({ data }) {
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

靜態生成(SSG)

export async function getStaticProps() {
const res = await fetch("https://api.example.com/data");
const data = await res.json();
return { props: { data } };
}

export default function Page({ data }) {
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

5. API 路由(API Routes)

pages/api/ 目錄下建立 API 端點。

範例:pages/api/hello.js

export default function handler(req, res) {
res.status(200).json({ message: "Hello, API!" });
}

請求 /api/hello 會返回 JSON 資料。

6. 使用全域狀態管理(React Context)

import { createContext, useContext, useState } from "react";

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}

export function useTheme() {
return useContext(ThemeContext);
}

_app.js 中使用 Provider:

import { ThemeProvider } from "../context/ThemeContext";

export default function MyApp({ Component, pageProps }) {
return (
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
);
}

在元件中存取狀態:

import { useTheme } from "../context/ThemeContext";

export default function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
切換主題(目前:{theme}
</button>
);
}

7. 總結

Next.js 提供了比 React 更豐富的功能,如內建路由、伺服器端渲染(SSR)和 API 路由,適合開發高效能與 SEO 友好的網站。熟悉這些核心概念後,你可以更輕鬆地構建現代化的前端應用程式。

補充:可選搭配技術(實務常見)

技術目的
Tailwind CSS快速開發 UI 樣式
Zustand/Redux全域狀態管理
NextAuth.js登入驗證(支援 Google, GitHub 等)
PrismaORM 操作 MySQL/PostgreSQL
React Query前端資料抓取與快取
shadcn/ui組件庫(Tailwind 美型元件)

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 中的完整生命週期與流程。