---
title: 2024程設筆記
tags: 2024
author: xCk27x
---
# 第二次上課
###### tags: `2024`
- [ ] Git
- [ ] TypeScript
- [ ] Docker
## Git
:::info
Git是眾所周知的版本管理工具,最基本的操作就是push、pull、clone這些。
但如果要管理一個大型專案,只有這樣是遠遠不夠的,所以以下是一些比較進階的操作。
:::
### 首先要普及幾個概念
1. 暫存區(Staging Area)
任何新增、修改、刪除的檔案都需要先使用 ```git add``` 送進git的暫存區,才可以被提交 -> commit。
2. 遠端 (remote)
你使用本地端使用git init時,建立的會是一個**local** repository (目錄下存在```.git```這個檔案),意思是你的commit、branch、history,全部都會存在你的電腦上。
而你在github上看到的都是**remote** repository,意思是你可以將local repository的東西**複製**一份到遠端上,這樣別人就能看到你的檔案跟git紀錄了。
:::warning
但請注意,local跟remote是不會自動同步的,所以你需要執行一些遠端的操作來同步兩者,例如push、fetch、pull。
:::
3. main / master (主分支)
你可能會看到一些專案的主分支是master,而有些是main。實際上兩個並沒有差,只是以前預設名稱是master,而現在是main。
4. head/HEAD (當前分支)
head是一個很特別的分支名稱,他永遠指向**目前所在的分支或commit位置**,概念上很像C語言的指標,```git checkout``` 基本上就是改變head指向的位置。
### 還是先從最常用的指令講起
- **```git add -A```** -> 將所有變更都送進暫存區,請注意 **A** 要大寫。
 就是那個+號
- **```git commit -m "your messege"```** -> 用暫存區的變更建立一個新commit,並用-m提供commit訊息。

- **```git push```** -> 將當前分支的變更同步到遠端。
- **```git pull```** -> 等於 ```git fetch``` + ```git merge```,兩者都會在後面講到。
### 接下來會複雜一些
:::info
請先下載 vscode extension: [**Git Graph**](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph)
:::
- **```git fetch```** -> 將遠端所有branches的狀態更新到本地端,但注意這並不是同步! 只是讓本地端知道目前遠端總體的狀態是如何。

