跳至主要内容

GraphQL 入門教學筆記(ESM 模組版)| 學習筆記

· 閱讀時間約 4 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在傳統的 REST API 中,前端往往需要根據不同的需求,呼叫多個端點取得資料,有時還會遇到資料過多(Over-fetching)或不足(Under-fetching)的問題。為了解決這些痛點,Facebook 在 2015 年開源了 GraphQL,一種靈活且高效率的查詢語言。

GraphQL 讓前端可以明確指定想要的資料欄位,伺服器僅回傳需要的資料,改善資料浪費與重複請求的問題。此語言既可以查詢(Query)、也可以修改資料(Mutation),更支援訂閱(Subscription)即時變化,成為 REST 的強大替代方案。

本文將以 ESM(ECMAScript Module)模組格式,搭配現代 Node.js 環境與 Apollo Server,手把手實作一個簡易 GraphQL API。


重點摘要​

  • GraphQL 是什麼? 一種用於 API 的查詢語言(Query Language),由 Facebook 開發。它不是資料庫,而是用來建立伺服器與用戶端之間通訊的一種規格。

  • 特色與優點

    • 客戶端可以精確指定所需資料
    • 單一端點處理所有請求
    • 減少 over-fetching / under-fetching 問題
    • 強型別系統(Schema 定義清楚 API 結構)
    • 自帶文件化功能(如 Apollo Studio / GraphQL Playground)
  • 核心概念

    • Schema:定義資料型別與查詢結構
    • Query:讀取資料
    • Mutation:修改資料(新增、刪除、更新)
    • Resolver:實際執行資料存取邏輯的函式
    • Type:定義資料欄位與型別,例如 type Book { title: String }
  • GraphQL vs REST

    特性RESTGraphQL
    資料請求多個端點單一端點
    回傳資料固定欄位,可能過多或不足客戶端可自訂所需欄位
    文件維護手動撰寫 Swagger/OpenAPI自動產生於 Playground / Studio
    資料關聯查詢多次 HTTP 請求一次查詢可取得巢狀關聯資料

實作範例(Node.js + Apollo Server + ESM)​

一、專案初始化​

建立資料夾並初始化:

mkdir graphql-esm-demo
cd graphql-esm-demo
npm init -y
npm install @apollo/server graphql

修改 package.json 啟用 ESM 模組:

{
"type": "module"
}

二、撰寫 GraphQL API(server.js)​

建立 server.js:

// server.js
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

// 模擬資料
const books = [
{ title: 'GraphQL 教學指南', author: 'Alice' },
{ title: '現代 JavaScript', author: 'Bob' },
];

// 定義 Schema
const typeDefs = `
type Book {
title: String
author: String
}

type Query {
books: [Book]
}
`;

// 定義 Resolver
const resolvers = {
Query: {
books: () => books,
},
};

// 建立 Apollo Server 實例
const server = new ApolloServer({ typeDefs, resolvers });

// 啟動伺服器
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});

console.log(`🚀 Server ready at ${url}`);

三、啟動伺服器並測試​

執行伺服器:

node server.js

