[TOC] # 簡介 歡迎加入我們的團隊!我們的目標是利用以下技術棧來構建一個現代化的全端網頁應用系統。本文檔將為大家介紹這些技術並提供設置開發環境的指引,確保團隊每個成員都能快速上手,為項目貢獻力量。 ## 技術棧介紹 ### Next.js **Next.js** 是一個基於 React 的框架,提供了伺服器端渲染(SSR)和靜態網站生成(SSG)的功能,讓開發者能夠輕鬆構建 SEO 友好的網站。它的主要特性包括: - 頁面基於檔案的路由系統。 - 支持動態路由與 API 路由。 - 自動代碼拆分與優化。 - 支持 TypeScript 開箱即用。 ### Tailwind CSS **Tailwind CSS** 是一個實用的 CSS 框架,它提供了大量的即用型類別(utility classes),讓你可以快速編寫具備響應式設計的網頁。相比於傳統的 CSS 框架,Tailwind 強調的是靈活性和可組合性: - 不需要定義特定的樣式表,直接使用類別。 - 方便地實現主題自定義和樣式重用。 - 簡化了 CSS 的管理,減少了樣式衝突。 ### TypeScript **TypeScript** 是 JavaScript 的超集,增加了靜態類型定義,幫助開發者在開發過程中捕捉錯誤,提升代碼的可維護性。使用 TypeScript 的好處包括: - 更好的代碼提示與自動補全。 - 防止常見的類型錯誤。 - 使代碼更容易理解和重構。 ### MySQL **MySQL** 是一個受歡迎的關聯式資料庫管理系統,廣泛用於各類應用中。它的特點包括: - 強大的查詢功能和靈活的資料表設計。 - 支持複雜的交易處理。 - 良好的擴展性與性能表現。 ### Prisma **Prisma** 是一個現代化的 ORM(物件關聯映射)工具,提供了與資料庫互動的高層次抽象。它能夠自動生成 TypeScript 類型,並與 MySQL 無縫集成。Prisma 的優點有: - 簡化的資料庫操作。 - 強類型支持,減少運行時錯誤。 - 方便的數據遷移工具。 ## 開發環境設置 1. **Node.js 安裝:** - 確保安裝 Node.js(建議版本為 20.x 或更高),可以通過 [Node.js 官方網站](https://nodejs.org/) 下載和安裝。 ## 項目結構與工作流程 1. **項目結構:** - `app/`:放置所有的頁面文件,每個文件代表一個路由。 - `components/`:可重用的 React 組件。 - `styles/`:全局樣式與 Tailwind 的配置文件。 - `prisma/`:Prisma schema 文件與遷移檔案。 - `app/api/`:放置 API 路由與伺服器端邏輯。 2. **工作流程:** - **前端開發:** 使用 Next.js 和 Tailwind CSS 構建頁面與 UI。 - **後端開發:** 利用 Next.js 的 API 路由與 Prisma 進行資料庫操作。 - **版本控制:** 使用 Git 進行版本控制,並且在開發新功能時創建分支。 3. **版本控制:** - 在軟體開發中,版本控制是協作和跟蹤代碼變更的關鍵流程。 - `main` 分支是受保護的,無法直接在其上進行修改或提交。 - 為了進行代碼變更,請執行以下步驟: 1. **檢出並創建新分支:** 使用 `git checkout -b feature/new-feature` 從 `main` 分支創建並切換到一個新的特性分支。 2. **開發與提交:** 在新分支上編寫代碼,進行本地測試,並將變更提交到該分支。 3. **推送到遠端倉庫:** 使用 `git push origin feature/new-feature` 將本地分支推送到遠端倉庫。 4. **創建拉取請求(PR):** 在遠端倉庫中,創建一個針對 `main` 分支的拉取請求。 5. **代碼審查(Code Review):** 其他團隊成員將審查 PR,提供改進建議或發現潛在問題。 6. **合併 PR:** 審查通過後,將 PR 合併到 `main` 分支,確保變更經過測試與審查,保持代碼庫的質量與穩定性。 4. **Coding Style:** 在我們的開發流程中,保持一致的代碼風格是至關重要的。為此,我們使用 **Prettier** 來自動格式化代碼,確保代碼風格的一致性,**ESLint** 用來檢查並修復潛在的代碼錯誤或不良的編碼習慣,**TypeScript 編譯器(TSC)** 則用於檢查 TypeScript 代碼的類型正確性。當你提交代碼並推送到 GitHub 時,我們的 CI 工作流程會自動運行這些工具,進行代碼格式化檢查、靜態代碼分析,以及類型檢查。如果任何一項檢查未通過,CI 會報告錯誤,並阻止代碼合併到主分支,這樣可以確保進入代碼庫的代碼質量和一致性。 在push到Github前可以在本地端執行以下指令來確保CI流程沒有問題: ```bash npm run format npm run lint --fix npm run tsc ``` ## 資源與學習材料 - [Next.js 官方文檔](https://nextjs.org/docs) - [Tailwind CSS 官方文檔](https://tailwindcss.com/docs) - [TypeScript 手冊](https://www.typescriptlang.org/docs/) - [Prisma 官方文檔](https://www.prisma.io/docs/) - [MySQL 文件](https://dev.mysql.com/doc/) 希望這份指南能幫助大家快速上手並順利開始項目的開發,若有任何疑問請隨時交流! # React 簡介 ## React 是什麼? **React** 是由 Facebook 開發的一個用於構建使用者界面的 JavaScript 庫。它的設計理念是組件化(Component-based),每個組件都可以看作是一個獨立的界面單元,這些組件可以被組合、重用,從而構建複雜的 UI。 React 的特點包括: - **聲明式編程:** 你可以用直觀的方式描述應該如何呈現界面,React 會負責更新和渲染界面。 - **組件化:** 應用程式被拆分成獨立的組件,這些組件各自負責渲染 UI 的一部分。 - **一次學習,到處使用:** React 不僅可以用來開發網頁,還可以用於開發行動應用程式(React Native)和桌面應用程式。 ## React 的核心概念 ### 組件 **組件** 是 React 的基本單位。每個組件可以視為一個功能模塊,組件可以是「有狀態的」或「無狀態的」,也可以接收輸入(props)並輸出 UI。 在 React 中,我們更推薦使用 **函數組件(Functional Component)** 而不是類組件(Class Component)。函數組件是簡單的 JavaScript 函數,並且可以利用 **Hooks** 來管理狀態和副作用。 ### JSX **JSX** 是一種 JavaScript 語法擴展,用來描述 UI 結構。它看起來很像 HTML,但實際上是 JavaScript 的語法糖,可以在 React 組件中使用: ```javascript const MyComponent = () => { return ( <div> <h1>Hello, World!</h1> </div> ); }; ``` 在這段代碼中,`<div>` 和 `<h1>` 看起來像 HTML,但實際上是 JSX,最終會被轉譯成純 JavaScript。 ### Props **Props**(屬性)是用來將數據從父組件傳遞到子組件的方式。子組件可以通過 `props` 參數來接收父組件傳遞的數據: ```javascript const Greeting = (props) => { return <h1>Hello, {props.name}!</h1>; }; // 使用方式 <Greeting name="Alice" /> ``` 在這個範例中,`Greeting` 組件接收 `name` 作為 prop,並將其顯示在畫面上。 ### State **State** 是 React 中用來儲存組件內部資料的物件。與 `props` 不同,`state` 是由組件自己管理的,而且可以在組件的生命週期中改變。 在函數組件中,我們通常使用 `useState` Hook 來管理組件的狀態。 ### 事件處理 React 使用與標準 HTML 類似的語法來處理事件,但不同的是 React 事件名稱採用小駝峰命名法(camelCase),而且事件處理函數通常會通過箭頭函數來定義: ```javascript const Button = () => { const handleClick = () => { alert('Button clicked!'); }; return <button onClick={handleClick}>Click Me</button>; }; ``` ## Hooks 介紹 **Hooks** 是 React 16.8 引入的新特性,它們允許我們在函數組件中使用狀態和其他 React 特性,無需使用類組件。以下是幾個常用的 Hooks: ### useState `useState` 是用來在函數組件中引入狀態的 Hook。它返回一個狀態值和一個更新該狀態的函數: ```javascript const Counter = () => { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increase</button> </div> ); }; ``` 在這個範例中,`useState(0)` 初始化了 `count` 狀態,並提供了 `setCount` 函數來更新 `count` 的值。 ### useEffect `useEffect` 是一個處理副作用的 Hook,副作用是指那些與渲染無直接關係但需要在渲染後執行的操作,例如資料獲取、訂閱、或手動操作 DOM。 ```javascript const DataFetcher = () => { const [data, setData] = useState(null); useEffect(() => { fetch('/api/data') .then(response => response.json()) .then(result => setData(result)); }, []); // 空陣列表示這個 effect 只在組件掛載和卸載時執行一次 return <div>{data ? `Data: ${data}` : 'Loading...'}</div>; }; ``` ## 一個簡單的範例 以下是結合多個概念的簡單範例: ```javascript import React, { useState, useEffect } from 'react'; const App = () => { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> </div> ); }; export default App; ``` 在這個範例中,我們使用了 `useState` 來管理 `count` 狀態,並使用 `useEffect` 在 `count` 每次變化後更新網頁的標題。 ## 總結 React 是一個強大的工具,通過其組件化和聲明式編程的特性,可以幫助我們更有效地構建現代化的網頁應用程式。Hooks 提供了一種更自然的方式來在函數組件中處理狀態和副作用,使得我們可以更加靈活地編寫 React 組件。希望這一章節能幫助大家對 React 有一個基本的了解,並為後續的項目開發打下基礎。 # Next.js 簡介 ## Next.js 是什麼? **Next.js** 是一個基於 React 的full stack框架,它提供了伺服器端渲染(SSR)、靜態網站生成(SSG)、和 API 路由等功能,讓我們能夠輕鬆地構建現代化的網頁應用程式。Next.js 的特點包括: - **零配置:** 內建強大的預設配置,使開發者可以專注於業務邏輯。 - **SEO 友好:** 透過伺服器端渲染和靜態生成來優化搜索引擎排名。 - **Full Stack能力:** 內建 API 路由功能,可以直接在同一框架內開發後端 API。 ## Next.js 的核心概念 ### App Router vs Page Router 在 Next.js 中,有兩種主要的路由系統:**Page Router** 和 **App Router**。隨著 Next.js 的發展,**App Router** 是下一代的路由系統,提供了更靈活的功能與改進的架構。因此,我們將在項目中使用 **App Router**。 #### Page Router - 傳統的基於檔案的路由系統。 - 每個頁面對應一個檔案,位於 `pages/` 目錄下。 - 自動處理路由。 #### App Router - 更加模組化的設計,提升了組件重用性。 - 透過 `app/` 目錄組織路由與頁面。 - 支持伺服器組件與客戶端組件的混合使用。 ### 文件結構 使用 **App Router** 的專案通常具有以下文件結構: ``` my-nextjs-app/ │ ├── app/ │ ├── layout.tsx │ ├── page.tsx │ ├── head.tsx │ ├── api/ │ │ └── hello/route.ts │ └── about/ │ ├── page.tsx │ └── head.tsx │ ├── components/ │ └── navbar.tsx │ ├── public/ │ └── images/ │ ├── styles/ │ └── globals.css │ ├── next.config.js ├── tsconfig.json └── package.json ``` - **app/**:這是新的根目錄,所有路由、頁面、API 和組件都在這裡定義。 - **layout.tsx**:定義共享的頁面佈局,適用於所有子路由。 - **page.tsx**:對應於 `/` 路徑的頁面。 - **api/**:API 路由在此目錄中定義,可以處理後端邏輯。 - **components/**:存放可重用的 UI 組件。 - **public/**:存放靜態資源,如圖片、字體等。 ### 路由系統 在 **App Router** 中,每個文件夾都可以代表一個路由,並且 `page.tsx` 文件代表該路由的默認頁面。例如: - `app/page.tsx` 對應於 `/` 路徑。 - `app/about/page.tsx` 對應於 `/about` 路徑。 Next.js 會自動將文件夾名稱映射為路由,而我們只需關注如何構建這些頁面組件。 ### 伺服器組件與客戶端組件 在 Next.js 中,你可以將組件定義為 **伺服器組件(Server Components)** 或 **客戶端組件(Client Components)**。 - **伺服器組件:** 預設情況下,所有組件都是伺服器組件,它們在伺服器上執行並將結果渲染為 HTML 傳送至客戶端。伺服器組件可以處理資料庫請求和敏感操作,並且沒有增加客戶端的 JavaScript 負擔。 - **客戶端組件:** 如果一個組件需要在瀏覽器中執行(例如,它需要處理狀態或事件),則需要顯式地聲明為客戶端組件。可以使用 `use client` 指令來標記它: ```javascript // 在文件的最上方添加 `use client` 來標記這是一個客戶端組件 'use client'; import React, { useState } from 'react'; const Counter = () => { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increase</button> </div> ); }; export default Counter; ``` ### 資料抓取(Data Fetching) Next.js 提供了多種資料抓取的方式,可以根據需要選擇在伺服器端或客戶端進行資料抓取。主要的抓取方式包括: - **伺服器端抓取:** 使用 `getServerSideProps`(Page Router)或直接在伺服器組件中進行資料抓取。這種方式適合需要每次請求都獲取最新資料的場景。 - **靜態生成:** 使用 `getStaticProps`(Page Router)或在伺服器組件中靜態生成內容。適合資料不常變動的頁面。 - **客戶端抓取:** 使用 `useEffect` 或其他資料抓取工具(例如 SWR 或 React Query)在客戶端進行資料抓取,適合需要在客戶端進行互動的場景。 ## 使用範例 以下是一個使用 **App Router** 的簡單範例: ```typescript // app/layout.tsx import './globals.css'; export default function RootLayout({ children }) { return ( <html lang="en"> <head /> <body> <header> <nav> {/* 導航欄 */} </nav> </header> <main>{children}</main> </body> </html> ); } ``` ```typescript // app/page.tsx export default function Home() { return ( <div> <h1>Welcome to Next.js App Router!</h1> <p>This is the home page.</p> </div> ); } ``` ```typescript // app/about/page.tsx export default function About() { return ( <div> <h1>About Us</h1> <p>This is the about page.</p> </div> ); } ``` ```typescript // app/api/hello/route.ts export async function GET(request: Request) { return new Response(JSON.stringify({ message: 'Hello, World!' }), { status: 200, }); } ``` ## 總結 Next.js 是一個強大的Full Stack框架,通過其 App Router 系統和伺服器組件的支持,開發者可以輕鬆地構建高性能且靈活的網頁應用程式。透過這些功能,我們可以將前端和後端邏輯無縫地結合在一起,並且優化了應用程式的 SEO 和用戶體驗。 希望這一章節能幫助大家更好地理解 Next.js,並為我們的項目開發奠定基礎。如果有任何問題,請隨時討論! # TypeScript 簡介 ## JavaScript 基本概念 在學習 TypeScript 之前,先了解一些 JavaScript 的基本概念會很有幫助,因為 TypeScript 是基於 JavaScript 之上的語言擴展。 ### 變數與類型 在 JavaScript 中,我們使用 `var`、`let` 或 `const` 來宣告變數: ```javascript var name = "Alice"; // 使用 var 宣告 let age = 25; // 使用 let 宣告 const isStudent = true; // 使用 const 宣告 ``` - `var`:功能最基本的變數宣告方式,作用域是函數級別。 - `let`:作用域是區塊級別,避免了 `var` 的一些陷阱。 - `const`:用來宣告常數,一旦賦值後不能重新賦值。 ### 函數 JavaScript 中的函數可以用多種方式來定義: ```javascript function add(a, b) { return a + b; } const subtract = (a, b) => { return a - b; }; ``` - `function` 關鍵字定義的函數:最傳統的函數定義方式。 - 箭頭函數(Arrow Function):語法更簡潔,且不綁定 `this`。 ### 物件與陣列 物件(Object)和陣列(Array)是 JavaScript 中最常見的資料結構: ```javascript const person = { name: "Alice", age: 25, isStudent: true, }; const numbers = [1, 2, 3, 4, 5]; ``` - 物件使用 `{}` 包裹,內部是 `key-value` 對。 - 陣列使用 `[]` 包裹,內部是有序的元素集合。 ## TypeScript 是什麼? **TypeScript** 是 JavaScript 的超集,它擴展了 JavaScript,增加了靜態類型(Static Type)和其他許多功能。TypeScript 由 Microsoft 開發,目的是讓 JavaScript 的開發更安全、更高效。所有合法的 JavaScript 代碼在 TypeScript 中都是合法的。 ## TypeScript 的核心特性 ### 靜態類型檢查 TypeScript 的核心是靜態類型檢查,這意味著你在編寫代碼時,可以提前捕捉到很多錯誤,而不是等到運行時才發現。 ```typescript let username: string = "Alice"; let age: number = 25; let isStudent: boolean = true; ``` 在這裡,我們顯式地定義了變數的類型,這樣 TypeScript 就能夠在編譯時期檢查是否有類型錯誤。 ### 型別註釋 TypeScript 允許我們為變數、函數參數和返回值等加上型別註釋: ```typescript function greet(name: string): string { return "Hello, " + name; } ``` 在這個例子中,我們指定了 `name` 是一個 `string`,而 `greet` 函數的返回值也是一個 `string`。這樣,如果你不小心傳入了非 `string` 類型的參數,TypeScript 會報錯。 ### 介面(Interfaces) 介面是 TypeScript 中用來定義物件結構的一種方式,它讓代碼更加嚴謹且可讀性更強: ```typescript interface Person { name: string; age: number; isStudent?: boolean; // 使用 `?` 表示這個屬性是可選的 } const alice: Person = { name: "Alice", age: 25, }; ``` 介面可以定義物件應該具備的屬性,這樣當我們使用這些物件時,就能確保它們符合預期的結構。 ### 類別(Classes) TypeScript 支持面向對象編程(OOP),包括類別和繼承等概念: ```typescript class Student { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet(): string { return `Hello, my name is ${this.name}`; } } const student = new Student("Alice", 25); console.log(student.greet()); ``` 在這個例子中,我們定義了一個 `Student` 類別,其中包含一個建構函數和一個方法。TypeScript 會確保我們按照類別定義來使用它。 ### 模組(Modules) TypeScript 支持模組化開發,允許我們將代碼分割到不同的文件中,並通過 `import` 和 `export` 來共享功能: ```typescript // utils.ts export function add(a: number, b: number): number { return a + b; } // main.ts import { add } from './utils'; console.log(add(2, 3)); ``` 模組化使得代碼更容易維護和重用。 ## TypeScript 的優勢 1. **提高開發效率:** 通過類型檢查可以提前發現錯誤,減少調試時間。 2. **增強可維護性:** 類型系統使得代碼更容易理解,特別是在大型項目中。 3. **豐富的工具支持:** TypeScript 與編輯器和 IDE 整合得非常好,提供強大的自動補全和即時錯誤提示。 ## TypeScript 的使用範例 以下是一個簡單的 TypeScript 範例,展示了類別、介面和型別註釋的應用: ```typescript interface Animal { name: string; makeSound(): void; } class Dog implements Animal { name: string; constructor(name: string) { this.name = name; } makeSound(): void { console.log("Woof! Woof!"); } } const myDog: Dog = new Dog("Buddy"); myDog.makeSound(); // 輸出:Woof! Woof! ``` 在這個例子中,我們定義了一個 `Animal` 介面和一個 `Dog` 類別,`Dog` 類別實現了 `Animal` 介面,並且按照規定實現了 `makeSound` 方法。 ## 總結 TypeScript 是一個強大的工具,特別適合開發大型應用程式或需要高可維護性的項目。它基於 JavaScript 並向其添加了靜態類型系統,這使得代碼更安全、更可靠。在本章節中,我們介紹了 JavaScript 的基本概念以及 TypeScript 的核心特性,希望這能幫助大家更好地理解並開始使用 TypeScript 來編寫更高質量的代碼。 如果有任何問題或需要更詳細的解釋,歡迎隨時討論! # Prisma 簡介 ## Prisma 是什麼? **Prisma** 是一個現代化的 ORM(物件關聯映射)工具,幫助開發者簡化與資料庫的交互。它支援多種資料庫(如 MySQL、PostgreSQL、SQLite 等),並且能夠自動生成與資料庫結構對應的 TypeScript 類型。這樣可以確保資料庫操作在編譯時期就能捕捉到潛在錯誤,並提供良好的自動補全功能。 ## Prisma 的核心概念 ### Prisma Schema **Prisma Schema** 是用來定義資料庫結構的核心文件。它使用一種簡單直觀的 DSL(Domain Specific Language)來描述資料庫的表結構、關聯以及資料來源。Schema 文件通常命名為 `schema.prisma`,其中包括三個主要部分: - **Datasource**: 定義資料庫的連接資訊,例如資料庫的類型和 URL。 - **Generator**: 定義生成 Prisma Client 的設定。 - **Models**: 定義資料庫表和其結構。 ```prisma // schema.prisma datasource db { provider = "mysql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model User { id Int @id @default(autoincrement()) name String email String @unique posts Post[] } model Post { id Int @id @default(autoincrement()) title String content String? author User? @relation(fields: [authorId], references: [id]) authorId Int? } ``` ### Prisma Client **Prisma Client** 是 Prisma 自動生成的類型安全的查詢建構工具。它允許你使用 JavaScript 或 TypeScript 來進行資料庫操作,並且提供了自動補全、類型檢查等功能。 Prisma Client 是根據 `schema.prisma` 文件中的模型定義來生成的,並且可以通過 `prisma generate` 指令生成。 ### Prisma Migrate **Prisma Migrate** 是一個遷移工具,幫助你管理資料庫模式的變更。當你修改 `schema.prisma` 文件後,可以使用 Prisma Migrate 來生成和應用遷移,確保資料庫的結構與代碼中的模型保持一致。 ### Prisma Studio **Prisma Studio** 是一個可視化工具,用於在瀏覽器中查看和管理資料庫中的資料。它提供了一個簡單的圖形界面,讓你可以輕鬆地檢視和編輯資料表中的記錄。 ## Prisma 的優勢 1. **類型安全**: Prisma Client 生成的代碼是完全類型安全的,這意味著你可以享受到 TypeScript 提供的所有優勢,包括自動補全和編譯期的錯誤檢查。 2. **高效開發**: 使用 Prisma 可以大幅度減少與資料庫互動時所需的樣板代碼,專注於業務邏輯的實現。 3. **簡單直觀**: Prisma 的 DSL 語法簡單易懂,讓定義和管理資料庫變得更加直觀。 4. **多資料庫支持**: Prisma 支持多種資料庫,且切換資料庫非常方便。 ## 如何在項目中使用 Prisma ### 安裝與配置 要在項目中使用 Prisma,首先需要安裝相關依賴: ```bash npm install @prisma/client npm install --save-dev prisma ``` 接著,初始化 Prisma: ```bash npx prisma init ``` 這會在項目的根目錄下生成一個 `prisma` 文件夾,其中包含 `schema.prisma` 文件。 ### 定義數據模型 在 `schema.prisma` 文件中定義數據模型。例如,我們可以為一個博客系統定義 `User` 和 `Post` 兩個模型: ```prisma model User { id Int @id @default(autoincrement()) name String email String @unique posts Post[] } model Post { id Int @id @default(autoincrement()) title String content String? author User? @relation(fields: [authorId], references: [id]) authorId Int? } ``` ### 運行遷移 當你定義好模型後,使用以下指令來創建資料庫遷移並應用它: ```bash npx prisma migrate dev --name init ``` 這會創建並應用一個名為 `init` 的遷移,將 `schema.prisma` 中定義的結構映射到資料庫中。 ### 使用 Prisma Client 生成 Prisma Client: ```bash npx prisma generate ``` 生成後,你可以在代碼中使用它來進行資料庫操作: ```typescript import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); async function main() { const newUser = await prisma.user.create({ data: { name: "Alice", email: "alice@example.com", }, }); console.log(newUser); } main() .catch(e => { throw e; }) .finally(async () => { await prisma.$disconnect(); }); ``` 在這個範例中,我們使用 `prisma.user.create` 來向 `User` 表中插入一條新的記錄。 ## 一個簡單的使用範例 以下是一個完整的範例,展示如何使用 Prisma 來進行資料庫操作: ```typescript import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); async function main() { // 創建一個新用戶 const user = await prisma.user.create({ data: { name: 'Bob', email: 'bob@example.com', posts: { create: { title: 'My first post', content: 'Hello world!', }, }, }, }); // 查詢所有用戶及其文章 const users = await prisma.user.findMany({ include: { posts: true, }, }); console.log(users); } main() .catch(e => console.error(e)) .finally(async () => { await prisma.$disconnect(); }); ``` 在這個範例中,我們首先創建了一個 `User` 和一篇 `Post`,然後查詢並列出所有用戶及其相關的文章。 ## 總結 Prisma 是一個功能強大的 ORM 工具,特別適合與 TypeScript 配合使用。它簡化了資料庫操作,並提供了類型安全的保證,使得開發過程更加高效且更少錯誤。在本章節中,我們介紹了 Prisma 的基本概念、核心功能及其優勢,並通過範例展示了如何在項目中使用 Prisma。 希望這個介紹能幫助大家更好地理解 Prisma,並快速在項目中應用。如果有任何問題,歡迎討論! # Docker 簡介 ## Docker 是什麼? **Docker** 是一個開源的平台,用於自動化應用程式的部署、擴展和管理。它允許開發者將應用程式及其所有依賴項打包成一個標準化的單位,稱為 **容器(Container)**,這樣無論在任何環境中都可以確保應用程式的一致運行。 ## 為什麼使用 Docker? 使用 Docker 的好處包括: - **一致性:** 開發、測試和生產環境中的一致性,避免了「在我電腦上可以跑」的問題。 - **可移植性:** 容器可以在任何支援 Docker 的平台上運行,無需擔心底層環境的差異。 - **隔離性:** 每個容器都是獨立的,這意味著不同應用程式之間互不干擾,並且可以避免依賴衝突。 - **輕量化:** 與虛擬機器相比,容器更輕量級,啟動速度更快,資源佔用更少。 ## Docker 的基本概念 ### 容器(Containers) **容器** 是一個輕量級且獨立的執行環境,它包含了應用程式運行所需的所有環境,如代碼、運行時、系統工具、系統庫等。容器可以看作是一個精簡版的虛擬機器,並且它們共享宿主機的操作系統內核。 ### 映像檔(Images) **映像檔** 是一個只讀的範本,用來創建 Docker 容器。每個映像檔可以包含應用程式所需的操作系統、軟體、環境變數等配置。映像檔通常是通過 **Dockerfile** 來構建的。 ### Dockerfile **Dockerfile** 是一個文本文件,其中包含了一系列指令來組建一個 Docker 映像檔。每一條指令都會在一個新的映像層中執行,最終生成一個完整的映像檔。 以下是一個簡單的 Dockerfile 範例,這個文件用於構建一個 Node.js 應用的映像檔: ```dockerfile # 使用官方 Node.js 映像檔作為基礎 FROM node:16 # 設置工作目錄 WORKDIR /app # 複製 package.json 和 package-lock.json COPY package*.json ./ # 安裝應用程式依賴 RUN npm install # 複製應用程式代碼 COPY . . # 暴露應用埠 EXPOSE 3000 # 啟動應用程式 CMD ["npm", "start"] ``` ### Docker Compose **Docker Compose** 是一個用來定義和運行多容器 Docker 應用的工具。使用 Docker Compose,我們可以在一個 `docker-compose.yml` 文件中定義應用程式的服務、網絡、和卷(volumes),然後使用一條簡單的指令來啟動整個應用。 ## 使用 Docker 部署應用 使用 Docker 部署應用程式非常簡單,只需幾個步驟: 1. **創建 Dockerfile**:定義應用程式的映像檔結構。 2. **構建映像檔**:使用 `docker build` 指令來構建映像檔。 3. **運行容器**:使用 `docker run` 指令來基於映像檔運行容器。 例如,構建並運行一個 Node.js 應用: ```bash docker build -t my-node-app . docker run -p 3000:3000 my-node-app ``` 這將在本地端口 3000 上啟動應用程式。 ## 使用 Docker Compose 啟動本地 MySQL 資料庫 在本地開發環境中,我們可以使用 Docker Compose 來輕鬆啟動一個 MySQL 資料庫。以下是 `docker-compose.yml` 的範例: ```yaml version: '3.8' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: example MYSQL_DATABASE: mydatabase MYSQL_USER: user MYSQL_PASSWORD: password ports: - "3306:3306" volumes: - db_data:/var/lib/mysql volumes: db_data: ``` 這個 `docker-compose.yml` 文件定義了一個名為 `db` 的服務,它會啟動一個 MySQL 8.0 容器,並在本地的 3306 端口上暴露該服務。 要啟動 MySQL 資料庫,只需在項目的根目錄下執行以下指令: ```bash docker-compose up -d ``` 這將在背景中啟動 MySQL 容器,你可以使用 `docker-compose down` 指令來停止並刪除容器。 ## 總結 Docker 是一個強大且靈活的工具,能夠大大簡化應用程式的部署和管理過程。通過 Docker,我們可以確保應用程式在任何環境下都能一致運行,並且可以輕鬆地在本地設置開發環境,例如啟動一個 MySQL 資料庫。希望通過這一章節的介紹,大家能夠對 Docker 有一個基本的了解,並能夠在開發過程中有效地應用 Docker 來提高效率。如果有任何問題或需要進一步的討論,隨時歡迎提問!