> 以此 Git Graph 舉例,```origin/oCk27o``` 就是oCk27o在遠端的狀態,但本地端的oCk27o則在其他位置。
> 而注意到main的左邊有一個圓圈,他代表head的位置。
- **```git merge```** -> 將head branch與其遠端branch合併。以上圖來看,就是將 ```main``` 與 ```origin/main```兩個分支合併。
- **```git rebase <target branch>```** -> 跟merge很類似,他會將到head分支上所有新的commit重新放到指定分支上。
> 我知道上面兩個指令說起來有點抽象,所以這裡有一個學習網站
> https://learngitbranching.js.org/?locale=zh_TW&demo=
> 請將基礎篇完成,這樣你就會比較有概念了👍
- **```git reset <模式> <commitID> ```** -> 將你的head移動到先前的commit,並將此commit以後的commit取消掉。
> 把commit取消掉,新的檔案不就消失了嗎,這樣會出大事吧?
對,所以reset提供你三個模式選擇,分別是mixed(預設)、soft、hard
具體的差別請看這篇 https://ithelp.ithome.com.tw/articles/10332631
## TypeScript
:::info
TypeScript 將 JavaScript 的弱型別問題做了些改善,讓debug變簡單。
但相對的,會需要額外花些時間去宣告型別,可以說是用時間換出錯機率。
:::
> 因為我們是前端是寫vue為主,而<script></script>中可以讓每個組件決定要不要寫TS,如果還是想寫JS也沒關係,但TS遲早會碰到。
> 後端嘛... 我再考慮一下到底寫JS還TS🤔
### 下載
1. 全局下載
```$ npm install -g typescript```
2. 讓專案能使用TypeScript 編譯器
```$ tsc --init```
這時候你的目錄下會出現 ```tsconfig.json```,這是 TypeScript 編譯器的設定檔。
3. 接下來使用 ```$ tsc```
TypeScript 編譯器就會幫我們自動掃描所有 .ts 結尾的檔案並且產出 JS 檔案。
4. 最後用 ```$ node yourfile.js``` 就可以執行了 !
### 型別
說實在,其實TS的基本型別你需要懂得還真沒幾個
1. any
2. number
3. string
4. boolean
5. null
6. undefined
7. void
8. never
> 好把我改口,其實很多🥲
#### 講太長我自己都不想看了,所以就盡可能簡單解釋吧
- any
-> ```let a: any```,任何型別都可能, 就是說 a 這個變數可以被assign任何型別的值。這就是JS變數的概念,也是TS想避免的事。
- number
-> ```let b: number```,只能接受數字,而甚麼叫數字呢?
example: 1, -12, 6.31, 2e10, -Infinite, Number.MAX_SAFE_INTEGER, NaN
- string
-> ```let c: string```,只能接受字串。
- boolean
-> ```let d: boolean```,只能接受布林值,true 或 false。
- null
-> ```let e: null```,這變數沒有存任何值。
- undefined
-> ```let f: undefined```,你要找的這個值沒有定義過啊!
- void
-> ```function modify(): void {...}```,回傳值為空的意思,通常用在函數的回傳值宣告,告訴我們這個函數不會回傳任何值。
- never
-> ```function mustError(): never {...}```,never return的意思,通常用在函數的回傳值宣告,告訴我們這個函數內存在無窮迴圈,或是一定會拋出錯誤。
### 型別宣告
如果一個變數的值可以是number或是null,則可以這樣指定:
```let numOrNull: number | null = 123```,這樣會先將初始值設定為123,而之後使用```numOrNull = null```便不會報錯。
funciton的型別宣告也很簡單
-> function hello(a: number, b: string, c: number | null = null): string {...}
hello 能傳入三個 Arguments,a 只能是 number,b 只能是 string,c 則可以是 number | null,並且預設值是null。
而他的回傳值是string。
### type
> 了解完基本型別,要來講物件的型別了
一個自定義的Object大概就是用大括弧包其來的東西,例如
```typescript=
let Jack = {
id: 12,
age: 20,
phone: "0900112233"
}
```
那我要怎麼定義這個物件應該長甚麼樣子呢?
-> 使用 type
```typescript=
type Person = {
id: number,
age?: number, // 注意這行的?:
phone: string
}
let Jack: Person = {
id: 12,
age: 20,
phone: "0900112233"
}
```
> 那剛剛的age?: number是甚麼意思呢?
這代表你可以不指派此物件的這個屬性,例如
```typescript=
type Person = {
id: number,
age?: number, // 注意這行的?:
phone: string
}
let Dora: Person = {
id: 21,
phone: "0912345678"
}
```
這也是合法的,而當你試圖存取,Dora.age的話,會回傳undefined。
當然,你也可以後期再去assign這個值 ```Dora.age = 10```
但是請注意,你不可以直接定義新的屬性到Dora裡。
```typescript=
Dora.height = 150 // 報錯: 類型 'Person' 沒有屬性 'height'
```
### interface
:::info
interface 直接翻譯是「介面」,或是「藍圖」,實際用起來跟 type 差不多,甚至很難界定甚麼時候用 type 甚麼時候用 interface。
:::
interface用來定義一個
```typescript=
interface Car { // 注意到定義interface是不用 = 的
wheel: 4
windows?: 6
metal: 'iron'
}
```
目前看起來都跟 type 很像,也一樣可以使用?:,那差別是甚麼呢
下面這樣的重複定義 interface 是合法的,因為他的概念是「為藍圖加入新的功能」
```typescript=
interface Car {
wheel: number,
windows?: number,
metal: string
}
interface Car {
brand: string,
price: number
}
```
而下面這樣會出錯,因為 type 不可重複定義。
```typescript=
type Person = {
id: number,
age?: number,
phone: string
}
type Person = { // 報錯: 識別碼 'Person' 重複。
school: string,
grade: number
}
```
若想要在程式碼中擴展 type
```typescript=
type Person = {
id: number,
age?: number,
phone: string
}
type SchoolInfo = {
school: string,
grade: number
}
type PersonInfo = Person & SchoolInfo;
```
> 詳細解釋可以參考 https://ithelp.ithome.com.tw/articles/10216794
可以發現實際上這並不算擴展,而是用兩個type重新定義了一個新的type。
:::info
結論是,如果物件的定義常需要修改或新增,則是建議用 interface,如果是固定的則用 type。
:::
## Docker
### 簡介
Docker 簡單來說,是用來打包應用程式的,而裡面有幾個重要概念你應該先知道。
1. 每個應用會在一個container裡實際運行
2. 應用長甚麼樣子儲存在image裡
3. 儲存image的倉庫叫registry
所以我要獲得某個寫成image的應用,並實際運行起來應該經過以下流程
1. 從registry取得此應用的image
2. 根據image建立container
3. 讓container跑起來
### 下載 Docker Desktop
[官方下載 Windows 版本](https://docs.docker.com/desktop/install/windows-install/)
[官方下載 MacOS 版本](https://docs.docker.com/desktop/install/mac-install/)
安裝後,在cmd執行 ```docker --version```,確認是否偵測到 docker 指令。
```
Docker version 24.0.2, build cb74dfc
```
:::warning
因為docker的指令提供了非常多的選項,所以沒辦法在這裡一一介紹,所以以下是我自己做的筆記,大部分可能用到的命令都在裡面:
https://thread-judo-0f8.notion.site/docker-5770051212e0446dabce273aed847bc2?pvs=4
:::
### container 指令
- ```docker container run <imagePath>```
他會先檢查你的Docker Desktop中有沒有要求的image,如果沒有就根據imagePath去registry下載image,再根據image建立一個新的container。
-> 試著實際下指令 ```docker container run diamol/ch02-hello-diamol-web```
現在你應該可以看到docker desktop中出現了第一個container,但名字是隨機的

如果你想要指定建立出來的container的名字,使用選項 ```--name <containerName>```
如果應用有成功運行的話應該會長這樣

這個應用其實是一個網站,但目前沒有辦法連到他的頁面,所以我們必須使用```--publish```選項來分配port
```
$ docker run --name diamol-web --publish 8088:80 diamol/ch02-hello-diamol-web
```

```--publish 8088:80``` 的意思是,「配發本機的 port 8088 給 container 的 port 80 使用」
因此你電腦的localhost:8088就會顯示此container內運行的網站。
最後介紹一個非常常用的選項: ```--detach (-d)```,這個選項告訴你再背景執行這個container,不要占用當前的terminal。
### image
現在點開docker desktop的**Image頁面**,你應該會看到剛剛從registry上下載的image -> diamol/ch02-hello-diamol-web
> 疑 我知道container是由image建立的,那image是怎麼建立的呢?
剛剛說過,image的目的是儲存應用程式的資料,所以我們應該在撰寫專案時使用dockerfile來建立image
### Dockerfile
:::info
範例程式碼 https://drive.google.com/drive/folders/1I02zHegNQN8WYUKKPI5p24WNq3ax2Jmv?usp=drive_link
:::

1. FROM
ex: FROM diamol/golang
每個docker image都必須以其他image作為基礎
2. WORKDIR
ex: WORKDIR web
建立一個目錄,並將其設定為工作目錄
3. COPY
ex: COPY index.html .
將本機的檔案從本機路徑複製到目標路徑,這邊是指將index.html複製到web資料夾中。
4. RUN
ex: RUN go build -o /web/server
從image建置container時會執行的命令
5. CMD
ex: CMD ["web/server"]
當container啟動時會執行的命令
6. ENV
ex: ENV USER=sixeyed"
用來設定環境變數
7. EXPOSE
ex: EXPOSE 80
定義容器執行時要監聽的端口
### Registry
registry是用來存放image的倉庫,預設是Docker Hub
而在尋找image的過程中,會使用「image referance」
ex: docker.io/diamol/golang:latest
- docker.io 代表registry的網址,而這就是預設的Docker Hub的。
- diamol 是image擁有者的帳戶名稱。
- golang 是指repository的名稱,裡面會有image的不同版本。
- latest 是指使用repository中image的最新版本,latest是預設值。