啟動後會顯示伺服器位置(通常是 http://localhost:4000 Apollo Studio,或使用 Postman / Altair / curl 測試。


四、查詢範例(GraphQL Playground 中輸入)​

query {
books {
title
author
}
}

我們也可以僅查詢某個欄位,例如:

query {
books {
title
}
}

這正是 GraphQL 的威力所在:我們只拿我們需要的資料。


延伸功能與建議學習方向​

  • Mutation 實作(新增、刪除資料) 可以加入 Mutation 類型與 resolver,實作 POST/PUT 功能。

  • 資料來源整合 Resolver 可以整合 REST API、資料庫、第三方服務等來源。

  • 驗證與權限 可透過 Context 傳入使用者驗證資訊,進行資料存取控管。

  • 使用工具

    • Apollo Studio:視覺化介面與測試工具
    • GraphQL Codegen:產生型別定義與前端 Hooks
    • Relay / urql / Apollo Client:前端整合工具

總結​

GraphQL 提供比 REST 更彈性與效率的 API 設計方式,尤其適合需要頻繁前後端協作或資料結構變化頻繁的專案。透過本篇教學,我們已經學會如何使用 Apollo Server 以 ESM 模組撰寫簡單的 GraphQL API,並實際查詢資料。

未來我們可以嘗試進一步加入 Mutation、串接資料庫(如 MongoDB、PostgreSQL)、前端 Apollo Client 等,打造一個全端的 GraphQL 應用。

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

· 閱讀時間約 4 分鐘
KD Chang
Builder, Curator & Lifelong Learner

一、什麼是 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/backend 的 package.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 分鐘
KD Chang
Builder, Curator & Lifelong Learner

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 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

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 定義參數
巢狀路由路由中可包含子路由
導覽守衛控制進入頁面的權限
模式hash 或 history 模式

結語​

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

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

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

· 閱讀時間約 6 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在現代前端開發中,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.js 或 main.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() 創建表格的數據源,並使用 ElTable 和 ElTableColumn 來渲染表格。

全域配置​

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 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在 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() 模組化使用

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

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

Webpack 入門教學筆記:現代前端建構工具的基礎與實戰 | 學習筆記

· 閱讀時間約 4 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在前端專案日益龐大與模組化的今天,建構工具(build tools)扮演了極其關鍵的角色。而 Webpack 作為目前最主流的模組打包器(module bundler),廣泛應用於各類前端應用與框架中(如 React、Vue 等)。本篇筆記將介紹 Webpack 的基本概念、核心組件與實際範例,協助你快速理解與實作。


一、什麼是 Webpack?​

Webpack 是一個靜態模組打包器,它會從你的應用程式進入點(entry point)開始,分析相依的模組(JavaScript、CSS、圖片、JSON 等),然後打包成一或多個 bundle,供瀏覽器載入使用。

主要特性包含:

  • 支援模組系統(如 CommonJS、ESM)
  • 可搭配各種 Loader 處理不同類型資源
  • 使用 Plugin 擴充打包功能
  • 開發與生產模式可分離配置
  • 支援 Hot Module Replacement(HMR)與 Dev Server

二、基本安裝與專案初始化​

建立一個新的專案資料夾:

mkdir my-webpack-app
cd my-webpack-app
npm init -y

安裝 Webpack 及其 CLI 工具:

npm install --save-dev webpack webpack-cli

建立基本的專案目錄結構:

my-webpack-app/
├── dist/
│ └── index.html
├── src/
│ └── index.js
├── package.json
└── webpack.config.js

三、撰寫基本範例​

1. src/index.js​

import "./style.css";

const element = document.createElement("h1");
element.textContent = "Hello Webpack!";
document.body.appendChild(element);

2. src/style.css​

body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
text-align: center;
padding-top: 100px;
}

3. dist/index.html​

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My Webpack App</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>

四、Webpack 基礎設定檔​

建立 webpack.config.js:

const path = require("path");

module.exports = {
entry: "./src/index.js", // 入口檔案
output: {
filename: "main.js", // 輸出檔案名稱
path: path.resolve(__dirname, "dist"), // 輸出目錄
clean: true, // 每次打包清空 dist
},
module: {
rules: [
{
test: /\.css$/i, // 處理 CSS 檔案
use: ["style-loader", "css-loader"],
},
],
},
mode: "development", // 模式(也可設為 'production')
};

安裝所需的 CSS loader:

npm install --save-dev style-loader css-loader

五、打包與啟動​

執行打包指令:

npx webpack

執行後會自動將所有相依檔案打包為 dist/main.js,可以直接打開 dist/index.html 查看效果。


六、啟用 Webpack Dev Server(開發伺服器)​

為了開發方便,我們可以啟用內建的開發伺服器,支援 HMR(Hot Module Replacement)功能。

安裝 dev server:

npm install --save-dev webpack-dev-server

在 webpack.config.js 增加設定:

devServer: {
static: './dist',
port: 3000,
open: true, // 自動開啟瀏覽器
hot: true, // 啟用 HMR
}

更新 package.json 的 scripts:

"scripts": {
"build": "webpack",
"start": "webpack serve"
}

執行:

npm run start

開發伺服器啟動後會自動開啟 localhost:3000,修改檔案後可即時預覽變更。


七、區分開發與生產模式​

在實際開發中,我們會針對開發與正式環境建立不同的設定檔,使用 webpack-merge 套件來合併共用設定:

npm install --save-dev webpack-merge

建立以下三個檔案:

webpack.common.js
webpack.dev.js
webpack.prod.js

webpack.common.js 放共用設定,其他兩個則各自設定環境特有的項目,例如:

  • webpack.dev.js: 開啟 Source Map、HMR
  • webpack.prod.js: 最佳化壓縮、移除 console.log 等

八、擴充功能:使用 Babel 處理 ES6+​

Webpack 本身不會轉譯 JavaScript,需要搭配 Babel:

npm install --save-dev babel-loader @babel/core @babel/preset-env

新增 .babelrc:

{
"presets": ["@babel/preset-env"]
}

在 webpack.config.js 加入規則:

{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}

這樣就能讓 Webpack 在打包過程中使用 Babel 將 ES6+ 語法轉譯為相容的版本。


九、使用 Plugin 擴充功能:HtmlWebpackPlugin​

HtmlWebpackPlugin 可自動產生並插入 script 標籤:

npm install --save-dev html-webpack-plugin

修改設定:

const HtmlWebpackPlugin = require("html-webpack-plugin");

