Table of Contents
MarkDown
常用語法查表
-
文字
| 說明 | Markdown語法 |
|---|---|
標題大小 |
# |
| 粗體字 | ** 粗體字 ** |
| 斜體字 | * 斜體字 * |
-
表格
-
對齊方式
-
置左 置中 置右 測試左邊 測試中間 測試右邊 -
使用程式碼區塊
-
圖片
-
連結
常用指令
-
查看有哪些主題可以選擇
python :root
mkdocs build --help | grep theme
-
新增專案
-
新增文件
-
建立靜態網站
注意事項
使用MarkDown時應特別注意Tab及空白鍵的使用時機
在 MkDocs 中,Markdown 語法會被轉換為對應的 HTML 標籤,然後這些 HTML 標籤可以被 CSS 選擇器選中並應用樣式。以下是一些常見的 Markdown 語法及其對應的 HTML 標籤和 CSS 選擇器:
粗體:Markdown 中的粗體語法(粗體文字 或 粗體文字)會被轉換為 標籤。在 CSS 中,您可以使用 strong 選擇器來選擇這些元素。
斜體:Markdown 中的斜體語法(斜體文字 或 斜體文字)會被轉換為 標籤。在 CSS 中,您可以使用 em 選擇器來選擇這些元素。
刪除線:Markdown 中的刪除線語法(~~刪除線文字~~)會被轉換為 標籤。在 CSS 中,您可以使用 del 選擇器來選擇這些元素。
代碼:Markdown 中的代碼語法(代碼文字)會被轉換為 標籤。在 CSS 中,您可以使用 code 選擇器來選擇這些元素。
鏈接:Markdown 中的鏈接語法(鏈接文字)會被轉換為 標籤。在 CSS 中,您可以使用 a 選擇器來選擇這些元素。
圖片:Markdown 中的圖片語法(
)會被轉換為 標籤。在 CSS 中,您可以使用 img 選擇器來選擇這些元素。
請注意,這些都是基本的選擇器,您也可以使用更複雜的選擇器來選擇特定的元素,例如使用類別選擇器(.class-name)、ID 選擇器(#id-name)或屬性選擇器([attribute="value"])。
MkDocs & MarkDown 入門
建制開發環境
安裝Python
- 安裝Python

Note
穩定版發行版(建議安裝)
通常已經用於生產且經過測試並修復已知的BUG
還在開發中的版本
可能包含新功能,但也可能包含錯誤或不穩定的代碼
下載完後檢查版本 在終端機輸入
不想多打一個3的話需要利用終端機執行下列命令將 python 指向 python3 的符號鏈接
安裝MkDocs 及 Material樣式
實作
新增專案
新增專案名稱為 my-project
新增後會生成這樣的檔案
my-project #專案資料夾
docs #
index.md #主畫面
mkdocs.yml #配置檔
-
啟動服務
會看到預設頁面
- 修改mkdocs.yml(配置檔)
配置檔
site_name:標題title
nav:在左邊側欄增加頁面
theme:
- name:主題樣式
- 修改後會如下圖
-
新增子頁
使用touch指令在專案中建立子頁面(需與docs建立在相同目錄下 )
在mkdocs.yml中新增nav- Home 為頁面顯示的文字 : home.md為路徑
頁面顯示 Home , About , Contact 路徑
- 以上是環境建置概略說明
Git
使用git操作 將專案部署至gitlab
1.建立新專案
2.建立金鑰與gitlab通訊
2-1.建立金鑰
- ”your_email@example.com" 替換為自己的email
這個指令會在電腦生成 RSA SSH 金鑰 可以跟gitlab進行安全通訊
2-2.與gitlab通訊
複製公鑰
這樣本地端電腦即可與gitlab安全通訊
2-3. 在本地開始專案
開啟終端機 輸入 git clone " 複製剛才 1-4.的 ssh"
開啟後會看到gitlab預設的readme檔案
接著就可以開始編輯專案了

2-4 建立CI/CD自動部署
新增.gitlab-ci.yml檔
將以下內容複製至.gitlab-ci.yml用來在gitlab自動部署
images/imageage: python:3.9
stages:
- deploy
- pages
deploy_job:
stage: deploy
script:
- pip install mkdocs
- pip install mkdocs-material
- mkdocs build
- mkdir .public
- cp -r site/* .public
- ls -la .public # 列出 .public 目錄的內容
- mv .public public
artifacts:
paths:
- public
pages:
stage: pages
script:
- echo "Using public directory for pages"
- ls -la public # 列出 public 目錄的內容
dependencies:
- deploy_job
artifacts:
paths:
- public
only:
- main
3.部署至gitlab
3-1 add.暫存
編輯完成後 將檔案加到暫存區
3-2 commit 新增節點
將暫存區提交到節點上
3-3 push 部署至gitlab
將本地節點部署至gitlab
失敗的話會是
可以點進去看哪裡fail或是從Build -> jobs查看

需要回去再修改問題,問題修復後需要再做一次 3-1~3-3的步驟
git add "修改的檔案" #將修改過的檔案暫存
git commit -m "對檔案做了什麼" #建議有修改或新增時都應該在commit時輸入以方便紀錄
git push origin main #部署至遠端主機
點網址連結進去就可以看到網站部署成功
或修改以下 username -> gitlab帳號及 projectname -> 專案名稱
淺談Git分佈式版本控制
Git 介紹
Git是一種分佈式版本控制系統,強大的地方在於可以靈活的分支跟合併功能,開發者的本地端及遠端都有一個完整的版本庫(Repository)。 可以回朔歷史或是在分支開發測試功能,在不污染主線 (1) 的同時測試功能。Git目前是最流行且強大的版本控制系統
-
主線 origin/main 通常指已上線版,當主線需要新增功能時,通常會先在支線測試,等確認沒問題才會部署至主線上
優點
- 分佈式架構:每個開發者的工作站上都有一個完整的版本庫,這使得開發者可以在離線或網絡連接不佳的情況下工作。
- 強大的分支和合併功能:Git 提供了一種簡單而強大的方式來創建和合併分支。這使得開發者可以在不同的分支上進行平行的開發工作,並輕鬆地將他們的更改合併到一起。
缺點
Warning
某些Git指令是能夠強制執行或覆蓋,可能會導致遠端的歷史被改寫 (1)
-
Danger
git push --force 是一個 Git 命令 用於強制將本地的更改推送到遠端倉庫 即使這會導致遠端倉庫的歷史被重寫
指令介紹
-
Note
Clone :將整個專案從遠端複製至本機 通常是第一次參與專案時才會使用
Pull :將專案最新的更改合併到本機端 (1)
Add :在本地端更改的檔案暫存後準備提交 (2)
Commit :將暫存的更改提交到本地端當前的分支 搭配 -m "輸入提交時顯示的訊息"
Push :將本地端推送到遠端
Merge :將兩個或多個分支合併-
Note
除了第一次使用Clone 後面都使用Pull 如果專案很大很肥的話用Clone效率不高
-
Note
git push 和 git push origin main 兩個命令的主要差別在於它們推送變更到的遠程分支。
git push:這個命令會將當前分支的變更推送到設定為跟蹤的遠程分支。如果當前分支沒有設定跟蹤的遠程分支,則會根據 Git 的 push.default 配置來決定要推送到哪個遠程分支。
git push origin main:這個命令會將變更推送到 origin 遠程的 main 分支,不論當前分支是什麼。origin 通常是你的遠程存儲庫的默認名稱,main 是一個常見的主分支名稱。
總的來說,如果你想要將變更推送到特定的遠程分支,你應該使用 git push <remote> <branch> 命令。如果你只是想要將變更推送到當前分支的跟蹤分支,你可以使用 git push 命令。
myenv) zoxul@zoxuldeMacBook-Pro markdown % git log
commit 0a0b87d92c149d444cf79be8664861083db29962 (HEAD -> user1_test, origin/user1_test)
Author: zoxulgit <zoxulyoshino@gmail.com>
Date: Mon Mar 25 14:58:22 2024 +0800
test1merge requesr
commit 18f3eda54696ca6ae1345434922bb4c56d2ccb3b (origin/main, main)
Author: zoxulgit <zoxulyoshino@gmail.com>
:
目前在
(myenv) zoxul@zoxuldeMacBook-Pro markdown % git branch
main
* user1_test
(myenv) zoxul@zoxuldeMacBook-Pro markdown %
查看當前分支名稱: git rev-parse --abbrev-ref HEAD
修改最近一次提交的內容(訊息)
test merge user1
模擬兩個branch local
Phasellus posuere in sem ut cursus
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'dark', 'gitGraph': {'mainBranchName': "local",'mainBranchOrder': 2 } }}%%
gitGraph TB :
commit id:"init"
commit id:"new"
branch bug_fix order:1
branch feature order:3
checkout bug_fix
commit tag: "1" id:"highlight" type:highlight
checkout local
merge bug_fix
checkout feature
commit tag: "2" id:"reverse" type:reverse
checkout local
merge feature
commit tag:"3" id:"normal" type:normal
```
git 基礎指令
分支管理
git branch
新增分支 bug_fix

輸入
git branch 可以查看所有的branch
先在bug_fix 暫存 -> 提交 -> 推送
意思是說我要推送bug_fix這個分支到遠端
git checkout (switch)
切換到bug_fix分支
將檔案回到上一次提交點
在Git2.23版本中新增 git switch 同樣用於切換分支
切換到 bug_fix分支
git merge
在main的分支做 git merge hot_fix 將hot_fix 與 main 合併成一個新的commit接在main後面
git rebase
git rebase簡單來說是將整個分支所做的更動一個一個移動到main的後面
假設我現在gitlab長這樣
將hot_fix分支上的commit移動到main後面,在hot_fix分支使用
git rebase 與 git merge的差異
兩者實現的功能是差不多的,都是將一個分支合併到另一個分支
最大的不同就是merge不會改變到歷史而rebase會改變
所以rebase最好是在本地端使用就好不要push到遠端
git stash
將現在分支暫存,處理別的分支
因為檔案未追蹤 所以需要先add
可以先將所有檔案暫存
回到main 新增commit
查看stash暫存的列表
會顯示最後一次commit的訊息及分支名稱
回到bug_fix分支 恢復剛才的工作
git cherry-pick
可以單獨將分支上的某個commit接到現在分支的後面
現在的分支
在main分支使用git cherry-pick將add file6 接在main後面
每個commit都有自己的獨一無二哈希值 所以在git cherry-pick時要指定add file6的哈希值
可以看到main log 將add file6 接在最後面
gitlab
與遠端通訊
remote
在gitlab新增專案 複製ssh
在本地端建立一個名字為 testremote 的遠端儲存庫參考 後面是專案連結
可以看到有兩個遠端儲存庫
git fetch
將遠端儲存庫拉下來但是不與本地端合併 可以查看遠端的提交或更改等log
查看testremote 的 log
git pull 將遠端除存庫的變更合併到本地除存庫

Milestones
Milestones
里程碑(Milestones)是一種用來追蹤項目進度的工具
- 現有專案的樣子
Issuse
- issuse看板 可以追蹤issuse進度
- 新增Bug Doing ToDo-list看板
- 新增issuse
- issuse board
- 待辦清單 (to-do list)
- 新增功能後 申請合併請求(merge requests)
- 新增合併請求 -1
- 新增合併請求 -2
- codereview
系統架構 : Angular(前端) http rest api -> Spring Boot(Backend) ->MQ Angular(前端) http rest api -> Spring Boot(Backend) ->VA Server Angular(前端) http rest api -> Spring Boot(Backend) ->SFTP Angular(前端) http rest api -> Spring Boot(Backend) ->DB2 Angular(前端) http rest api -> Spring Boot(Backend) ->難字系統
系統功能模組:
共用component
contact-tel.component
功能說明: 提供區碼、電話號碼及分機的輸入與驗證功能
table.component
功能說明: 提供資料列表示、排序、分頁控制,以及檢視、編輯、刪除等操作按鈕功能
dialog.component
功能說明: 提供對話框功能,包含標題、內容及多種輸入與操作按鈕
difficult-words.component
功能說明: 根據字體陣列動態設置字體顯示
e-paginator.component
功能說明: 提供分頁功能,包含頁碼選擇和頁面大小設置
keyboard.component
功能說明: 提供難字輸入的專用鍵盤,支持注音和倉頡兩種輸入法,並可進行分頁搜尋
loading.component
功能說明: 提供一個旋轉的進度條,用於顯示加載中的狀態
共用 Directives
taiwan-id.directive
功能說明: 檢核台灣身分證字號的合法性
共用 Pipes
mapper.pipe
功能說明: 提供將值映射(mapping)到對應描述的功能,通常用於將代碼轉換為人類可讀的描述。
number-formatter.pipe
功能說明: 提供數字格式化功能,將數字轉換為千分位格式,並可選擇性地添加單位。
truncate.pipe
功能說明: 提供文字截斷功能,可設定最大字數並添加省略符號。
共用 Service
alert.service
功能說明: 提供彈出式通知(alert)功能,用於在應用程式中顯示各種提示訊息,如成功、錯誤、警告等。
certificate.service
功能說明: 提供證書管理功能,包括證書的生成、驗證及其他相關操作。
dialog.service
功能說明: 提供對話框(dialog)管理功能,用於顯示和操作應用程式中的對話框。
load-font.service
功能說明: 提供字體加載功能,用於動態加載應用程式所需的字體資源。
sys-info.service
功能說明: 提供系統信息管理功能,用於獲取和處理應用程式運行環境的各種信息。
validate.service
功能說明: 提供數據驗證功能,用於對應用程式中的輸入數據進行各種驗證操作。
各個主要功能模組及其子模組。例如:用戶管理、產品管理、訂單處理、支付系統等。 模組之間的關係:
各模組之間如何互動和通信。 主要的頁面和元件:
各頁面的具體功能和它們所需的主要元件。 狀態管理:
是否使用 NgRx 或其他狀態管理工具,如果有,需要描述狀態管理的策略。 路由設計:
phase2/各自component/ phase2/各自component/各自component-detail ex: legacy2/f020 legacy2/f020/f020-detail
主要路由和子路由結構。 共用服務: alert service dialog service (pop up 提示框) validate service
需要的共用服務,如 HTTP 服務、驗證服務、數據服務等。 UI 設計和樣式: Angular Material
是否使用特定的 UI 框架或樣式庫,如 Angular Material、Bootstrap。 第三方庫和插件:
需要整合的第三方庫或插件。
imports: [ CommonModule, Phase2fRoutingModule, FrameworkModule, MatFormFieldModule, MatInputModule, MatTableModule, ReactiveFormsModule, MatDatepickerModule, MatNativeDateModule, MatButtonModule, MatSelectModule, MatCheckboxModule, MatRadioModule,
],
TypeSctipt
基礎
基本變數
let str :string = "string"
let num :number = 123
let boo :boolean = true
let str2 ="string"
let num2 = 123
let boo2 = true
let arr1 :string[]= ["1","2","3","4","5"];
let arr2 :number[][]=[[1,2],[3,4],[5,6],[7,8],[9,10]];
let tuple: [string, number, boolean]=["Hello", 1, true]; //元祖的元素與型別是固定的
let tuple2:[[string,string],[number,number]]=[["Hello","World"],[1,2]];
any
let any :any //不確定型別時,any可以被賦值任何型別
any = 123
any = '123'
any = true
any = null
any = undefined
any = {name:'123'}
any = [1,2,3,4,5]
Danger
- 避免過度使用
any失去TS的型別檢查的優點 - 使用
unknown代替any
null
let data: string | null = "Hello, world!";
console.log(data); // 輸出 "Hello, world!"
data = null;
console.log(data); // 輸出 "null"
undefine
let data: string | undefined;
console.log(data); // 輸出 "undefined"
data = "Hello, world!";
console.log(data); // 輸出 "Hello, world!"
data = undefined;
console.log(data); // 輸出 "undefined"
Note
- null :是一個表示無值或無對象的特殊值。它通常用於表示變數應該沒有值或對象。
- undefine :當一個變數被宣告但未被賦值時,它的值就是 undefined。這表示變數存在,但沒有被初始化。
Enum 列舉
enum Color {
Red, //0
Green, //1
Blue //2
}
let myColor: Color = Color.Green;
console.log(myColor); // 輸出 1
enum HttpStatusCode {
Ok = 200,
BadRequest = 400,
Unauthorized = 401,
}
let myStatusCode: HttpStatusCode = HttpStatusCode.Ok;
console.log(myStatusCode); // 輸出 200
type 類型
type StringOrNumber = string | number;
let data: StringOrNumber;
data = "Hello, world!";
console.log(data); // 輸出 "Hello, world!"
data = 123;
console.log(data); // 輸出 "123"
data = true; // 錯誤:Type 'boolean' is not assignable to type 'StringOrNumber'.
interface 接口
interface User2 {
name: string;
age: number;
email: string;
}
interface User2 {
phone?: string;
}
let user2: User2 = {
name: "John",
age: 30,
email: "email@email.com",
}
type User = {
name: string;
age: number;
email: string;
}
//識別碼 'User' 重複。ts(2300)
type User = {
phont:string;
}
type UserWithPhone = User & {
phone:string;
}
let userA: UserWithPhone = {
name: "John",
age: 30,
email: "email@email.com",
phone:"0912346578"
}
修飾符
| public | private | protected | |
|---|---|---|---|
| 類別本身 | 可以 | 可以 | 可以 |
| 繼承的子類別 | 可以 | 不行 | 可以 |
| 類別的物件實例 | 可以 | 不行 | 不行 |
public 公開變數:變數宣告時預設就是public,可以在任意地方訪問
protected受保護:可以在類別本身和繼承的子類別中被訪問
private私有變數:只可以在類別本身訪問
class Person {
public name: string ;
protected email: string ;
private age: number ;
constructor(name: string, email: string, age: number) {
this.name = name;
this.email = email;
this.age = age;
console.log(this.name+" 1");
console.log(this.email+" 2");
console.log(this.age+" 3");
}
}
let user: Person = new Person("Alex", "gmail", 40);
console.log(user.name+" 4"); //Alex
//console.log(user.email); //error
//console.log(user.age); //error
class Person2 extends Person {
phone: number;
constructor(name: string, email: string, age: number, phone: number) {
super(name, email, age);
this.phone = phone;
}
}
let user2: Person2 = new Person2( "Cindy", "hotmail", 20 , 123456789);
console.log(user2.name+" 5"); //Cindy
//console.log(user2.email); //error
//console.log(user2.age); //error
console.log(user2.phone+" 6"); //123456789
輸出
泛型
- 函式
function print<Z> (data: Z): Z {
console.log(data);
return data;
}
print<string>("Hello");
print<number>(1);
- 接口
interface Printer<Z>{
(data: Z): Z;
}
const print2: Printer<string> = (data) => {
console.log(data);
return data;
}
print2("Hello");
const print3: Printer<number> = (data) => {
console.log(data);
return data;
}
print3(1);
- 類別
class DataPrinter<Z> {
Data: Z;
constructor(data: Z) {
this.Data = data;
}
}
const A = new DataPrinter<string>("Hello");
const B = new DataPrinter<number>(1);
- Union 聯合類型
- Intersection Types 交叉類型
type Name = { name: string , eat: boolean};
type Age = { age: number };
type Person = Name & Age;
let person: Person = {
name: "Alice",
age: 30,
eat: true
};
Type Guards 類型守衛
- typeof
- in
- instanceof
class MyClass {
myProp = 123;
}
class NotClass {
myProp = 123;
}
const myInstance = new MyClass();
const NotInstance = new NotClass();
if (myInstance instanceof MyClass) {
// 在這裡,myInstance 是 MyClass 類型
}
if (NotInstance instanceof MyClass) {
// 在這裡,myNotInstance 不是 MyClass 類型
} else {
console.log('myNotInstance 不是 MyClass 類型');
}
- 自訂義
type Person = { name: string; age: number; };
type Product = { name: string; price: number; };
function isPerson(x: any): x is Person {
return x.age !== undefined;
}
function process(x: Person | Product) {
if (isPerson(x)) {
console.log(`Hello, ${x.name}. You are ${x.age} years old.`);
} else {
console.log(`The product ${x.name} costs $${x.price}.`);
}
}
let person = { name: "Alice", age: 30 };
let product = { name: "Apple", price: 12 };
process(person); // Output: Hello, Alice. You are 30 years old.
process(product); // Output: The product Apple costs $1.2.
等式收縮
type Shape = { kind: 'circle', radius: number } | { kind: 'square', sideLength: number };
function getArea(shape: Shape) {
if (shape.kind === 'circle') {
// 在這裡,shape 的類型被收縮為 { kind: 'circle', radius: number }
return Math.PI * shape.radius ** 2;
} else {
// 在這裡,shape 的類型被收縮為 { kind: 'square', sideLength: number }
return shape.sideLength ** 2;
}
}
非同步
Promise
let promise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("Promise resolved"); // 在 2 秒後,Promise 狀態變為 fulfilled,並返回 "Promise resolved"
}, 2000);
});
promise.then(value => {
console.log(value); // 輸出 "Promise resolved"
}).catch(error => {
console.log(error);
});
Async/Await
let promise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("Promise resolved"); // 在 2 秒後,Promise 狀態變為 fulfilled,並返回 "Promise resolved"
}, 2000);
});
async function asyncFunction() {
try {
let value = await promise;
console.log(value); // 輸出 "Promise resolved"
} catch (error) {
console.log(error);
}
}
asyncFunction();
// 模擬一個可能需要長時間的網路請求
let networkRequest = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("Network request succeeded");
}, 10000); // 假設網路請求需要 10 秒才能完成
});
// 創建一個在 5 秒後變為 rejected 狀態的 Promise
let timeout = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject("Network request timed out");
}, 5000); // 如果網路請求在 5 秒內沒有完成,就觸發 timeout
});
// 使用 Promise.race 來自動取消超時的網路請求
Promise.race([networkRequest, timeout])
.then(value => {
console.log(value); // 如果網路請求在 5 秒內完成,則輸出 "Network request succeeded"
})
.catch(error => {
console.log(error); // 如果網路請求在 5 秒內沒有完成,則輸出 "Network request timed out"
});
//類似邏輯運算的 or
- promise.all
// 模擬兩個網路請求
let networkRequest1 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("Network request 1 succeeded");
}, 2000); // 假設第一個網路請求需要 2 秒才能完成
});
let networkRequest2 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("Network request 2 succeeded");
}, 3000); // 假設第二個網路請求需要 3 秒才能完成
});
// 使用 Promise.all 等待兩個網路請求都完成
Promise.all([networkRequest1, networkRequest2])
.then(values => {
console.log(values); // 輸出 ["Network request 1 succeeded", "Network request 2 succeeded"]
})
.catch(error => {
console.log(error); // 如果任何一個網路請求失敗,就輸出錯誤
});
//類似邏輯運算的and
Tsconfig.json
Regular Express
Regex
正規表達式
正規表達式 Regular Expression (regex & regexp)
-
是一種強大的文本處理工具,可以用來匹配、查找、替換文本中的特定模式。
-
數據驗證:您可以使用正則表達式來驗證用戶輸入的數據是否符合特定的格式,例如電子郵件地址、電話號碼、郵政編碼等。
-
搜索和替換:在文本編輯器或程式碼編輯器中,您可以使用正則表達式來進行複雜的搜索和替換操作。
-
數據提取:如果您有一大段文本,並且想從中提取特定的信息(例如從日誌文件中提取錯誤消息),您可以使用正則表達式。
-
文本分割:您可以使用正則表達式來分割文本。例如,您可以使用逗號加上任意數量的空格作為分隔符,來分割 CSV 文件。
| 符號 | 描述 | 範例 | 匹配 |
|---|---|---|---|
\ |
轉譯 | \. | . |
. |
匹配任何單個字符(除了換行符) | a.b |
"axb"、"a!b"、"a1b" 等 |
^ |
匹配字符串的開頭 | ^abc |
"abc"、"abcd",但不匹配 "xabc" |
$ |
匹配字符串的結尾 | abc$ |
"123abc"、"xyzabc", 但不匹配 "abc123" |
| 符號 | 描述 | 範例 | 匹配 |
|---|---|---|---|
\d |
匹配任何數字(等同於 [0-9]) | \d{3} |
"123"、"456" 等 |
\D |
匹配任何 非數字字符 (等同於 [^0-9]) | \D{2} |
"ab"、"!@" 等 |
\w |
匹配任何字母、數字或下劃線 (等同於 [a-zA-Z0-9_]) |
\w+ |
"hello"、"world_123" 等 |
\W |
匹配任何非字母、非數字和非下劃線字符 (等同於 [^a-zA-Z0-9_]) |
\W{2} |
"!@"、"#%" 等 |
\s |
匹配任何空白字符 (包括空格、制表符、換行符等) |
\s+ |
" "、"\t"、"\n" 等 |
\S |
匹配任何非空白字符 | \S{3} |
"abc"、"123" 等 |
[abc] |
匹配方括號中的任何字符 (在這裡是 a、b 或 c) |
[abc]+ |
"a"、"abc"、"ba" 等 |
[^abc] |
匹配除方括號中的字符以外的任何字符 (在這裡是除 a、b 和 c 以外的任何字符) |
[^abc]+ |
"def"、"xyz" 等 |
(abc|def) |
匹配括號中的任何一個選項 (在這裡是 "abc" 或 "def") |
(abc|def) |
"abc" 或 "def" |
| 限定符 | 描述 | 範例 | 匹配 |
|---|---|---|---|
{n} |
匹配前面的元素恰好 n 次 | a{2} |
"aa" |
{n,} |
匹配前面的元素至少 n 次 | a{2,} |
"aa", "aaa", "aaaa", ... |
{n,m} |
匹配前面的元素至少 n 次,但不超過 m 次 | a{2,3} |
"aa", "aaa" |
* |
匹配前面的元素零次或多次 | a* = a{0,} |
"", "a", "aa", "aaa", ... |
+ |
匹配前面的元素一次或多次 | a+ = a{1,} |
"a", "aa", "aaa", ... |
? |
匹配前面的元素零次或一次 | a? = a{0,1} |
"", "a" |
1.用一個字串表達以下網址
2.用一個字串表達下列所有IP
3.用一個字串表達下列所有網址
transbiz.com.tw/fb/post01
transbiz.com.tw/fb/post02
transbiz.com.tw/fb/post03
transbiz.com.tw/web/post01
transbiz.com.tw/web/post02
transbiz.com.tw/web/post03
例子 Regex101
身分證字號
詳解
[A-Za-z] = 大小寫英文字母
[12] = 1或2
\d{8} = 8碼數字
西元生日
詳解
\d{4} = 0000~9999
[-\/\s] = 使用者可以輸入-或/或空格 已表示日期分隔
(0[1-9]|1[0-2]) = 01~09 或 10~12
(0\d|1\d|2\d|3[01])
01~09 或 10~19 或 20~29 或 30~31
電子信箱
Note
這個正則表達式的解釋如下:
^:開始匹配輸入的開頭。
[a-zA-Z0-9._%+-]+:匹配一個或多個字母、數字、點、下劃線、百分號、加號或破折號。這部分對應電子郵件地址的用戶名部分。
@:匹配 '@' 符號。
[a-zA-Z0-9.-]+:匹配一個或多個字母、數字、點或破折號。這部分對應電子郵件地址的域名部分。
.:匹配點符號,這個點符號分隔域名和頂級域名。
[a-zA-Z]{2,}:匹配兩個或更多的字母。這部分對應電子郵件地址的頂級域名部分(例如 '.com'、'.net'、'.org' 等)。
$:結束匹配輸入的結尾。
網址URL
/^(https:\/\/www.|https?:\/\/|www\.)(((?!-)(?!.*--)[a-zA-Z0-9-]+(?<!-))+\.)+(?<!-)[a-z]{2,}(\/[a-zA-Z0-9-\/\?\=\+\&\#\.]*)?(?<!-)$/gm
詳解
^:開始匹配輸入的開頭。
(https:\/\/www.|https?:\/\/|www\.):匹配 https://www.、http://、https:// 或 www.。這部分對應網址的協議和可選的 www 子域名。
(((?!-)(?!.*--)[a-zA-Z0-9-]+(?<!-))+\.)+:匹配一個或多個由字母、數字或破折號組成的子域名或域名,
但是不能以破折號開頭或結尾,也不能包含連續的破折號。每個子域名或域名後面都有一個點號。
(?<!-)[a-z]{2,}:匹配兩個或更多的小寫字母。這部分對應網址的頂級域名(例如 .com、.net、.org 等),並且不能以破折號結尾。
(\/[a-zA-Z0-9-\/\?\=\+\&\#\.]*)?:可選部分,匹配由字母、數字、破折號、斜線、問號、等號、加號、和號、井號或點號組成的路徑、查詢參數或錨點。
$:結束匹配輸入的結尾。
/gm:這是正則表達式的標誌。
'g' 表示全局匹配(匹配輸入中的所有符合條件的部分),
'm' 表示多行匹配('^' 和 '$' 會匹配每一行的開頭和結尾,而不只是整個輸入的開頭和結尾)。
創建正則表達式的方法
let pattern = 'abc';
let flags = 'g';
let regex = new RegExp(pattern, flags);
let regex2 = new RegExp('abc', 'g');
旗標
| 標誌 | 描述 |
|---|---|
/i |
不區分大小寫 |
/g |
全局匹配 |
/m |
多行匹配 |
/u |
Unicode碼點 |
/y |
黏性匹配 |
/s |
dotAll .表示任何字符包含換行或換頁符 |
ingoreCase
global
multiline
Sticky matching
每次匹配後,它會從上次匹配結束的位置開始下一次匹配。
const text = 'test1 test2 test3';
const regex = /\w+\s/y;
console.log(regex.lastIndex); // 0
console.log(regex.exec(text)); // ["test1"]
console.log(regex.lastIndex); // 6
console.log(regex.exec(text)); // ["test2"]
console.log(regex.lastIndex); // 12
regex.lastIndex = 0;
console.log(regex.lastIndex); // 0
console.log(regex.exec(text)); // ["test1"]
Unicode
Unicode參考
console.log('xyz林123'.replace(/\u6797/, 'Lin'))
//xyzLin123
console.log('xyz林123'.replace(/\u{6797}/, 'Lin'))
//xyz林123
console.log('xyz林123'.replace(/\u{6797}/u, 'Lin'))
//xyzLin123
| 符號 | 描述 | 範例 | 匹配 |
|---|---|---|---|
\p{L} |
匹配任何字母 | \p{L}{3} |
"abc"、"def" 等 |
\p{N} |
匹配任何數字 | \p{N}{3} |
"123"、"456" 等 |
\p{P} |
匹配任何標點符號 | \p{P}{2} |
"!@"、"#%" 等 |
\p{Z} |
匹配任何分隔符(包括空格、制表符、換行符等) | \p{Z}+ |
" "、"\t"、"\n" 等 |
\p{S} |
匹配任何符號(非字母、非數字、非標點、非分隔符) | \p{S}{2} |
"$%"、"&*" 等 |
\p{Lu} |
匹配任何大寫字母 | \p{Lu}{2} |
"AB"、"CD" 等 |
\p{Ll} |
匹配任何小寫字母 | \p{Ll}{2} |
"ab"、"cd" 等 |
\p{Nd} |
匹配任何十進制數字 | \p{Nd}{3} |
"123"、"456" 等 |
正規表達式
正規表達式的使用方式
regex.方法(字串)
.test()
是否有匹配到 return true or false
let regex = /abc/;
console.log(regex.test('abcdef')); // 輸出:true
console.log(regex.test('xyz')); // 輸出:false
.exec()
const regex = /([a-z]+)@([a-z]+)\.com/;
const exec = regex.exec('abc@def.com');
const exec2 = regex.exec('not-an-email');
console.log(exec);
//輸出:['abc@def.com', 'abc', 'def', index: 0, input: 'abc@def.com', groups: undefined]
console.log(exec[2]); // "def"
console.log(exec2)
字串.方法(regex)
.split()
.match()
const str = "I have 5 apples and 3 oranges.";
const regex = /\d+/g;
const matches = str.match(regex);
console.log(matches); // 輸出:['5', '3']
.replace()
const str = "I have 5 apples and 3 oranges.";
const regex = /\d+/g;
const nstr = str.replace(regex, 'X');
console.log(nstr); // I have X apples and X oranges.
.search()
斷言
前瞻斷言(Lookahead Assertion)
- 正向前瞻(Positive Lookahead Assertion):語法為
(?=...)
它表示當前位置的後面(右邊)必須是括號內的字符 - 負向前瞻(Negative Lookahead Assertion):語法為
(?!...)
它表示當前的位置後面(右邊)不能是括號內的字符
後顧斷言(Lookbehind Assertion)
Warning
JavaScript 在 ES2018 版本之前不支援後顧,且後顧的長度是固定的不可使用* +
-
正向後顧(Positive Lookbehind Assertion):語法為
(?<=...)
它表示當前的位置前面(左邊)必須是括號內的字符 -
負向後顧(Negative Lookbehind Assertion):語法為
(?<!...)
它表示當前的位置前面(左邊)不能是括號內的字符。
嵌套
const text = "Alice: 95, Bob: 88, Charlie: 92, David: 85";
const regex = /\b\w+(?=: (?![9][0-9]\b))/g;
const matches = text.match(regex);
console.log(matches); // Output: ["Bob", "David"]
| 斷言類型 | 語法 | 描述 | 範例 | 範例說明 |
|---|---|---|---|---|
| 正向前瞻 | (?=...) |
當前位置後面必須是括號內的字符 |
/可愛(?=貓)/g |
匹配 '可愛貓' 的 '可愛' 不匹配 '可愛狗' 的 '可愛' |
| 負向前瞻 | (?!...) |
當前位置後面不能是括號內的字符 |
/可愛(?!貓)/g |
匹配 '可愛狗' 的 '可愛' 不匹配 '可愛貓' 的 '可愛' |
| 正向後顧 | (?<=...) |
當前位置前面必須是括號內的字符 |
/(?<=可愛)貓/g |
匹配 '可愛貓' 的 '貓' 不匹配 '野生貓' 的 '貓' |
| 負向後顧 | (?<!...) |
當前位置前面不能是括號內的字符 |
/(?<!可愛)貓/g |
匹配 '野生貓' 的 '貓' 不匹配 '可愛貓' 的 '貓' |
分組
捕獲分組
const reg = /([a-z]+)@([a-z]+)\.com/;
const str = "abc@def.com";
const matches = str.match(reg);
console.log( matches[0]); // "abc@def.com"
console.log( matches[1]); // "abc"
console.log( matches[2]); // "def"
非捕獲分組
const reg = /(?:[a-z]+)@(?:[a-z]+)\.com/;
const str = "abc@def.com";
const matches = str.match(reg);
console.log( matches[0]); // "abc@def.com"
console.log( matches[1]); // undefined
console.log( matches[2]); // undefined
貪婪與非貪婪
貪婪匹配
var str = 'abcabcabc';
var reg = /a.*c/;
var result = str.match(reg);
console.log(result); // 輸出:['abcabcabc']
非貪婪匹配
var str = 'abcabcabc';
var reg = /a.*?c/;
var result = str.match(reg);
console.log(result); // 輸出:['abc']
參考資料:
Regex101
Regexone
RegexUnicode
Python
虛擬環境
- 創建虛擬環境
在這個命令中,venv 是你的虛擬環境的名稱。你可以將它更改為你想要的任何名稱。
- 啟動虛擬機
- 退出虛擬機
- 刪除虛擬環境
- 查看安裝套件
還沒想到
- 安裝mkdocs
| Package | Version |
|---|---|
| click | 8.1.7 |
| colorama | 0.4.6 |
| ghp-import | 2.1.0 |
| Jinja2 | 3.1.4 |
| Markdown | 3.6 |
| MarkupSafe | 2.1.5 |
| mergedeep | 1.3.4 |
| mkdocs | 1.6.0 |
| mkdocs-get-deps | 0.2.0 |
| packaging | 24.0 |
| pathspec | 0.12.1 |
| pip | 24.0 |
| platformdirs | 4.2.1 |
| python-dateutil | 2.9.0.post0 |
| PyYAML | 6.0.1 |
| pyyaml_env_tag | 0.1 |
| six | 1.16.0 |
| watchdog | 4.0.0 |
mkdocs-print-site-plugin==2.4.0
└── mkdocs-material [required: >=7.3.0, installed: 9.5.21]
├── Babel [required: ~=2.10, installed: 2.15.0]
├── colorama [required: ~=0.4, installed: 0.4.6]
├── Jinja2 [required: ~=3.0, installed: 3.1.4]
│ └── MarkupSafe [required: >=2.0, installed: 2.1.5]
├── Markdown [required: ~=3.2, installed: 3.6]
├── mkdocs [required: ~=1.6, installed: 1.6.0]
│ ├── click [required: >=7.0, installed: 8.1.7]
│ │ └── colorama [required: Any, installed: 0.4.6]
│ ├── colorama [required: >=0.4, installed: 0.4.6]
│ ├── ghp-import [required: >=1.0, installed: 2.1.0]
│ │ └── python-dateutil [required: >=2.8.1, installed: 2.9.0.post0]
│ │ └── six [required: >=1.5, installed: 1.16.0]
│ ├── Jinja2 [required: >=2.11.1, installed: 3.1.4]
│ │ └── MarkupSafe [required: >=2.0, installed: 2.1.5]
│ ├── Markdown [required: >=3.3.6, installed: 3.6]
│ ├── MarkupSafe [required: >=2.0.1, installed: 2.1.5]
│ ├── mergedeep [required: >=1.3.4, installed: 1.3.4]
│ ├── mkdocs-get-deps [required: >=0.2.0, installed: 0.2.0]
│ │ ├── mergedeep [required: >=1.3.4, installed: 1.3.4]
│ │ ├── platformdirs [required: >=2.2.0, installed: 4.2.1]
│ │ └── PyYAML [required: >=5.1, installed: 6.0.1]
│ ├── packaging [required: >=20.5, installed: 24.0]
│ ├── pathspec [required: >=0.11.1, installed: 0.12.1]
│ ├── PyYAML [required: >=5.1, installed: 6.0.1]
│ ├── pyyaml_env_tag [required: >=0.1, installed: 0.1]
│ │ └── PyYAML [required: Any, installed: 6.0.1]
│ └── watchdog [required: >=2.0, installed: 4.0.0]
├── mkdocs-material-extensions [required: ~=1.3, installed: 1.3.1]
├── paginate [required: ~=0.5, installed: 0.5.6]
├── Pygments [required: ~=2.16, installed: 2.18.0]
├── pymdown-extensions [required: ~=10.2, installed: 10.8.1]
│ ├── Markdown [required: >=3.6, installed: 3.6]
│ └── PyYAML [required: Any, installed: 6.0.1]
├── regex [required: >=2022.4, installed: 2024.4.28]
└── requests [required: ~=2.26, installed: 2.31.0]
├── certifi [required: >=2017.4.17, installed: 2024.2.2]
├── charset-normalizer [required: >=2,<4, installed: 3.3.2]
├── idna [required: >=2.5,<4, installed: 3.7]
└── urllib3 [required: >=1.21.1,<3, installed: 2.2.1]
pipdeptree==2.20.0
├── packaging [required: >=23.1, installed: 24.0]
└── pip [required: >=23.1.2, installed: 24.0]
mkdocs-glightbox==0.3.7
mkdocs-print-site-plugin==2.4.0
└── mkdocs-material [required: >=7.3.0, installed: 9.5.21]
├── Babel [required: ~=2.10, installed: 2.15.0]
├── colorama [required: ~=0.4, installed: 0.4.6]
├── Jinja2 [required: ~=3.0, installed: 3.1.4]
│ └── MarkupSafe [required: >=2.0, installed: 2.1.5]
├── Markdown [required: ~=3.2, installed: 3.6]
├── mkdocs [required: ~=1.6, installed: 1.6.0]
│ ├── click [required: >=7.0, installed: 8.1.7]
│ │ └── colorama [required: Any, installed: 0.4.6]
│ ├── colorama [required: >=0.4, installed: 0.4.6]
│ ├── ghp-import [required: >=1.0, installed: 2.1.0]
│ │ └── python-dateutil [required: >=2.8.1, installed: 2.9.0.post0]
│ │ └── six [required: >=1.5, installed: 1.16.0]
│ ├── Jinja2 [required: >=2.11.1, installed: 3.1.4]
│ │ └── MarkupSafe [required: >=2.0, installed: 2.1.5]
│ ├── Markdown [required: >=3.3.6, installed: 3.6]
│ ├── MarkupSafe [required: >=2.0.1, installed: 2.1.5]
│ ├── mergedeep [required: >=1.3.4, installed: 1.3.4]
│ ├── mkdocs-get-deps [required: >=0.2.0, installed: 0.2.0]
│ │ ├── mergedeep [required: >=1.3.4, installed: 1.3.4]
│ │ ├── platformdirs [required: >=2.2.0, installed: 4.2.1]
│ │ └── PyYAML [required: >=5.1, installed: 6.0.1]
│ ├── packaging [required: >=20.5, installed: 24.0]
│ ├── pathspec [required: >=0.11.1, installed: 0.12.1]
│ ├── PyYAML [required: >=5.1, installed: 6.0.1]
│ ├── pyyaml_env_tag [required: >=0.1, installed: 0.1]
│ │ └── PyYAML [required: Any, installed: 6.0.1]
│ └── watchdog [required: >=2.0, installed: 4.0.0]
├── mkdocs-material-extensions [required: ~=1.3, installed: 1.3.1]
├── paginate [required: ~=0.5, installed: 0.5.6]
├── Pygments [required: ~=2.16, installed: 2.18.0]
├── pymdown-extensions [required: ~=10.2, installed: 10.8.1]
│ ├── Markdown [required: >=3.6, installed: 3.6]
│ └── PyYAML [required: Any, installed: 6.0.1]
├── regex [required: >=2022.4, installed: 2024.4.28]
└── requests [required: ~=2.26, installed: 2.31.0]
├── certifi [required: >=2017.4.17, installed: 2024.2.2]
├── charset-normalizer [required: >=2,<4, installed: 3.3.2]
├── idna [required: >=2.5,<4, installed: 3.7]
└── urllib3 [required: >=1.21.1,<3, installed: 2.2.1]
mkdocs-table-reader-plugin==2.2.1
├── mkdocs [required: >=1.0, installed: 1.6.0]
│ ├── click [required: >=7.0, installed: 8.1.7]
│ │ └── colorama [required: Any, installed: 0.4.6]
│ ├── colorama [required: >=0.4, installed: 0.4.6]
│ ├── ghp-import [required: >=1.0, installed: 2.1.0]
│ │ └── python-dateutil [required: >=2.8.1, installed: 2.9.0.post0]
│ │ └── six [required: >=1.5, installed: 1.16.0]
│ ├── Jinja2 [required: >=2.11.1, installed: 3.1.4]
│ │ └── MarkupSafe [required: >=2.0, installed: 2.1.5]
│ ├── Markdown [required: >=3.3.6, installed: 3.6]
│ ├── MarkupSafe [required: >=2.0.1, installed: 2.1.5]
│ ├── mergedeep [required: >=1.3.4, installed: 1.3.4]
│ ├── mkdocs-get-deps [required: >=0.2.0, installed: 0.2.0]
│ │ ├── mergedeep [required: >=1.3.4, installed: 1.3.4]
│ │ ├── platformdirs [required: >=2.2.0, installed: 4.2.1]
│ │ └── PyYAML [required: >=5.1, installed: 6.0.1]
│ ├── packaging [required: >=20.5, installed: 24.0]
│ ├── pathspec [required: >=0.11.1, installed: 0.12.1]
│ ├── PyYAML [required: >=5.1, installed: 6.0.1]
│ ├── pyyaml_env_tag [required: >=0.1, installed: 0.1]
│ │ └── PyYAML [required: Any, installed: 6.0.1]
│ └── watchdog [required: >=2.0, installed: 4.0.0]
├── pandas [required: >=1.1, installed: 2.2.2]
│ ├── numpy [required: >=1.26.0, installed: 1.26.4]
│ ├── python-dateutil [required: >=2.8.2, installed: 2.9.0.post0]
│ │ └── six [required: >=1.5, installed: 1.16.0]
│ ├── pytz [required: >=2020.1, installed: 2024.1]
│ └── tzdata [required: >=2022.7, installed: 2024.1]
├── PyYAML [required: >=5.4.1, installed: 6.0.1]
└── tabulate [required: >=0.8.7, installed: 0.9.0]
pipdeptree==2.20.0
├── packaging [required: >=23.1, installed: 24.0]
└── pip [required: >=23.1.2, installed: 24.0]
API
API 代碼意思
| 錯誤碼 | 含義 |
|---|---|
| 400 Bad Request | 這個錯誤通常表示你傳遞的請求資料格式不正確,例如 JSON 資料格式錯誤,或者缺少必要的資料。 |
| 401 Unauthorized | 這個錯誤表示你需要認證才能訪問該 API,例如你忘記提供 API 金鑰或者提供了錯誤的 API 金鑰。 |
| 403 Forbidden | 這個錯誤表示你沒有權限訪問該 API,即使你已經認證。 |
| 404 Not Found | 這個錯誤表示你試圖訪問的 API 不存在。 |
| 500 Internal Server Error | 這個錯誤表示服務器遇到了一個未知的錯誤。 |
test衝突
NPX
NPM 與 NPX 詳解
1. 什麼是 NPM?
NPM (Node Package Manager) 是 Node.js 的包管理工具,也是 JavaScript 生態系統中最受歡迎的工具之一。
NPM 的功能
- 安裝套件:可以從 npm 的官方倉庫下載並安裝 JavaScript 套件。
- 版本管理:方便管理和升級套件的版本。
- 共享程式碼:開發者可以將自己的套件發布到 npm。
- 依賴管理:自動處理專案所需的所有依賴項目。
NPM 的基本用法
- 安裝全域套件:
- 全域安裝:將套件安裝到系統的全域環境中,所有專案都可以使用,例如 CLI 工具。
- 安裝專案本地套件:
- 專案本地安裝:將套件安裝到專案的
node_modules資料夾中,僅在該專案內可用。 - 移除套件:
- 更新套件:
- 初始化專案:
如何透過 node_modules/.bin 路徑或額外配置
node_modules/.bin是 NPM 安裝本地套件時,自動生成的二進制執行檔路徑。- 執行方式:
- 可通過新增路徑到環境變數來簡化操作,例如:
什麼是 Node.js?
Node.js 是一個基於 Chrome V8 JavaScript 引擎的開放原始碼伺服器端執行環境,讓 JavaScript 可以在伺服器端運行。
NPM 的優缺點
優點
- 擁有龐大的社群和豐富的套件資源。
- 提供語義化版本控制 (Semantic Versioning)。
- 自動管理依賴關係。
缺點
- 部分版本的安裝速度較慢(可以透過使用
npm ci或其他加速工具改善)。 - 無法直接執行套件中的 CLI 工具,需要透過
node_modules/.bin路徑或額外配置。
2. 什麼是 NPX?
NPX 是隨 NPM 一起提供的工具(從 NPM 版本 5.2.0 開始內建)。NPX 的主要目的是簡化執行 Node.js CLI 套件的過程,讓開發者無需全域安裝即可運行命令。
NPX 的功能
- 執行臨時套件:
- 臨時套件指的是未事先安裝的套件,NPX 在執行時會自動下載並運行該套件,執行完成後不會保留。
- 指定套件版本:允許運行特定版本的 CLI 工具。
- 避免全域污染:不需要將所有 CLI 工具安裝到全域,減少全域環境中的冗餘。
NPX 的基本用法
- 執行未安裝的 CLI 工具:
- 指定版本執行:
- 執行本地安裝的套件:
NPX 如何簡化 Node.js CLI 工具
- 不需要手動安裝 CLI 工具,只需使用一次的工具可以直接運行,避免安裝後再移除的麻煩。
- 支援執行本地安裝的套件,無需指定完整路徑。
NPX 的優缺點
優點
- 節省硬碟空間,避免安裝多餘的全域套件。
- 減少全域安裝帶來的版本衝突問題。
- 即用即走,適合執行一次性任務。
缺點
- 每次執行未安裝的套件都需要下載,可能較慢。
- 如果離線使用,無法執行未安裝的套件。
3. NPM 與 NPX 的比較
| 特性 | NPM | NPX |
|---|---|---|
| 主要用途 | 管理與安裝套件 | 執行 Node.js CLI 工具 |
| 是否需安裝 | 是 | 否,臨時執行 |
| 存取套件版本 | 固定在 package.json | 可指定任意版本 |
| 離線支持 | 支持已安裝的套件 | 僅支持本地已安裝的套件 |
| 全域污染 | 可能會因全域安裝而發生污染 | 不會污染全域 |
4. 實際範例演示
使用 NPM 安裝與執行
- 安裝 ESLint:
- 執行 ESLint:
使用 NPX 簡化操作
- 無需安裝,直接執行 ESLint:
5. 教學建議
- 練習操作:讓實習生嘗試安裝套件(如 lodash)、執行 CLI 工具(如 create-react-app)。
- 解釋依賴管理:
- dependencies:在專案運行時需要的套件,例如後端框架或資料庫驅動。
- devDependencies:僅在開發過程中需要的套件,例如測試框架或編譯工具。
- 強調安全性:說明如何檢查套件的來源與版本安全性。
- 介紹加速工具:例如使用 Yarn 或 PNPM 作為替代選項。
希望這份簡報能幫助實習生快速掌握 NPM 與 NPX,並應用於實際開發中!
Login
Spring Boot Login 教學:從 0 建立登入系統
本文件說明如何從零開始建立一個簡單的 Spring Boot 登入系統專案,並確保成功連線資料庫與驗證帳密。
📁 專案結構
src/
├── main/
│ ├── java/com/example/login/
│ │ ├── controller/
│ │ ├── model/
│ │ ├── repository/
│ │ └── service/
│ └── resources/
│ ├── application.yml
│ ├── static/
│ └── templates/
└── test/java/com/example/login/
1️⃣ 建立 Spring Boot 專案
使用 Spring Initializr 產生一個專案,勾選以下依賴:
- Spring Web
- Spring Data JPA
- MySQL Driver
src/main/resources/application.yml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/your_db_name
username: root
password: your_password
jpa:
hibernate:
ddl-auto: update
show-sql: true
database-platform: org.hibernate.dialect.MySQLDialect
3️⃣ 建立 Model
src/main/java/com/example/login/model/Login.java:
package com.example.login.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "login")
public class Login {
@Id
private String username;
private String password;
// Getter/Setter
}
4️⃣ 建立 Repository
package com.example.login.repository;
import com.example.login.model.Login;
import org.springframework.data.jpa.repository.JpaRepository;
public interface LoginRepository extends JpaRepository<Login, String> {
Login findByUsernameAndPassword(String username, String password);
}
5️⃣ 建立 Service
package com.example.login.service;
import com.example.login.model.Login;
import com.example.login.repository.LoginRepository;
import org.springframework.stereotype.Service;
@Service
public class LoginService {
private final LoginRepository loginRepository;
public LoginService(LoginRepository loginRepository) {
this.loginRepository = loginRepository;
}
public boolean authenticate(String username, String password) {
Login login = loginRepository.findByUsernameAndPassword(username, password);
System.out.println("Login attempt: " + username + ", result: " + login);
return login != null;
}
}
6️⃣ 建立 Controller
package com.example.login.controller;
import com.example.login.service.LoginService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class LoginController {
private final LoginService loginService;
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
boolean success = loginService.authenticate(username, password);
return success ? "Login success!" : "Invalid credentials.";
}
}
✅ 確認資料庫連線
- 看 console 是否有印出:
HikariPool-1 - Start completed - SQL 有執行
select查詢(表示連線正常) - 可加入測試資料:
🧪 測試 API
使用 Postman 或 curl:
📦 打包專案
產出 target/login-0.0.1-SNAPSHOT.jar 後啟動:
📝 小提醒
- 如果出現
No database selected,記得在 application.yml 的 URL 中加上資料庫名稱 - 確認 MySQL 有開啟,且帳密正確
📝 初始需求
使用者希望從零開始學習架設一個「庫存收支相關網站」,技術棧如下:
- 前端:Angular
- 導端:Java (Spring Boot)
- 資料庫:MySQL
並且希望了解完整步驟: 1. 專案建立 2. API 開發與資料庫連接 3. 前後端串接 4. 打包與部署
✅ 專案架構建立
1. Spring Boot 專案初始化
- 使用 Spring Initializr 建立 Maven 專案。
- 選擇依賴:Spring Web、Spring Data JPA、MySQL Driver、Spring Boot DevTools。
目錄結構如下:
src/
├─ main/
│ ├─ java/
│ │ └─ com/example/login/
│ │ ├─ controller/
│ │ ├─ model/
│ │ ├─ repository/
│ │ └─ service/
│ └─ resources/
│ ├─ application.properties
│ ├─ static/
│ └─ templates/
2. 設定資料庫連線
於 application.properties 中設定:
spring.datasource.url=jdbc:mysql://localhost:3306/your_db?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
3. 建立 Entity 與 Repository
// model/Login.java
@Entity
@Table(name = "login")
public class Login {
@Id
private String username;
private String password;
}
// repository/LoginRepository.java
public interface LoginRepository extends JpaRepository<Login, String> {
Login findByUsernameAndPassword(String username, String password);
}
4. 建立 Service 與 Controller
// service/LoginService.java
@Service
public class LoginService {
private final LoginRepository loginRepository;
public LoginService(LoginRepository loginRepository) {
this.loginRepository = loginRepository;
}
public boolean authenticate(String username, String password) {
Login login = loginRepository.findByUsernameAndPassword(username, password);
System.out.println("Login result: " + login);
return login != null;
}
}
// controller/LoginController.java
@RestController
@RequestMapping("/api")
public class LoginController {
private final LoginService loginService;
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Login login) {
boolean result = loginService.authenticate(login.getUsername(), login.getPassword());
return ResponseEntity.ok(result);
}
}
🐛 常見錯誤與排查
1. 找不到資料或回傳 null
- 確認資料表名稱與欄位是否與 Entity 一致
- 確認資料已正確插入資料庫
- 開啟
spring.jpa.show-sql=true觀察 SQL 查詢
2. No database selected
- URL 設定錯誤,需指明資料庫名稱,如:
jdbc:mysql://localhost:3306/login
- 確保 package 名稱與實際目錄一致,例如:
對應路徑應為
src/main/java/com/example/login/model
🛠️ 打包與執行
1. 使用 Maven 打包
成功後會生成 .jar 檔於 target/ 資料夾,例如:login-0.0.1-SNAPSHOT.jar
2. 執行打包後的應用程式
📦 附加說明
如何確認資料庫有連上
- 看 console log 是否出現:
HikariPool - Start completed. - Hibernate 查詢是否執行
- 可於 Service 中
System.out.println()輸出查詢結果
✅ 結語
此為學習專案,從零建立後端架構與登入功能,後續可擴展:
- 建立使用者註冊
- 記錄商品進出庫紀錄
- 搭配 Angular 建立前端畫面
- 部署至雲端服務(如 AWS、GCP、Render 等)






























































