分享程式代碼相關筆記
目前文章總數:190 篇
最後更新:2025年 07月 26日
TypeScript 是由 Microsoft 開發的 JavaScript 的超集(superset),也就是說:
# 所有合法的 JavaScript 程式碼都是合法的 TypeScript。
# 但 TypeScript 增加了靜態型別檢查(Static Type Checking)和一些語法糖(如介面 interface、泛型 generics 等)來提升開發效率與程式碼品質。
簡言之: TypeScript = JavaScript + 型別系統 + 更好的開發工具支援(例如自動補全、錯誤提示)
TypeScript 解決了 .js 不能偵錯的問題,不用部署到環境才能知道是不是執行異常。這也是優勢:
# TypeScript 確實能幫助彌補前端開發中「內部測試(internal testing)」不落實的問題,這是導入 TS 的一大價值。
# TypeScript 雖然不是測試框架,但它在「沒有寫測試的情況下」也能給你最基本、最必要的安全網:
提供前端的安全網的特點:
1. 靜態型別檢查 | 等於「參數驗證」 |
2. 編譯時錯誤提示 | 等於「測試初步輸入合法性」 |
3. 自動提示/補全 | 減少「拼錯欄位名」 |
4. IDE 直接跳轉型別定義 | 減少「黑箱開發」與誤用 |
既然前端也可以偵錯,那麼簽入 Git 版控時,應該簽入 .ts 而非 .js 檔案
1. 開發者只簽入 .ts 原始碼(不包含編譯後的 .js) |
2. CI/CD 自動執行 TypeScript 編譯(tsc),產生 .js |
3. CD 階段部署編譯後的 .js 結果 到伺服器或 CDN。 |
單體市專案:在 TypeScript 導入前,如下 => 無法區分權責(.js 會簽入)
單體式專案:在 TypeScript 導入後,如下 => 可以區分權責(.js 不簽入)
可以有效的做第一層檢核,避免 .js 的常見錯誤,部署到環境上
打開範例代碼後,架構基本分成以下:
此為最後的版本,第 第四部分:Jenkins 自動化部署 - DEMO 成果 由 Jenkins 抓取 GitHub Source Code 後使用
1. Dockerfile | : | 此專案的容器化基礎配置 |
2. package.json | : | 自動產生 TypeScript 依賴 |
3. tsconfig.json | : | 專案對 TypeScript 的基本設定,包含檢查 .ts 路徑,輸出 .js 檔案路徑等 |
4. wwwroot | : | 網站靜態資源,由 Typescript 建置後會產出 .js 檔案 |
第三部分會將述建構此範例專案的過程
新建專案 -> Visual Studio 2022 -> WEB 類型 -> Nuget 安裝
輸入 Microsoft.TypeScript.MSBuild 進行安裝
專案根目錄 -> 滑鼠右鍵 -> 新建 tsconfig.json 檔案
依照自己所需進行配置,這裡的設定 rootDir 會取得 .ts 檔案建置,然後輸入到 outDir 產生 .js 檔案
{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es5",
"outDir": "wwwroot/js",
"rootDir": "wwwroot/ts"
},
"exclude": [
"node_modules",
"wwwroot/js"
]
}
這裡建立了 HelloWorld.ts 檔案
function sayHello(name: string): void {
alert(`Hello, ${name}!`);
}
如果建置成功會產生 HelloWorld.js 檔案
function sayHello(name) {
alert("Hello, ".concat(name, "!"));
}
//# sourceMappingURL=HelloWorld.js.map
這時進行 Git 版控簽入時,會發現 HelloWorld.js 檔案會被掃瞄到,這是不對的,正確的做法是由 TypeScript 生成 Javascript
只需要簽入 TypeScript 的檔案才正確
將自己專案根目錄下的 .gitignore 打開,要為此專案由 TypeScript 生成的 Javscript 目錄排除簽入
因此貼上以下(依照自己專案設定,這裡是範例說明):
# Our Example Include
wwwroot/js/
*.js
*.js.map
設定完成後,進行 Git 版控簽入,可以發現不再出現 wwwroot/js/ 目錄下的 .js 檔案,這是為了實現 TypeScript 自動化建置的所需步驟
補充:簽入後,可以發現 wwwroot/js/ 資料夾沒有簽入,因為裡面沒有檔案,正確的畫面
開啟專案根目錄,並且執行 Windows Command Line ,輸入以下,產生 package.json :
npm init -y
接著輸入以下:
>npm install --save-dev typescript
最終可以完成相異的設定檔案,並且我們知道 typescript@5.8.3 版本所需的 NodeJs 至少需要 14.17 以上 ※補充: Node 14 已在 2023-04 EOL,安全更新停止;長期來看風險高
這在 Jenkins 自動化建置時,可以知道安裝的 NodeJs 版本需要多少
最後進行 Git 版控簽入,完成所有專案的配置
進入 Jenkins 主機後 -> 管理Jenkins -> Plugins -> 打開 Available plugins
找到 NodeJS 插件進行安裝,並且重新啟動 Jenkins 讓插件生效
進入 Jenkins 主機後 -> 管理Jenkins -> Tools
找到 NodeJS 的配置地方,設定名稱,並且安裝 NodeJs 14.17 版本以上
以下是完整的 groovy 腳本,關鍵在 ‘Frontend Install & Build TypeScript’ 與 ‘Backend Building’
成功區分出前端的 Typescript 建置與專案的後端代碼建置,解耦問題的 Stage
pipeline {
agent any
// 環境變數
environment {
PROJECT_NAME = "TypeScriptDeployExample"// 專案名稱
PROJECT_NAME_FOR_DOCKER = "typescriptdeployexample"// DockerName 強制小寫
GIT_SOURCE_REPOSITORY = "https://github.com/gotoa1234/MyBlogExample.git"// 專案來源
TARGET_MACHINE_IP = "192.168.51.93"// 對應的部署機器IP
TARGET_MACHINE_CREDENTIAL = "DeployMechineUbuntu"// 對應部署機器的SSH Server Name
}
// 定義單一建置時可異動參數
parameters {
string(name: 'GIT_HASH_TAG', defaultValue: '', description: '指定發布的GIT Hash 標籤(雜湊版號),預設 head 表示更新最新代碼')
}
stages {
// step 1. start
stage('Checkout') {
steps {
checkout([$class: 'GitSCM',
branches: [[name: "remotes/origin/main"]],
userRemoteConfigs: [[url: "${env.GIT_SOURCE_REPOSITORY}"]]
])
sh """
git pull origin main
"""
sh """
git checkout ${params.GIT_HASH_TAG}
"""
}
}
// step 1. end
// step 2-1. start
stage('Frontend Install & Build TypeScript') {
steps {
dir("${PROJECT_NAME}") { // <-- 進入含 package.json 的資料夾
nodejs('NodeJS24.3.0') { // 由 NodeJS 外掛掛好 PATH
sh 'npm ci' // 讀 package-lock.json
sh 'npx tsc' // 編譯 .ts → .js
}
}
}
}
// step 2-1. end
// step 2-2. start
stage('Backend Building') {
steps {
script {
nodejs('NodeJS24.3.0') {
sh """
dotnet publish ${PROJECT_NAME}/${PROJECT_NAME}.csproj -c Release -o publish/${PROJECT_NAME} --disable-build-servers
"""
}
}
}
}
// step 2-2. end
// step 3. start
stage('Publish Main Host') {
steps {
sshPublisher(publishers:
[sshPublisherDesc(configName: "${env.TARGET_MACHINE_CREDENTIAL}",
transfers: [
sshTransfer(cleanRemote: true,
excludes: '',
execCommand: '',
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: "var\\dockerbuildimage\\${PROJECT_NAME}\\publish",
remoteDirectorySDF: false,
removePrefix: "publish\\${PROJECT_NAME}",
sourceFiles: "publish\\${PROJECT_NAME}\\**")],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: false)
])
}
}
// step 3. end
// step 4. start
stage('Publish DockerFile') {
steps {
sshPublisher(publishers:
[sshPublisherDesc(configName: "${env.TARGET_MACHINE_CREDENTIAL}",
transfers: [
sshTransfer(cleanRemote: false,
excludes: '',
execCommand: '',
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: "var\\dockerbuildimage\\${PROJECT_NAME}",
remoteDirectorySDF: false,
removePrefix: "${PROJECT_NAME}",
sourceFiles: "${PROJECT_NAME}\\Dockerfile")],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: false)
])
}
}
// step 4. end
// step 5. start
stage('Build Image Remotely') {
steps {
sh """
echo cd /var/dockerbuildimage/${env.PROJECT_NAME}
echo docker build --no-cache -t ${env.PROJECT_NAME_FOR_DOCKER} .
echo docker tag ${env.PROJECT_NAME_FOR_DOCKER}:latest ${env.PROJECT_NAME_FOR_DOCKER}:hash_${params.GIT_HASH_TAG}
"""
sshPublisher(
failOnError: true,
publishers: [sshPublisherDesc(
configName: "${env.TARGET_MACHINE_CREDENTIAL}",
transfers: [sshTransfer(
excludes: '',
execCommand: "cd /var/dockerbuildimage/${env.PROJECT_NAME} && \
docker build --no-cache -t ${env.PROJECT_NAME_FOR_DOCKER} . && \
docker tag ${env.PROJECT_NAME_FOR_DOCKER}:latest ${env.PROJECT_NAME_FOR_DOCKER}:hash_${params.GIT_HASH_TAG}",
execTimeout: 120000,
patternSeparator: '[, ]+')],
verbose: false)])
}
}
// step 5. end
// step 6. start
stage('ReConstruct Container') {
steps {
sshPublisher(
failOnError: true,
publishers: [sshPublisherDesc(
configName: "${env.TARGET_MACHINE_CREDENTIAL}",
transfers: [sshTransfer(
excludes: '',
execCommand: "sudo docker stop ${env.PROJECT_NAME_FOR_DOCKER} && \
docker rm ${env.PROJECT_NAME_FOR_DOCKER} || true && \
docker run --name ${env.PROJECT_NAME_FOR_DOCKER} -d -p 8095:8080 -p 8195:8081 \
--mount type=bind,source=/var/dockervolumes/${env.PROJECT_NAME}/appsettings.json,target=/app/appsettings.json \
--mount type=bind,source=/var/dockervolumes/${env.PROJECT_NAME}/appsettings.Development.json,target=/app/appsettings.Development.json \
${env.PROJECT_NAME_FOR_DOCKER}:latest",
execTimeout: 120000,
patternSeparator: '[, ]+')],
verbose: false)])
}
}
// step 6. end
// step 7. start
stage('Image Purne') {
steps {
sshPublisher(
failOnError: true,
publishers: [sshPublisherDesc(
configName: "${env.TARGET_MACHINE_CREDENTIAL}",
transfers: [sshTransfer(
excludes: '',
execCommand: "docker image prune -f",
execTimeout: 120000,
patternSeparator: '[, ]+')],
verbose: false)])
}
}
// step 7. end
}
}
執行 Jenkins 建置此 Job 後,可以觀察到 TypeScript 生成 Js 檔案,是一個獨立的 Stage
需要排查問題時,前端的 TypeScript 代碼撰寫編譯失敗時,就會在 ‘Frontend Install & Build TypeScript’ 報錯。
Potainer 觀察專案部署成功,成功運行網站 (若 TypeScript 編譯失敗,網站會無法成功運行)