plugins: [
new HtmlWebpackPlugin({
template: "./dist/index.html",
inject: "body",
}),
];

不再需要手動在 index.html 中引入 main.js,Webpack 會自動插入對應 bundle。


十、結語與建議​

Webpack 雖然設定上比 Vite 複雜,但擁有極高的自訂彈性與完整生態系,是大型專案不可或缺的建構工具。透過 Loader 處理不同格式的資源、Plugin 擴充功能,再加上分環境設定與開發伺服器支援,Webpack 能有效協助你管理現代前端應用的整個建構流程。

適合情境包括:

  • React、Vue 中大型應用
  • 多入口或模組系統複雜的專案
  • 需要高度自訂打包流程的企業內部系統

雖然 Vite、Parcel 等工具正迅速崛起,但 Webpack 仍是學習前端建構工具不可忽略的重要基礎。

Grid 網頁排版技巧入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在現今網頁設計中,排版的結構與呈現方式對於用戶體驗至關重要。隨著網頁設計的演進,CSS Grid 成為了最強大且靈活的排版工具之一。透過 CSS Grid,你可以輕鬆創建複雜的佈局,並且適應不同設備的需求。本文將介紹 CSS Grid 的基本概念與使用技巧,幫助你迅速掌握如何在網頁設計中使用 Grid。

1. 什麼是 CSS Grid?​

CSS Grid Layout(簡稱 Grid)是 CSS3 的一個強大功能,允許開發者以列和行的方式來設計網頁佈局。它使得網頁設計更加直觀和靈活,不再需要依賴浮動(float)或定位(position)等老舊技巧,簡化了許多複雜的布局問題。

Grid 是由「容器」和「項目」兩部分組成。容器定義了網格的結構,而項目則是容器內部的元素。你可以在容器內輕鬆地將項目放置在指定的網格區域中,從而創建各種排版樣式。

2. CSS Grid 的基本語法​

在使用 CSS Grid 時,首先需要定義一個容器元素並啟用 Grid 布局。這樣,容器內的子元素將成為 Grid 項目。以下是基本的設置步驟:

.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto;
gap: 10px;
}
  • display: grid; 啟用容器的 Grid 布局。
  • grid-template-columns: repeat(3, 1fr); 設定三列,每列的寬度為等比例(1fr)。1fr 代表容器寬度的 1/3。
  • grid-template-rows: auto; 使得行的高度根據內容自動調整。
  • gap: 10px; 定義網格項目之間的間距。

3. 定義 Grid 容器的列和行​

你可以使用 grid-template-columns 和 grid-template-rows 來設置容器內列和行的數量、大小及比例。

定義列​

例如,若想創建四列的佈局,可以這樣寫:

.container {
display: grid;
grid-template-columns: 200px 300px 400px 1fr;
}

這裡,我們設置了四列的寬度,前三列有固定的像素寬度,最後一列使用 1fr,即佔據剩餘的可用空間。

定義行​

類似地,你可以設置行的大小。若要讓每一行的高度根據內容自動調整,可以使用:

.container {
display: grid;
grid-template-rows: 100px auto;
}

這表示網格容器有兩行,第一行的高度固定為 100px,第二行的高度將根據內容自動調整。

4. 放置 Grid 項目​

Grid 容器的子元素會自動成為 Grid 項目。你可以使用 grid-column 和 grid-row 屬性來指定某個項目占據的列與行。

例子​
.item1 {
grid-column: 1 / 3;
grid-row: 1;
}

這表示 .item1 元素會從第 1 列跨越到第 3 列,占據第一行。

  • grid-column: 1 / 3; 表示該元素會從第 1 列起,跨越兩列,直到第 3 列結束。
  • grid-row: 1; 表示該元素位於第 1 行。

5. 使用 fr 單位設置比例​

CSS Grid 中最重要的單位之一是 fr(fraction,分數),它讓你可以設定元素在可用空間中所佔比例。

假設你有三列,且希望第一列占 1 部分,第二列占 2 部分,第三列占 3 部分:

.container {
display: grid;
grid-template-columns: 1fr 2fr 3fr;
}

這樣會將總寬度分成六等份,其中第一列占 1 份,第二列占 2 份,第三列占 3 份。這樣的佈局適合於動態響應式設計。

6. 設置間距​

除了 gap 屬性,你還可以為列與行的間距分別設置不同的值:

.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px 20px;
}

這裡,gap: 10px 20px; 的第一個數值設定了行之間的間距為 10px,第二個數值設定了列之間的間距為 20px。

7. 嵌套 Grid​

Grid 允許你在網格項目內再次使用 Grid 佈局,這樣可以創建更複雜的排版結構。例如,假設你在某個項目內再創建一個小的網格:

.item1 {
display: grid;
grid-template-columns: 1fr 2fr;
}

在這個範例中,.item1 是一個 Grid 項目,它內部再次使用 Grid 排版,定義兩列,其中第一列占據 1 部分,第二列占據 2 部分。

8. 響應式設計​

Grid 讓你輕鬆應對不同設備的佈局需求。你可以使用媒體查詢來調整 Grid 排版。例如:

.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
}

@media (max-width: 768px) {
.container {
grid-template-columns: repeat(2, 1fr);
}
}

@media (max-width: 480px) {
.container {
grid-template-columns: 1fr;
}
}

這段程式碼會根據螢幕寬度變化調整容器的列數:在寬度大於 768px 時顯示四列,在寬度小於 768px 時顯示兩列,在寬度小於 480px 時顯示單列。

9. 小結​

CSS Grid 為現代網頁設計提供了一個簡單、強大且靈活的排版解決方案。它不僅能夠解決過去使用浮動或定位方法無法達成的複雜佈局,還能輕鬆適應響應式設計的需求。掌握 CSS Grid,你將能夠更高效地創建現代化的網頁佈局,提升設計的精確度與可維護性。希望本文的介紹能幫助你快速入門並將這項技術應用到你的項目中。

Flexbox 網頁排版技巧入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
KD Chang
Builder, Curator & Lifelong Learner

1. Flexbox 簡介​

Flexbox(Flexible Box Layout)是一種 CSS3 佈局模式,專門用來設計一維的彈性佈局,適用於水平或垂直排列元素,使網頁排版更加靈活且易於維護。

1.1 為何使用 Flexbox?​

  • 彈性調整:元素可根據可用空間動態調整大小。
  • 簡化佈局:減少對 float、inline-block 及 position 依賴。
  • 更好的對齊方式:內建強大的對齊與分佈控制。

2. Flexbox 基本概念​

Flexbox 佈局的核心概念是 容器(container) 和 子項目(items)。

.container {
display: flex;
}

一旦對容器使用 display: flex,其內部的子元素將自動變成 Flex 子項目,並受 Flex 佈局影響。

3. Flex 容器屬性​

3.1 flex-direction​

決定主軸(main axis)方向。

.container {
flex-direction: row; /* 預設值,水平排列 */
flex-direction: row-reverse; /* 反向水平排列 */
flex-direction: column; /* 垂直排列 */
flex-direction: column-reverse; /* 反向垂直排列 */
}

3.2 justify-content​

控制子項目在主軸上的對齊方式。

.container {
justify-content: flex-start; /* 預設,從左到右排列 */
justify-content: flex-end; /* 靠右排列 */
justify-content: center; /* 置中排列 */
justify-content: space-between; /* 兩端對齊,間距平均分布 */
justify-content: space-around; /* 子項目兩側有相等的間距 */
justify-content: space-evenly; /* 所有間距均等 */
}

3.3 align-items​

控制子項目在交叉軸(cross axis)上的對齊方式。

.container {
align-items: flex-start; /* 靠起始位置對齊 */
align-items: flex-end; /* 靠末端對齊 */
align-items: center; /* 垂直置中對齊 */
align-items: stretch; /* 預設,撐滿高度 */
align-items: baseline; /* 依據文本基線對齊 */
}

3.4 align-content​

適用於多行佈局,控制多行之間的間距。

.container {
align-content: flex-start;
align-content: flex-end;
align-content: center;
align-content: space-between;
align-content: space-around;
align-content: space-evenly;
}

4. Flex 子項目屬性​

4.1 flex-grow​

設定子項目如何分配多餘空間。

.item {
flex-grow: 1; /* 所有子項目平均分配空間 */
}

4.2 flex-shrink​

控制子項目如何縮小。

.item {
flex-shrink: 0; /* 防止縮小 */
}

4.3 flex-basis​

設定子項目的初始大小。

.item {
flex-basis: 200px; /* 設定初始寬度或高度 */
}

4.4 flex​

綜合 flex-grow、flex-shrink 和 flex-basis。

.item {
flex: 1 1 100px; /* grow, shrink, basis */
}

5. Flexbox 常見佈局範例​

5.1 水平置中​

.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

5.2 等寬三欄佈局​

.container {
display: flex;
}
.item {
flex: 1;
}

5.3 兩欄固定 + 自適應​

.container {
display: flex;
}
.sidebar {
flex: 0 0 200px; /* 固定 200px */
}
.content {
flex: 1; /* 佔滿剩餘空間 */
}

6. 結論​

Flexbox 是一種強大且直觀的 CSS 佈局方式,能夠解決傳統排版難題,使開發者能夠輕鬆實現響應式佈局與對齊方式。本篇筆記介紹了 Flexbox 的基本屬性及常見佈局範例,進一步學習可以研究 CSS Grid 和更進階的排版技巧。