Select an item from the sidebar to view content.
提供專屬於您的網站設計,確保符合品牌形象及業務需求,提升使用者體驗與吸引力。
打造適應多種裝置的網站,無論是桌面、平板還是手機,都能提供最佳瀏覽效果。
支援完整的電子商務功能,包括商品展示、購物車系統、線上付款整合等,助您輕鬆進行線上銷售。
建立穩定且高效的後端系統,包括資料庫管理、API整合及伺服器配置,支持網站的順暢運行。
針對網站進行速度優化及SEO優化,提高網站效能及搜尋引擎排名,吸引更多流量。
提供定期網站維護及內容更新服務,確保網站安全性與最新資訊同步。
隨時提供技術支援,解決問題並提供專業諮詢,協助您提升網站的運營效率。
JavaScript 是一種高效、靈活且具備動態特性的腳本語言,廣泛應用於前端和後端開發。以下是 JavaScript 的主要特點:
JavaScript 是一種動態型別語言,變數在宣告時不需要指定資料型別。這意味著變數的型別可以在程式執行過程中動態改變。
let value = 10; // 初始化為數字型
value = "Hello"; // 可以改為字串型
JavaScript 支援物件導向,並使用 原型繼承 的方式來實現物件的繼承與重用。這讓 JavaScript 能夠更靈活地定義和操作物件。
let person = {
name: "Alice",
greet: function() {
return "Hello, " + this.name;
}
};
在 JavaScript 中,函式也是物件,可以作為參數傳遞、返回,或賦值給變數。這特性支援許多函式式程式設計的模式。
function greet(name) {
return "Hello, " + name;
}
let sayHello = greet;
console.log(sayHello("Alice")); // 可以將函式作為變數使用
JavaScript 原生支援非同步操作,特別是透過 Promise 和 async/await 讓處理非同步行為更加直觀。這對於處理網路請求和計時事件等異步操作非常有用。
async function fetchData() {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
}
fetchData();
JavaScript 最初是用於網頁瀏覽器中的前端開發,但現在已廣泛應用於後端開發(如 Node.js)、桌面應用程式、行動應用程式甚至物聯網設備。
JavaScript 的靈活性與多樣性使其成為現代網頁開發的核心技術之一,並在各種應用領域中提供強大的支援。
在 JavaScript 中,布林值是一種基本資料類型,它只有兩種可能的狀態或值:
true:代表真、是、開啟或成功。false:代表假、否、關閉或失敗。布林值是所有邏輯判斷和流程控制的基礎,例如 if...else 語句和迴圈。
您可以直接將布林值賦給變數,或使用比較運算子來產生布林值。
let isLogged = true;
let isGuest = false;
// 比較運算子會回傳布林值
let isGreater = (10 > 5); // true
let isEqual = (5 === '5'); // false (因為資料類型不同)
這是布林值最常見的用途。if 語句會檢查括號內表達式的結果是否為 true,然後決定是否執行程式碼區塊。
let age = 20;
if (age >= 18) {
console.log("已成年,可以投票。");
} else {
console.log("未成年。");
}
let userValid = checkUserStatus(); // 假設回傳 true 或 false
if (userValid) {
// 如果 userValid 是 true
displayDashboard();
} else {
// 如果 userValid 是 false
showLoginError();
}
邏輯運算子用於結合或反轉布林值,主要有三種:
! (NOT):邏輯非,將布林值反轉。&& (AND):邏輯與,只有當兩邊都是 true 時才回傳 true。|| (OR):邏輯或,只要有一邊是 true 就回傳 true。
let isLoggedIn = true;
let hasPermission = false;
// ! (NOT)
let isNotLoggedIn = !isLoggedIn; // false
// && (AND)
if (isLoggedIn && hasPermission) {
console.log("用戶已登入且有權限。");
}
// || (OR)
if (isLoggedIn || isGuest) {
console.log("只要登入或為訪客就可以繼續。");
}
JavaScript 允許在需要布林值的地方使用其他資料類型,這時會進行**隱含轉換**。這些值會被視為 true 或 false:
false 的值):在條件判斷中,以下六個值會被視為 false:
false (布林值本身)0 (數字零)"" 或 '' (空字串)nullundefinedNaN (Not a Number)true 的值):除了上述六個 Falsy 值之外的**所有其他值**,包括:
"0" (非空字串)"false" (非空字串)1, -100)[]{}
let myString = "Hello";
let myNumber = 0;
if (myString) {
// myString 是 "Hello",為 Truthy 值,會執行
console.log("字串非空。");
}
if (myNumber) {
// myNumber 是 0,為 Falsy 值,不會執行
console.log("數字非零。");
}
if ({}) {
// 空物件 {} 是 Truthy 值,會執行
console.log("物件已存在。");
}
JavaScript 陣列(Array)是一種用於儲存多個值的變數。陣列中的每個值都稱為一個元素(Element),每個元素都有一個數字索引(Index),從 0 開始。
// 建立陣列
let fruits = ["Apple", "Banana", "Cherry", "Date"];
// 存取陣列元素
console.log(fruits[0]); // 輸出: Apple
console.log(fruits.length); // 輸出: 4
// 新增元素到陣列尾部
fruits.push("Elderberry");
console.log(fruits); // 輸出: ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
// 移除陣列尾部元素
let lastFruit = fruits.pop();
console.log(lastFruit); // 輸出: Elderberry
console.log(fruits); // 輸出: ["Apple", "Banana", "Cherry", "Date"]
slice() 方法會回傳一個新的陣列,其中包含從原始陣列中選定的元素。它不會修改原始陣列。
語法:array.slice(start, end)
start (可選): 擷取的起始索引(包含)。如果未指定,則從索引 0 開始。end (可選): 擷取的結束索引(不包含)。如果未指定,則擷取到陣列末尾。let numbers = [10, 20, 30, 40, 50, 60, 70]; // 從索引 2 (30) 開始到結尾 let slice1 = numbers.slice(2); console.log(slice1); // 輸出: [30, 40, 50, 60, 70] // 從索引 1 (20) 開始到索引 4 (50) 之前 let slice2 = numbers.slice(1, 4); console.log(slice2); // 輸出: [20, 30, 40] // 使用負數索引:-1 指向最後一個元素,-3 指向倒數第三個元素 (50) let slice3 = numbers.slice(-3, -1); console.log(slice3); // 輸出: [50, 60] // 複製整個陣列 let copy = numbers.slice(); console.log(copy); // 輸出: [10, 20, 30, 40, 50, 60, 70] console.log(numbers); // 原始陣列未被修改
以下函式提供從陣列中隨機選取指定數量(例如 5 個)元素的有效方法。這個方法會確保選出的元素是不重複的。
/**
* 從陣列中隨機選取指定數量的元素
* @param {Array} arr 原始陣列
* @param {number} count 要選取的元素數量 (預設為 5)
* @returns {Array} 包含隨機選取元素的新陣列
*/
function getRandomElements(arr, count = 5) {
// 如果陣列元素少於或等於所需的數量,則回傳陣列的副本
if (arr.length <= count) {
return arr.slice();
}
// 建立一個原始陣列的副本,用於隨機選取
let tempArray = arr.slice();
let result = [];
for (let i = 0; i < count; i++) {
// 隨機產生一個索引
let randomIndex = Math.floor(Math.random() * tempArray.length);
// 將選中的元素加入結果陣列
result.push(tempArray[randomIndex]);
// 將選中的元素從 tempArray 中移除,確保不會重複選取
tempArray.splice(randomIndex, 1);
}
return result;
}
let largeArray = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T"
];
let randomFive = getRandomElements(largeArray, 5);
console.log(randomFive); // 輸出範例: ["P", "A", "H", "R", "M"] (每次執行結果不同)
您可以使用 concat 方法將兩個陣列合併:
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let result = arr1.concat(arr2);
console.log(result); // [1, 2, 3, 4, 5, 6]
使用展開運算符(spread operator)也是一個常見的方式:
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let result = [...arr1, ...arr2];
console.log(result); // [1, 2, 3, 4, 5, 6]
如果您希望將第二個陣列的元素加入到第一個陣列中,可以結合 push 與展開運算符:
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr1.push(...arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]
將 console.log('op_panel_htm()') 改為動態取得函式名稱的方式輸出,例如 console.log(get_curr_method_name())。
function get_curr_method_name() {
const stack = new Error().stack;
const lines = stack.split("\n");
// 第0行是 Error,第1行是 get_curr_method_name,第2行是呼叫它的函式
const callerLine = lines[2] || '';
const match = callerLine.match(/at (.+?) \(/);
return match ? match[1] : 'anonymous';
}
TableOperator.prototype.op_panel_htm = function () {
console.log(get_curr_method_name());
};
如果你這樣執行:
const t = new TableOperator();
t.op_panel_htm();
可能會在控制台輸出:
TableOperator.op_panel_htm
實際輸出取決於瀏覽器堆疊格式,Chrome/Edge 一般會輸出函式完整路徑。
Error().stack,不同瀏覽器格式略有差異。可將其封裝成自訂除錯函式:
function debug_log(msg = '') {
console.log(`[${get_curr_method_name()}] ${msg}`);
}
使用方式:
debug_log("初始化完成");
輸出結果如:
[TableOperator.op_panel_htm] 初始化完成
當 JavaScript 專案變大、函式眾多時,應透過模組化與分類來提升維護性與可讀性。以下說明如何有效管理多個函式,包括是否使用 class。
將相關函式依功能分類儲存在不同的檔案中,例如:
/utils/mathUtils.js → 數學運算相關函式
/utils/domUtils.js → DOM 操作函式
/modules/tableOperator.js → 表格操作相關 class 或函式
每個檔案使用 export / import 語法:
// mathUtils.js
export function add(a, b) {
return a + b;
}
// main.js
import { add } from './utils/mathUtils.js';
console.log(add(2, 3));
將具相關邏輯的函式包裝在一個物件或 class 中更具結構性,例如:
// domUtils.js
export const DomUtils = {
createElement(tag) {
return document.createElement(tag);
},
removeElement(el) {
el.parentNode.removeChild(el);
}
};
或使用 class:
// tableOperator.js
export class TableOperator {
constructor(tableId) {
this.table = document.getElementById(tableId);
}
highlightRow(index) {
// ...
}
sortByColumn(colIndex) {
// ...
}
}
如果只是工具性函式(如字串處理、數學運算),建議使用 export function 或 export const obj = { ... } 管理即可。
/src
/utils
stringUtils.js
mathUtils.js
/components
tableOperator.js
chartViewer.js
main.js
JavaScript 中的 class 使用與其他 OOP 語言類似,包含建構子 constructor、成員變數與方法(函式)定義。
class Person {
// 成員變數(public)
name;
age;
// 建構子
constructor(name, age) {
this.name = name;
this.age = age;
}
// 成員方法
greet() {
console.log(`你好,我是 ${this.name},今年 ${this.age} 歲`);
}
// 靜態方法(無需實例化)
static species() {
return '人類';
}
}
const p = new Person("小明", 25);
p.greet(); // 輸出:你好,我是 小明,今年 25 歲
console.log(p.name); // 小明
console.log(Person.species()); // 人類
this.name,可由外部存取。
class Counter {
#count = 0; // 私有變數
increment() {
this.#count++;
console.log(this.#count);
}
#secret() {
console.log("這是私有方法");
}
revealSecret() {
this.#secret(); // 可以內部呼叫
}
}
class Animal {
speak() {
console.log("發出聲音");
}
}
class Dog extends Animal {
speak() {
console.log("汪汪!");
}
}
class 方法中的 this 指向該實例。但若在事件或 callback 中使用,請用 bind() 或箭頭函式確保指向。
class Button {
constructor() {
this.label = "Click";
document.addEventListener("click", this.handleClick.bind(this));
}
handleClick() {
console.log(`你按了 ${this.label}`);
}
}
JavaScript 的 class 提供類似其他語言的封裝、繼承、方法與屬性支援,適合中大型專案使用,也方便與模組結合。若需更嚴謹型別與控制,可考慮搭配 TypeScript。
JavaScript 模組(module)提供一種將變數與函式封裝在檔案中的方式,可透過 import/export 機制實現類似 class 的分工與結構化功能,適合用於工具函式或單一功能邏輯單元。
| 功能 | class | module |
|---|---|---|
| 封裝資料與行為 | ✅ | ✅(透過 export 封裝) |
| 可實例化 | ✅ | ❌(以單例方式存在) |
| 繼承與多型 | ✅ | ❌(需手動管理) |
| 單一功能組件 | 可 | 更適合 |
// file: mathUtils.js
export const MathUtils = {
add(a, b) {
return a + b;
},
multiply(a, b) {
return a * b;
}
};
// file: main.js
import { MathUtils } from './mathUtils.js';
console.log(MathUtils.add(3, 4)); // 7
console.log(MathUtils.multiply(2, 5)); // 10
// file: stringHelper.js
export function toUpper(str) {
return str.toUpperCase();
}
export function truncate(str, len) {
return str.length > len ? str.slice(0, len) + '…' : str;
}
// file: main.js
import { toUpper, truncate } from './stringHelper.js';
console.log(toUpper("hello")); // HELLO
console.log(truncate("JavaScript", 5)); // JavaS…
// file: config.js
let config = {
env: "dev",
version: "1.0"
};
export function getConfig() {
return config;
}
export function setEnv(env) {
config.env = env;
}
// file: app.js
import { getConfig, setEnv } from './config.js';
console.log(getConfig()); // { env: "dev", version: "1.0" }
setEnv("prod");
console.log(getConfig()); // { env: "prod", version: "1.0" }
模組適合封裝工具函式、常數與設定,與 class 不同,模組在 載入時就自動執行且全域唯一,非常適合邏輯分類與程式架構整齊化。當需要可多次建立的物件實例或物件導向繼承時,則使用 class 較為適合。
function runExclusive(fn) {
let last = Promise.resolve();
return function (...args) {
last = last.then(() => fn(...args));
return last;
};
}
async function doTask(name) {
console.log(`Start ${name}`);
await new Promise(resolve => setTimeout(resolve, 2000));
console.log(`End ${name}`);
}
const doTaskOnce = runExclusive(doTask);
// 同時呼叫多次
doTaskOnce("A");
doTaskOnce("B");
doTaskOnce("C");
Start A
End A
Start B
End B
Start C
End C
將網頁上的特定 HTML 元素(如 $\lt$table$\gt$)儲存為本機 $\text{.html}$ 檔案,通常需要使用 JavaScript 取得該元素的 HTML 程式碼,然後透過瀏覽器的下載 API 來觸發儲存過程。這是一種客戶端(Client-side)的解決方案。
使用 $\text{document.getElementById}$ 或 $\text{document.querySelector}$ 選擇器來獲取您想要儲存的 $\lt$table$\gt$ 元素,並擷取其 $\text{outerHTML}$。
var tableElement = document.getElementById('myTableId');
var tableHtml = tableElement.outerHTML;
僅有 $\lt$table$\gt$ 程式碼不足以構成一個完整的 $\text{.html}$ 檔案。您需要將它包裹在基本的 $\lt$html$\gt$、$\lt$head$\gt$ 和 $\lt$body$\gt$ 標籤中,以確保該檔案可以在瀏覽器中正確打開和顯示。同時,您也應該包含任何必要的 $\lt$style$\gt$ 標籤或 $\lt$link$\gt$ 標籤來保留表格的樣式(CSS)。
var fullHtml = '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>儲存的表格</title></head><body>' + tableHtml + '</body></html>';
Blob(Binary Large Object)物件用於儲存二進位或類二進位資料。我們將包含完整 HTML 程式碼的字串轉換為 $\text{text/html}$ 類型的 $\text{Blob}$。
var blob = new Blob([fullHtml], { type: 'text/html' });
利用 $\text{URL.createObjectURL}$ 創建一個指向 $\text{Blob}$ 的本地 $\text{URL}$,並將其賦予一個 $\lt$a$\gt$ 元素的 $\text{href}$ 屬性,同時設定 $\text{download}$ 屬性來指定下載的檔案名稱。
var a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'exported_table.html'; // 指定下載的檔案名稱
a.click(); // 模擬點擊,觸發下載對話框
// 釋放資源
URL.revokeObjectURL(a.href);
如果您的表格樣式是透過外部 $\text{CSS}$ 檔案或頁面上的 $\text{JavaScript}$ 動態添加的,那麼簡單地儲存 $\text{outerHTML}$ 可能會遺失這些樣式或功能。要解決這個問題:
模擬 C/C++ 的 #ifdef DEBUG 條件編譯機制,在 JavaScript 中實現可控制的 debug_log(),依據開發階段(開發中、測試中、正式版)控制日誌的輸出層級與開關。
// 0 = 無輸出, 1 = 錯誤, 2 = 警告, 3 = 訊息, 4 = 除錯
const LOG_LEVEL = 3;
function debug_log(msg, level = 3) {
if (level <= LOG_LEVEL) {
const name = get_curr_method_name();
console.log(`[${name}] ${msg}`);
}
}
function get_curr_method_name() {
const stack = new Error().stack;
const lines = stack.split("\n");
const callerLine = lines[2] || '';
const match = callerLine.match(/at (.+?) \(/);
return match ? match[1] : 'anonymous';
}
function initApp() {
debug_log("初始化中", 4); // 除錯級別
debug_log("載入設定成功", 3); // 訊息
debug_log("設定值可能過舊", 2); // 警告
debug_log("載入失敗", 1); // 錯誤
}
LOG_LEVEL = 4(全開)LOG_LEVEL = 2(只顯示錯誤與警告)LOG_LEVEL = 0(完全靜默)搭配工具如 Webpack/Vite/ESBuild,可透過條件常數 process.env.NODE_ENV 移除日誌:
if (process.env.NODE_ENV === 'development') {
debug_log("開發環境中的訊息");
}
也可以搭配 Babel 外掛(如 babel-plugin-transform-remove-console)在打包時移除所有 console.*。
雖然 JavaScript 沒有條件編譯,但可以透過變數控制與打包優化策略,實現類似 C++ 的除錯等級與發佈控制機制,確保開發階段資訊完整,正式版本乾淨無雜訊。
瀏覽器 Console(控制台)是開發者工具的一部分,用來:
F12 或 Ctrl + Shift + I → 點選「Console」分頁Cmd + Option + Iconsole.log("訊息"):印出訊息$0:選取 Elements 面板中目前選到的元素document.querySelector():選取 DOM 元素dir(obj):列出物件屬性copy(obj):複製變數到剪貼簿// 印出訊息
console.log("Hello Console");
// 取得頁面元素並操作
const btn = document.querySelector("button");
btn.textContent = "新標籤";
btn.click();
// 查看物件
console.dir(btn);
---
若 Console 出現以下訊息,表示 Chrome 為安全起見禁止直接貼上多行程式碼:
Don’t paste code into the DevTools Console ... Please type ‘allow pasting’ below and press Enter
輸入 allow pasting 並按 Enter,即可解除限制。
clear():清除 Console 訊息inspect(element):直接跳到 Elements 面板定位該元素$ 和 $$:是 querySelector 與 querySelectorAll 的縮寫Chrome 為了安全,防止惡意網站或他人誘導貼上危險程式碼,預設禁止直接貼上多行程式碼到 Console。
出現訊息:
Warning: Don’t paste code into the DevTools Console that you don’t understand or haven’t reviewed yourself.
This could allow attackers to steal your identity or take control of your computer.
Please type ‘allow pasting’ below and press Enter to allow pasting.
---
allow pasting(不含引號)// 1. Console 輸入 allow pasting 然後按 Enter
allow pasting
// 2. 再貼上你要執行的 JavaScript 程式碼
const host = document.querySelector('i18n-message[key="login.button.loginWithLine"]');
// ...(後續程式碼)
---
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
document.getElementById("result").innerHTML = xhr.responseText;
}
};
xhr.open("GET", "data.php", true);
xhr.send();
</script>
<script>
var xhr = new XMLHttpRequest();
xhr.open("POST", "submit.php", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
alert("伺服器回應:" + xhr.responseText);
}
};
xhr.send("name=小明&score=90");
</script>
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var json = JSON.parse(xhr.responseText);
console.log("姓名:" + json.name);
}
};
xhr.send();
</script>
$.ajax({
url: "data.php",
method: "GET",
success: function(response) {
$("#result").html(response);
},
error: function(xhr) {
alert("錯誤:" + xhr.status);
}
});
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
console.log("回應內容:" + xmlhttp.responseText);
}
};
xmlhttp.open("GET", "api/data.php", true);
xmlhttp.send();
xmlhttp.open("POST", "api/save.php", true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send("name=測試&value=123");
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var data = JSON.parse(xmlhttp.responseText);
console.log("狀態:" + data.status);
}
};
<script>
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
try {
var json = JSON.parse(xmlhttp.responseText);
console.log("資料接收成功:", json);
} catch (e) {
console.error("回傳內容不是有效 JSON", xmlhttp.responseText);
}
}
};
xmlhttp.open("GET", "data.php", true);
xmlhttp.send();
</script>
error_reporting(0) 關閉錯誤輸出。
<br />
<b>Deprecated</b>: Facebook\FacebookApp implements the Serializable interface...
...
{ "status": "ok", "data": [...] }
error_reporting(0);
ini_set('display_errors', 0);
這樣可以讓 PHP 不輸出任何錯誤或警告訊息。
<?php
error_reporting(0);
ini_set('display_errors', 0);
header('Content-Type: application/json');
// 假設這是要回傳的資料
$data = ["status" => "ok", "value" => 123];
echo json_encode($data);
exit;
?>
composer update facebook/graph-sdk
或檢查 composer.json 中的版本需求,使用符合 PHP 8.1 的版本。
let response = xmlhttp.responseText;
let jsonStart = response.indexOf("{");
if (jsonStart >= 0) {
let jsonStr = response.substring(jsonStart);
let json = JSON.parse(jsonStr);
// 使用 json 處理資料
}
<div id="msg"></div>
<script>
let 名字 = "小明";
document.getElementById("msg").textContent = 名字;
</script>
<div id="box"></div>
<script>
let 顏色 = "red";
document.getElementById("box").innerHTML =
`<p style="color:${顏色}">文字顏色:${顏色}</p>`;
</script>
<a id="link">連結</a>
<input id="nameInput">
<script>
let 網址 = "https://example.com";
let 名稱 = "小華";
document.getElementById("link").href = 網址;
document.getElementById("nameInput").value = 名稱;
</script>
<img id="avatar" alt="使用者頭像">
<script>
let 圖片來源 = "https://example.com/avatar.png";
document.getElementById("avatar").setAttribute("src", 圖片來源);
</script>
<div id="card"></div>
<script>
let 使用者ID = 456;
document.getElementById("card").dataset.userid = 使用者ID; // 會對應到 data-userid="456"
</script>
<a id="myLink">連結</a>
<input id="myInput">
<script>
let 網址 = "https://example.com";
let 名稱 = "小明";
document.getElementById("myLink").href = 網址;
document.getElementById("myInput").value = 名稱;
</script>
<img id="avatar">
<script>
let 圖片來源 = "https://example.com/avatar.png";
document.getElementById("avatar").setAttribute("src", 圖片來源);
</script>
<div id="card"></div>
<script>
let 使用者代號 = 123;
document.getElementById("card").dataset.userid = 使用者代號; // 會對應到 data-userid="123"
</script>
<div id="container"></div>
<script>
let 標題 = "動態標題";
document.getElementById("container").innerHTML =
`<h3 data-title="${標題}">${標題}</h3>`;
</script>
以下範例會在一個指定的父元素下,加入一個 <div class="abc"> 的子元素:
// 假設有一個父元素
const parent = document.getElementById("myParent");
// 建立 div 元素
const child = document.createElement("div");
// 設定 class
child.className = "abc"; // 或使用 classList.add("abc");
// 加入到父元素
parent.appendChild(child);
<div id="myParent"></div>
<script>
const parent = document.getElementById("myParent");
const child = document.createElement("div");
child.className = "abc";
child.textContent = "這是新增的 DIV";
parent.appendChild(child);
</script>
child.classList.add("abc", "def")id、style、dataset 等屬性將會在 #myParent 中出現:
<div class="abc">這是新增的 DIV</div>
querySelectorAll() 使用 CSS 選擇器語法來選取所有符合條件的元素,並回傳一個靜態的 NodeList。
// 選取所有類別為 "item" 的 <div>
let items = document.querySelectorAll("div.item");
// 使用 forEach 遍歷結果
items.forEach(item => {
item.style.color = "blue";
});
與 querySelectorAll() 相似,但它只會回傳符合條件的「第一個」元素。如果找不到,則回傳 null。
// 選取第一個 ID 為 "main-title" 的元素
let title = document.querySelector("#main-title");
// 選取頁面中第一個 <p> 標籤
let firstPara = document.querySelector("p");
這是最快且最直接的方法,專門用於透過唯一的 ID 選取單一元素。
let header = document.getElementById("header-section");
根據類別名稱選取元素,回傳一個「即時」(Live)的 HTMLCollection。當 DOM 結構改變時,此集合會自動更新。
let buttons = document.getElementsByClassName("btn-submit");
// 注意:不能直接對 HTMLCollection 使用 forEach,需先轉換成陣列
Array.from(buttons).forEach(btn => console.log(btn));
根據標籤名稱(如 div, p, span)選取元素,同樣回傳一個即時的 HTMLCollection。
let allLinks = document.getElementsByTagName("a");
console.log("頁面共有 " + allLinks.length + " 個連結");
| 方法 | 回傳類型 | 參數類型 | 性能 |
|---|---|---|---|
| querySelector | 單個 Element | CSS 選擇器 | 普通 |
| querySelectorAll | NodeList (靜態) | CSS 選擇器 | 普通 |
| getElementById | 單個 Element | ID 字串 | 最快 |
| getElementsByClassName | HTMLCollection (即時) | 類別字串 | 快 |
querySelectorAll 回傳的 NodeList 是靜態的,這意味著即便稍後在 DOM 中刪除了某個元素,該清單內容也不會改變。而 getElementsBy... 系列回傳的 HTMLCollection 是動態的,會隨時反映 DOM 的最新狀態。
當你想選取同時具備多個條件的元素時,只需將選擇器緊貼在一起,中間不留空格。
// 選取類別同時包含 "btn" 且包含 "primary" 的元素
let primaryButtons = document.querySelectorAll(".btn.primary");
// 選取 class 為 "item" 的 <li> 標籤
let listItems = document.querySelectorAll("li.item");
如果你想選取符合條件 A 或條件 B 的所有元素,請使用「逗號」, 分隔不同的選擇器。
// 選取所有的 <h1> 以及所有的 <h2>
let headers = document.querySelectorAll("h1, h2");
// 選取類別為 "active" 的元素或類別為 "highlight" 的元素
let markers = document.querySelectorAll(".active, .highlight");
根據 HTML 屬性的存在與否或屬性值進行過濾。這在處理表單或自定義數據屬性時非常有用。
// 選取所有具有 "disabled" 屬性的 <button>
let disabledBtns = document.querySelectorAll("button[disabled]");
// 選取所有 name 屬性以 "user" 開頭的 <input> (使用 ^=)
let userInputs = document.querySelectorAll("input[name^='user']");
// 選取所有 data-type 屬性剛好等於 "video" 的元素
let videos = document.querySelectorAll("[data-type='video']");
利用元素之間的結構關係進行深度篩選。
// 後代選擇器:選取 #nav 內部所有的 <a> (不限層級)
let navLinks = document.querySelectorAll("#nav a");
// 子代選擇器:選取 .container 直接下一層的 <div> (僅限第一層)
let directDivs = document.querySelectorAll(".container > div");
// 相鄰兄弟選擇器:選取緊跟在 <h1> 後面的第一個 <p>
let firstParas = document.querySelectorAll("h1 + p");
這在處理清單或狀態篩選時極其強大。
// 選取表格中所有的偶數列 (第 2, 4, 6...)
let evenRows = document.querySelectorAll("tr:nth-child(even)");
// 選取目前被勾選 (checked) 的所有核取方塊
let checkedBoxes = document.querySelectorAll("input[type='checkbox']:checked");
// 選取不具備 "hidden" 類別的所有 <section> (使用 :not)
let visibleSections = document.querySelectorAll("section:not(.hidden)");
你可以組合上述所有規則來建立複雜的查詢條件。
// 選取 ID 為 "main" 容器內,所有 class 為 "card" 且具有 "data-id" 屬性的 <div>
let complexQuery = document.querySelectorAll("#main div.card[data-id]");
// 同時選取第一個 <li> 以及最後一個 <li>
let endpoints = document.querySelectorAll("li:first-child, li:last-child");
要找到 currElem 之後的第一個 <footer> 元素,最可靠的方法是遍歷文件中的所有 <footer> 元素,並使用 Node.compareDocumentPosition() 方法來判斷它們相對於 currElem 的位置。
document.querySelectorAll('footer') 獲取頁面上所有的 <footer> 元素。<footer> 元素,調用 currElem.compareDocumentPosition(footer)。如果結果的位元遮罩(bitmask)中包含 $4$(NODE_FOLLOWING),則表示該 <footer> 在文件順序上位於 currElem 之後。<footer>,即可停止遍歷。找到目標 <footer> 元素後,可以使用現代 DOM 接口 Element.before() 將新內容插入到該元素之前。
targetFooter.before(contentToAdd);
這裡的 contentToAdd 可以是一個字串(會被轉換為文字節點)或一個或多個 DOM 節點/元素。
function addContentBeforeNextFooter(currElem, contentToAdd) {
// 1. 獲取文件中的所有 footer 元素
const allFooters = document.querySelectorAll('footer');
let nextFooter = null;
// 2. 遍歷並找到在 currElem 之後的第一個 footer
for (const footer of allFooters) {
// 檢查 footer 是否跟隨在 currElem 之後 (位元遮罩 4)
// 4: The argument node follows the reference node.
if (currElem.compareDocumentPosition(footer) & 4) {
nextFooter = footer;
break; // 找到第一個後立即停止
}
}
// 3. 如果找到目標 footer,則在其前面插入內容
if (nextFooter) {
nextFooter.before(contentToAdd);
console.log('內容已成功插入到 currElem 之後的第一個 footer 之前。');
return nextFooter;
} else {
console.log('在 currElem 之後未找到 footer 元素。');
return null;
}
}
// 範例:創建要插入的內容
const newDiv = document.createElement('div');
newDiv.textContent = '這是新插入的內容。';
// 假設 currElem 是您已經獲取到的某個 DOM 元素
// const currElem = document.getElementById('some-id');
// addContentBeforeNextFooter(currElem, newDiv);
這個更新後的函式 findNearestFooter 允許您指定一個方向('before' 或 'after')來尋找最接近 currElem 的 <footer> 元素。
Node.compareDocumentPosition() 方法的位元遮罩來確定 <footer> 是在 currElem 之前還是之後。'after'):判斷是否包含位元 $4$(NODE_FOLLOWING)。
document.querySelectorAll 已經按文件順序排列,第一個找到且符合條件的 <footer> 就是目標。'before'):判斷是否包含位元 $2$(NODE_PRECEDING)。
document.querySelectorAll 是從上到下排序,我們需要**反向**遍歷列表,第一個找到且符合條件的 <footer> 才是最接近 currElem 的那一個。
/**
* 尋找在 currElem 之前或之後的第一個 <footer> 元素,並在其前後插入內容。
* @param {HTMLElement} currElem - 作為參考點的元素。
* @param {('before'|'after')} direction - 尋找的方向 ('before' 或 'after')。
* @param {HTMLElement|string} contentToAdd - 要插入的內容 (元素或字串)。
* @returns {HTMLElement|null} - 找到的 footer 元素,如果找不到則為 null。
*/
function findNearestFooter(currElem, direction, contentToAdd) {
const allFooters = document.querySelectorAll('footer');
let nearestFooter = null;
if (direction === 'after') {
// 尋找「之後」的第一個 footer
for (const footer of allFooters) {
// 4 (NODE_FOLLOWING): footer 在 currElem 之後
if (currElem.compareDocumentPosition(footer) & 4) {
nearestFooter = footer;
break; // 找到第一個就停止,因為它是最接近的
}
}
// 插入內容 (在找到的 footer 之前)
if (nearestFooter) {
nearestFooter.before(contentToAdd);
console.log('內容已插入到 currElem 之後的第一個 footer 之前。');
}
} else if (direction === 'before') {
// 尋找「之前」的第一個 footer
// 必須從列表尾部反向遍歷,才能找到最接近 currElem 的 footer
for (let i = allFooters.length - 1; i >= 0; i--) {
const footer = allFooters[i];
// 2 (NODE_PRECEDING): footer 在 currElem 之前
if (currElem.compareDocumentPosition(footer) & 2) {
nearestFooter = footer;
break; // 找到第一個 (反向遍歷中的第一個) 就停止
}
}
// 插入內容 (在找到的 footer 之後)
// 由於我們要求是找到「之前」的 footer,通常會將內容插入到該 footer 之後
if (nearestFooter) {
// 為了與您上一個要求 (在 footer 前面插入) 保持一致,我們仍假設是 before
// 如果要求在找到的 footer「之後」插入,應使用 nearestFooter.after(contentToAdd);
nearestFooter.before(contentToAdd);
console.log('內容已插入到 currElem 之前的第一個 footer 之前。');
}
}
if (!nearestFooter) {
console.log(`在 currElem ${direction === 'before' ? '之前' : '之後'} 未找到 footer 元素。`);
}
return nearestFooter;
}
// 範例:假設 currElem 和 contentToAdd 已定義
// const currElem = document.getElementById('some-reference');
// const newContent = document.createElement('p');
// newContent.textContent = '這是插入的內容';
// // 呼叫範例:尋找在 currElem 之後的第一個 footer
// findNearestFooter(currElem, 'after', newContent.cloneNode(true));
// // 呼叫範例:尋找在 currElem 之前的第一個 footer
// findNearestFooter(currElem, 'before', newContent.cloneNode(true));
document.querySelectorAll() 回傳的是一個 **NodeList**。NodeList 雖然不是真正的 JavaScript 陣列,但它具備 length 屬性和數字索引,因此可以使用 Array.from() 或展開運算符(Spread Operator ...)將其轉換為陣列,然後使用 Array.prototype.slice() 方法來取得開頭的子集。
/**
* 取得 querySelectorAll 回傳集合中的前 5 個元素。
* @param {string} selector - CSS 選擇器字串。
* @returns {Array} - 包含前 5 個元素的陣列 (如果元素不足 5 個,則回傳所有元素)。
*/
function getFirstFiveElements(selector) {
const nodeList = document.querySelectorAll(selector);
// 1. 將 NodeList 轉換為陣列
const allElements = Array.from(nodeList);
// 2. 使用 slice(0, 5) 取得前 5 個元素
const firstFive = allElements.slice(0, 5);
return firstFive;
}
// 範例用法:
// const topFiveDivs = getFirstFiveElements('div');
// console.log("前 5 個元素:", topFiveDivs);
要從集合中隨機取得元素,需要執行以下步驟:
最有效的方法通常是對陣列進行隨機排序,然後再取前五個。
/**
* 從 querySelectorAll 回傳集合中隨機取得 5 個元素。
* @param {string} selector - CSS 選擇器字串。
* @returns {Array} - 包含隨機 5 個元素的陣列 (如果元素不足 5 個,則回傳所有元素)。
*/
function getRandomFiveElements(selector) {
const nodeList = document.querySelectorAll(selector);
// 1. 將 NodeList 轉換為陣列
let allElements = Array.from(nodeList);
// 如果元素總數小於 5,則直接回傳所有元素
if (allElements.length <= 5) {
return allElements;
}
// 2. 實作 Fisher-Yates (或稱 Knuth) 隨機排序法
// 這是一種高效且公平的隨機排列方法。
for (let i = allElements.length - 1; i > 0; i--) {
// 產生 0 到 i 之間的隨機索引 j
const j = Math.floor(Math.random() * (i + 1));
// 交換 allElements[i] 和 allElements[j]
[allElements[i], allElements[j]] = [allElements[j], allElements[i]];
}
// 3. 取得經過隨機排序後陣列的前 5 個元素
const randomFive = allElements.slice(0, 5);
return randomFive;
}
// 範例用法:
// const randomFiveImages = getRandomFiveElements('img');
// console.log("隨機 5 個元素:", randomFiveImages);
響應式網頁設計是一種網站設計方法,使網站可以適應不同裝置的螢幕尺寸和解析度。這意味著無論是手機、平板電腦,還是桌面電腦,網站的內容都會自動調整,以提供最佳的使用體驗。
響應式設計的關鍵在於靈活的佈局、可調整的圖片以及CSS媒體查詢。這些技術可以讓網站依據裝置的寬度自動調整元素的大小和排列。
在CSS中使用媒體查詢來調整版面,例如:
@media (max-width: 600px) { body { font-size: 14px; } }
響應式設計可以改善用戶體驗,減少開發成本,並增加SEO效果。由於無需為每種裝置設計不同的版本,響應式網頁更加容易維護。
JavaScript 是前端程式語言,其程式碼會被下載到用戶的瀏覽器中執行,因此很容易被查看或複製。 雖然無法完全防止程式碼被逆向工程,但可以採取多種措施增加保護強度,並實現試用與正式版的授權機制。
使用工具將原始程式碼轉換為難以閱讀的格式。
npm install -g javascript-obfuscator
javascript-obfuscator input.js --output output.js
混淆後的程式碼範例:
var _0x1a2b=["\x68\x65\x6C\x6C\x6F"];console[_0x1a2b[0]]("Hello World!");
利用壓縮工具移除空白與註解,減少程式碼可讀性,例如使用 UglifyJS。
將重要邏輯存於伺服器,通過 API 提供服務,而非直接執行於前端。
fetch('https://example.com/api').then(response => response.json()).then(data => console.log(data));
在頁面中加入偵測機制,阻止開發者工具的使用。
document.addEventListener('keydown', (event) => {
if (event.key === 'F12' || (event.ctrlKey && event.shiftKey && event.key === 'I')) {
event.preventDefault();
}
});
將試用與正式版邏輯與 API 結合,例如根據授權碼檢查用戶身份。
fetch('https://example.com/verify_license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey: userLicenseKey })
}).then(response => response.json()).then(data => {
if (data.valid) {
console.log('正式版啟用');
} else {
console.log('試用版限制功能');
}
});
在試用版中禁用部分功能,例如 UI 元件的可用性或數據存取權限。
基於用戶的首次使用日期,設置試用期檢查邏輯。
const startDate = localStorage.getItem('startDate') || new Date();
localStorage.setItem('startDate', startDate);
if (new Date() - new Date(startDate) > 14 * 24 * 60 * 60 * 1000) {
console.log('試用期已結束');
} else {
console.log('試用期內');
}
在網頁中,監聽滾動位置的變更可以實現許多動態效果,例如顯示返回頂部按鈕、懸浮導航欄等。使用 JavaScript 可以輕鬆實現這類事件的監聽。
在 JavaScript 中,可以透過 window.addEventListener('scroll', handler) 方法來監聽滾動事件。在滾動時觸發 handler
函式,進行相應的處理。
以下範例顯示當前頁面滾動的垂直位置,並在頁面下方顯示。
<script>
// 定義滾動事件處理函式
function handleScroll() {
const scrollPosition = window.scrollY; // 獲取垂直滾動位置
document.getElementById("scrollPositionDisplay").textContent = "目前滾動位置:" + scrollPosition + " px";
}
// 添加滾動事件監聽器
window.addEventListener('scroll', handleScroll);
</script>
<div id="scrollPositionDisplay" style="position: fixed; bottom: 20px; left: 20px; background-color: #eee; padding: 10px;">
目前滾動位置:0 px
</div>
在滾動事件中添加太多運算可能會影響性能,建議使用 requestAnimationFrame 或 setTimeout 進行節流(throttling),減少事件觸發的頻率。
監聽滾動位置變更的事件可用於多種動態效果,從而增強網頁的交互性。透過簡單的 JavaScript 即可實現滾動事件的監聽,提升用戶體驗。
width: max-content這種方法讓元素的寬度根據內容自動調整,同時保持在新行。
pre {
display: inline-block;
width: max-content;
}
效果:
這是一段文字。
這是一個代碼區塊。
float: left此方法使用 float 讓元素浮動至左邊,並讓其新行開始。
pre {
display: inline-block;
float: left;
}
效果:
這是一段文字。
這是一個代碼區塊。
::before 虛擬元素透過虛擬元素強制元素換行,同時保持寬度自適應。
pre {
display: inline-block;
}
pre::before {
content: '';
display: block;
clear: both;
}
效果:
這是一段文字。
這是一個代碼區塊。
white-space: pre使用 white-space 控制換行行為,讓區塊寬度自然適應內容。
pre {
display: inline-block;
white-space: pre;
}
效果:
這是一段文字。
這是一個代碼區塊。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Windows App UI 模擬</title>
<style>
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
background-color: #f0f0f0;
}
.app-window {
width: 800px;
height: 500px;
margin: 50px auto;
background: #ffffff;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
overflow: hidden;
display: flex;
flex-direction: column;
}
.title-bar {
background: #0078d7;
color: #ffffff;
padding: 10px 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title-bar h1 {
font-size: 16px;
margin: 0;
}
.title-bar .buttons {
display: flex;
gap: 5px;
}
.title-bar .buttons button {
width: 20px;
height: 20px;
background: #ffffff;
border: none;
border-radius: 50%;
cursor: pointer;
}
.title-bar .buttons button:hover {
background: #e0e0e0;
}
.content {
flex: 1;
padding: 15px;
overflow: auto;
}
.sidebar {
width: 200px;
background: #f3f3f3;
border-right: 1px solid #ddd;
display: inline-block;
vertical-align: top;
}
.main-content {
display: inline-block;
width: calc(100% - 200px);
vertical-align: top;
}
.sidebar ul {
list-style: none;
margin: 0;
padding: 0;
}
.sidebar ul li {
padding: 10px 15px;
cursor: pointer;
border-bottom: 1px solid #ddd;
}
.sidebar ul li:hover {
background: #e9e9e9;
}
.main-content p {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div class="app-window">
<div class="title-bar">
<h1>My Windows App</h1>
<div class="buttons">
<button title="Minimize"></button>
<button title="Maximize"></button>
<button title="Close" style="background: #e81123;"></button>
</div>
</div>
<div class="content">
<div class="sidebar">
<ul>
<li onclick="showContent('Home')">Home</li>
<li onclick="showContent('Settings')">Settings</li>
<li onclick="showContent('About')">About</li>
</ul>
</div>
<div class="main-content" id="mainContent">
<p>Select an item from the sidebar to view content.</p>
</div>
</div>
</div>
<script>
function showContent(section) {
const content = {
Home: '<h2>Home</h2><p>Welcome to the home page.</p>',
Settings: '<h2>Settings</h2><p>Adjust your application preferences here.</p>',
About: '<h2>About</h2><p>This is a mock Windows App created using HTML and JavaScript.</p>',
};
document.getElementById('mainContent').innerHTML = content[section] || '<p>Content not found.</p>';
}
</script>
</body>
</html>
index.html。Select an item from the sidebar to view content.
React 是由 Facebook 開發的熱門 JavaScript 庫,適合構建動態且高效的使用者介面。其元件化設計使開發者能快速建立和重複使用 UI 元件。
Vue.js 是一個輕量且易於上手的框架,適合開發中小型應用程式。其簡單的 API 和直觀的設計讓快速設計 UI 成為可能。
Bootstrap 是一個前端框架,提供多種現成的 UI 元件和排版工具,能快速建構響應式網站。
Material-UI 是基於 Google Material Design 的 React UI 框架,包含許多精美的元件,適合快速設計高品質的介面。
Tailwind 是一個實用的 CSS 框架,結合 JavaScript 使用時能快速構建客製化的 UI。它的原子化設計讓開發者能靈活控制樣式。
Foundation 是一個響應式前端框架,提供多種 UI 元件與網格系統,適合快速建立現代化的網頁設計。
Chakra UI 是一個簡單且可客製化的 React UI 框架,提供直觀的 API 和多樣的元件。
Quasar 是基於 Vue.js 的 UI 框架,能快速構建響應式和跨平台的應用程式。
React 是由 Meta 開發的宣告式 JavaScript 函式庫,專注於建立使用者介面。它透過組件化開發與虛擬 DOM 技術,極大地提升了資料庫驅動型應用的開發效率與維護性。
在開發基於資料庫的應用時,React 通常結合現代化工具鏈來處理數據流:
雖然 React 開發可以使用任何純文字編輯器,但為了配合資料庫欄位提示與型別檢查,以下是業界最常用的選擇:
為了優化 React 與資料庫應用的開發流程,建議在 IDE 中安裝以下工具:
| 工具類型 | 熱門選擇 | 核心價值 |
|---|---|---|
| 程式碼編輯器 (IDE) | VS Code, WebStorm | 高效的代碼撰寫與型別提示 |
| 套件管理工具 | npm, pnpm, yarn | 管理 React 及其生態系依賴 |
| 建置工具 | Vite, Turbopack | 極速的熱更新與專案打包效能 |
| 除錯工具 | React DevTools | 即時監控組件狀態與性能瓶頸 |
import React, { useState, useEffect } from 'react';
function DatabaseView() {
// 模擬的資料庫資料
const [data, setData] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
// 模擬從資料庫獲取資料
useEffect(() => {
const fetchData = async () => {
const mockData = [
{ id: 1, name: 'John Doe', email: '[email protected]', age: 28 },
{ id: 2, name: 'Jane Smith', email: '[email protected]', age: 34 },
{ id: 3, name: 'Alice Brown', email: '[email protected]', age: 25 },
{ id: 4, name: 'Bob White', email: '[email protected]', age: 40 },
];
setData(mockData);
};
fetchData();
}, []);
// 過濾資料
const filteredData = data.filter(
(item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.email.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h1>Database View</h1>
<input
type="text"
placeholder="Search by name or email"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
style={{
marginBottom: '20px',
padding: '10px',
width: '300px',
fontSize: '16px',
}}
/>
<table
style={{
width: '100%',
borderCollapse: 'collapse',
marginTop: '10px',
}}
>
<thead>
<tr>
<th style={{ border: '1px solid #ddd', padding: '10px' }}>ID</th>
<th style={{ border: '1px solid #ddd', padding: '10px' }}>Name</th>
<th style={{ border: '1px solid #ddd', padding: '10px' }}>Email</th>
<th style={{ border: '1px solid #ddd', padding: '10px' }}>Age</th>
</tr>
</thead>
<tbody>
{filteredData.map((item) => (
<tr key={item.id}>
<td style={{ border: '1px solid #ddd', padding: '10px' }}>
{item.id}
</td>
<td style={{ border: '1px solid #ddd', padding: '10px' }}>
{item.name}
</td>
<td style={{ border: '1px solid #ddd', padding: '10px' }}>
{item.email}
</td>
<td style={{ border: '1px solid #ddd', padding: '10px' }}>
{item.age}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default DatabaseView;
create-react-app。DatabaseView.js。App.js 中匯入並使用 <DatabaseView />。React 與 PHP 之間並不直接混合程式碼,而是透過 JSON 格式的數據進行溝通。React 使用 fetch 或 axios 發送請求,PHP 處理完後回傳結果。
後端 PHP 程式碼不再輸出 HTML,而是輸出 JSON 標頭與數據。
<?php
// 允許跨域請求 (CORS)
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
// 模擬從資料庫取得數據
$stocks = ["AAPL", "TSLA", "NVDA", "GOOGL", "MSFT"];
echo json_encode($stocks);
?>
在 React 中,我們通常在 useEffect 鉤子中呼叫 PHP API。
import { useEffect, useState } from 'react';
function StockList() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://your-server.com/api.php")
.then(res => res.json())
.then(json => setData(json));
}, []);
return (
<ul>
{data.map(item => <li key={item}>{item}</li>)}
</ul>
);
}
| 類型 | 推薦方案 | 說明 |
|---|---|---|
| 傳統 PHP | 原生 PHP 或 CodeIgniter | 適合簡單的 API 介面。 |
| 現代框架 | Laravel | 最強大的 PHP 框架,內建完善的 API 支援。 |
| 混合方案 | Inertia.js | 不需要寫 API,就能讓 Laravel 與 React 無縫整合。 |
在開發環境中,由於 React 通常跑在localhost:5173,而 PHP 跑在localhost:8000,你會遇到 CORS (跨來源資源共享) 錯誤。請務必在 PHP 頂部加入Access-Control-Allow-Origin標頭。
Vue.js 是一套用於構建使用者介面的漸進式 JavaScript 框架。與其他大型框架不同,Vue 被設計為可以自底向上逐層應用,核心庫只專注於視圖層(View layer),非常容易上手且便於與既有專案整合。
Vue 結合了 HTML 模板與 JavaScript 物件,讓開發者能以直觀的方式管理網頁內容。目前主流推薦使用 Vue 3 的 Composition API。
<!-- Vue 模板 -->
<div id="app">
<h1>{{ message }}</h1>
<button @click="reverseMessage">反轉文字</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp, ref } = Vue;
createApp({
setup() {
const message = ref("Hello Vue!");
const reverseMessage = () => {
message.value = message.value.split('').reverse().join('');
};
return { message, reverseMessage };
}
}).mount('#app');
</script>
指令是帶有 v- 前綴的特殊屬性,負責將數據與 DOM 行為綁定。
| 指令 | 用途 | 範例 |
|---|---|---|
| v-bind (:) | 綁定 HTML 屬性 | :src="imageUrl" |
| v-on (@) | 監聽事件 | @click="doSomething" |
| v-if / v-else | 條件式渲染 | v-if="isVisible" |
| v-for | 列表渲染 (類似 Array 用法) | v-for="item in items" |
| v-model | 雙向數據綁定 | v-model="inputText" |
你可以輕鬆地在 Vue 中整合之前提到的 slice() 或隨機選取邏輯,並透過選取器觀念來操作組件。
<script setup>
import { ref } from 'vue';
const allStocks = ref(['AAPL', 'TSLA', 'GOOGL', 'MSFT', 'AMZN', 'NVDA', 'META']);
const selectedStocks = ref([]);
// 結合隨機選取邏輯
const pickFive = () => {
const shuffled = [...allStocks.value].sort(() => 0.5 - Math.random());
selectedStocks.value = shuffled.slice(0, 5);
};
</script>
<template>
<div>
<button @click="pickFive">隨機選取 5 檔股票</button>
<ul>
<li v-for="stock in selectedStocks" :key="stock">{{ stock }}</li>
</ul>
</div>
</template>
Web API(Web 應用程式介面)是一種基於 HTTP 協定的介面,允許不同系統之間交換資料,常用於前端與後端之間的溝通。
| 方法 | 用途 |
|---|---|
| GET | 取得資料 |
| POST | 新增資料 |
| PUT | 更新資料 |
| DELETE | 刪除資料 |
以下是一個使用 JavaScript 發送 GET 請求的範例:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('錯誤:', error));
請輸入一個中文字,系統將會顯示出該字的注音:
尚未查詢
Google ID登入是一種基於OAuth 2.0協議的認證方式,允許用戶使用其Google帳戶登入第三方網站或應用程式。這樣的方式不僅提高了用戶的便利性,還能增強安全性,因為用戶不需要記住額外的密碼。
<script src="https://apis.google.com/js/platform.js" async defer></script>
<div class="g_id_signin" data-type="standard" data-shape="rectangular" data-theme="outline" data-text="signin_with" data-size="large" data-logo_alignment="left"></div>
當用戶點擊登入按鈕後,Google會進行身份驗證。成功登入後,使用者的資訊(如電子郵件、名字等)會回傳給你的應用程式,你可以根據這些資訊進行後續的處理。
使用Google ID登入為用戶提供了一種簡單、安全的登入方式,並且減少了管理多個帳號的麻煩。透過整合Google的登入系統,開發者可以提高用戶體驗,並吸引更多的使用者使用其網站或應用程式。
若想嵌入 YouTube 影片,請將原本的連結修改為嵌入格式:
<iframe width="560" height="315" src="https://www.youtube.com/embed/{vid}" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
其中 {vid} 為 YouTube 影片的 ID。
例如,影片連結為 https://www.youtube.com/watch?v=dQw4w9WgXcQ,則嵌入代碼如下:
<iframe width="560" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
這樣的嵌入格式可以避免 X-Frame-Options 限制,讓影片可以正常顯示在頁面中。
在 HTML 中可以直接使用 <iframe> 內嵌 YouTube 影片,
但 YouTube 並不支援直接以 iframe 嵌入「搜尋結果頁」,
因為搜尋結果屬於互動式內容(含登入、推薦、追蹤等功能),會被 YouTube 的嵌入策略所阻擋。
以下嘗試嵌入搜尋結果會失敗或顯示錯誤訊息:
<!-- ⚠️ 無法正常顯示 -->
<iframe width="800" height="600"
src="https://www.youtube.com/results?search_query=cat+video">
</iframe>
此方式會被瀏覽器或 YouTube 攔截,顯示「拒絕顯示在 iframe 中」。
---
要在網頁中嵌入「搜尋結果」的效果,可以使用
YouTube Data API v3
動態查詢影片,然後用 JavaScript 自行顯示結果並建立 <iframe>。
前往 Google Cloud Console 啟用「YouTube Data API v3」,取得 API 金鑰。
<div>
<input id="search" type="text" placeholder="搜尋關鍵字">
<button onclick="searchYouTube()">搜尋</button>
</div>
<div id="results" style="display:grid;grid-template-columns:repeat(auto-fill,300px);gap:1em;margin-top:1em;"></div>
<script>
const API_KEY = "YOUR_API_KEY";
function searchYouTube() {
const q = document.getElementById("search").value;
const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&maxResults=6&q=${encodeURIComponent(q)}&key=${API_KEY}`;
fetch(url)
.then(r => r.json())
.then(data => {
const container = document.getElementById("results");
container.innerHTML = "";
data.items.forEach(item => {
const vid = item.id.videoId;
const title = item.snippet.title;
const frame = document.createElement("iframe");
frame.width = "300";
frame.height = "170";
frame.src = `https://www.youtube.com/embed/${vid}`;
frame.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
frame.allowFullscreen = true;
const div = document.createElement("div");
const caption = document.createElement("p");
caption.textContent = title;
caption.style.fontSize = "14px";
caption.style.fontWeight = "bold";
div.appendChild(frame);
div.appendChild(caption);
container.appendChild(div);
});
});
}
</script>
---
<iframe> 方式嵌入,可直接播放。iframe.src = "https://www.youtube.com/embed/" + videoId + "?autoplay=1" 自動播放。snippet.thumbnails 顯示縮圖而非立即嵌入。Node.js 是一個基於 Chrome V8 JavaScript 引擎的伺服器端執行環境,讓開發者可以使用 JavaScript 撰寫後端應用程式。
express:輕量級 Web 應用框架fs:檔案系統操作http:建立 HTTP 伺服器path:處理檔案路徑const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello, Node.js!');
});
server.listen(3000, () => {
console.log('伺服器已啟動,監聽埠口 3000');
});
Node.js 為前端開發者帶來統一語言的後端開發環境,提升開發效率並適合建構現代網路應用。
安裝 Node.js 有多種方式,但針對開發者,強烈建議使用版本管理工具(如 nvm)而非直接下載官網安裝檔。這樣可以避免未來權限錯誤,並能輕鬆切換不同版本。
這是最彈性的做法,適合需要長期開發的電腦環境。
nvm-setup.exe 並安裝。nvm install lts // 安裝最新的長期支援版本 (LTS) nvm use lts // 切換並啟用該版本 node -v // 確認安裝成功
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install --lts // 安裝 LTS 版本 node -v // 查看版本編號
如果你只需要快速安裝,不打算頻繁更換版本,可以前往 Node.js 官網。
安裝 Node.js 會同時包含以下核心元件:
| 組件名稱 | 說明 |
|---|---|
| Node.js 執行環境 | 讓 JavaScript 可以在伺服器端運作的核心。 |
| npm (Node Package Manager) | 全球最大的軟體註冊表,用於下載套件(如 Vue, React)。 |
| Corepack | 管理封裝管理員(如 Yarn, pnpm)的新工具。 |
安裝完成後,可以輸入以下指令確認所有工具都已就緒:
node -v // 顯示 Node 版本,如 v20.11.0 npm -v // 顯示 npm 版本,如 10.2.4
如果在 Windows 執行時出現「因為系統上禁止執行腳本」的錯誤,請以管理員身份開啟 PowerShell 並執行:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
nvm(Node Version Manager)是專為 Node.js 設計的版本管理工具。它允許你在同一台電腦上安裝並切換多個不同版本的 Node.js,這對於需要同時維護多個使用不同 Node 版本專案的開發者來說是必備工具。
在開發前端專案(如 Vue、React)時,不同的專案可能依賴特定的 Node.js 版本。直接在官網下載安裝檔只能安裝單一版本,而 nvm 解決了版本衝突的問題。
以下是開發過程中頻繁使用的 nvm 指令:
// 查看目前已安裝的所有版本 nvm ls // 查看雲端所有可供安裝的 Node.js 版本 nvm ls-remote // 安裝特定版本 (例如 v18.16.0) nvm install 18.16.0 // 切換到指定版本 nvm use 18.16.0 // 查看目前正在使用的版本 nvm current // 設定預設版本(每次開啟終端機時自動使用的版本) nvm alias default 18.16.0
當你執行 nvm use 時,nvm 會動態修改你系統的 PATH 環境變數,將指向 Node 執行檔的位址切換到對應的版本資料夾,從而實現無痛切換。
| 作業系統 | 推薦版本 / 工具 | 備註 |
|---|---|---|
| macOS / Linux | nvm (nvm-sh) | 最原始且穩定的版本。 |
| Windows | nvm-windows | 並非同一團隊開發,但功能幾乎一致,需下載 .exe 安裝。 |
在 Windows 上安裝 nvm-windows 之前,強烈建議先卸載電腦中原本已安裝的 Node.js,以避免環境變數衝突導致切換失敗。
NPM(Node Package Manager)是 Node.js 的套件管理工具,用來管理 JavaScript 程式碼的函式庫與模組。它讓開發者能夠安裝、分享與版本控制專案所需的各種套件。
npm init:初始化一個新的專案並建立 package.json 檔案。npm install:安裝 package.json 中列出的所有相依套件。npm install [套件名稱]:安裝指定套件。npm uninstall [套件名稱]:移除指定套件。npm update:更新已安裝的套件。package.json 是專案的核心檔案,記錄了專案的名稱、版本、描述、依賴套件等資訊,是 NPM 管理套件的基礎。
NPM 是最早也是最廣泛使用的 JavaScript 套件管理工具,但後來也出現了如 Yarn 等替代方案,提供更快的安裝速度與平行處理等功能。兩者皆可使用於大部分的 Node.js 專案。
Vite(法文意為「快速」)是新一代的前端建構工具,旨在提供極速的開發體驗。它主要由兩個部分組成:一個開發伺服器(基於原生 ES 模組)以及一套建構指令(使用 Rollup 進行打包)。
在 Vite 出現之前,Webpack 是主流工具,但隨著專案變大,編譯速度會變得非常緩慢。Vite 解決了這個痛點:
你可以透過一行指令,快速選擇要建立 Vue、React、Svelte 或純 JavaScript 專案。
// 使用 npm 建立專案 npm create vite@latest my-vue-app // 進入資料夾並安裝套件 cd my-vue-app npm install // 啟動開發環境 npm run dev
在專案的 package.json 中,你會看到以下常用腳本:
| 指令 | 用途 | 說明 |
|---|---|---|
| npm run dev | 啟動開發伺服器 | 本地開發時使用,支援即時更新。 |
| npm run build | 專案打包 | 產出高度優化的靜態檔案(放入 dist 資料夾)。 |
| npm run preview | 預覽打包結果 | 在本地模擬部署後的運行狀態。 |
Vite 的配置非常直觀,通常用於加入插件(Plugins)或設定伺服器代理(Proxy)。
import { defineConfig } from 'vite'
import vue from '@vue/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
port: 3000, // 修改開發伺服器埠號
open: true // 啟動時自動開啟瀏覽器
}
})
Webpack: 像是一台全功能的「大型加工廠」,在開始運作前必須處理完所有零件,較慢但功能極致齊全。
Vite: 像是「現代化快遞」,你需要什麼零件它才動態載入什麼,極速且符合現代瀏覽器標準。
Electron 是一個開源框架,讓開發者可以使用 HTML、CSS 和 JavaScript 建立跨平台的桌面應用程式。它結合了 Chromium 和 Node.js,讓網頁技術可以存取本機系統資源。
npm install --save-dev electron
// main.js
const { app, BrowserWindow } = require('electron');
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
<!DOCTYPE html>
<html>
<body>
<h1>你好,Electron!</h1>
</body>
</html>
npx electron .
electron-builder 可用來產生安裝檔TypeScript 是 JavaScript 的超集(Superset),由微軟開發。它在 JavaScript 的基礎上加入了「型別系統」(Type System),最終會被編譯成純 JavaScript 以便在瀏覽器中執行。它解決了 JavaScript 作為弱型別語言在大型專案中難以維護、容易出錯的問題。
透過在變數後方加上 : type 來宣告型別。
let name: string = "Gemini";
let age: number = 18;
let isStudent: boolean = true;
// 陣列型別
let stocks: string[] = ["AAPL", "TSLA", "NVDA"];
// 函式參數與回傳值型別
function add(a: number, b: number): number {
return a + b;
}
這是 TypeScript 最強大的功能之一,用來定義物件的結構(Shape)。
interface User {
id: number;
username: string;
email?: string; // 問號表示選填屬性
}
const newUser: User = {
id: 1,
username: "Alex"
};
現代開發環境(如 Vite)預設就支援 TypeScript。在建立專案時選取 vue-ts 或 react-ts,Vite 就會自動處理 tsconfig.json 配置。
// 使用 Vite 建立 TS 專案 npm create vite@latest my-ts-app -- --template vue-ts
將之前提到的隨機選取功能改寫為 TypeScript 版本,會變得更加安全:
// 泛型 <T> 讓此函式可以處理任何型別的陣列
function getRandomElements<T>(arr: T[], count: number = 5): T[] {
if (arr.length <= count) return [...arr];
const tempArray = [...arr];
const result: T[] = [];
for (let i = 0; i < count; i++) {
const randomIndex = Math.floor(Math.random() * tempArray.length);
result.push(tempArray.splice(randomIndex, 1)[0]);
}
return result;
}
const randomStocks = getRandomElements<string>(["AAPL", "TSLA", "GOOGL"], 2);
TypeScript 檔案(.ts)無法直接在瀏覽器執行,其生命週期如下:
| 階段 | 動作 | 工具 |
|---|---|---|
| 撰寫期 | 定義型別、撰寫 .ts 程式碼 | VS Code + TS Engine |
| 編譯期 | 檢查型別錯誤並移除型別標記 | tsc / Vite / esbuild |
| 執行期 | 在瀏覽器跑純 .js 檔案 | Browser Engine |
瀏覽器擴充功能(Browser Extension)是用來擴充瀏覽器功能的小型應用程式,通常以 HTML、CSS、JavaScript 撰寫。它可以修改網頁內容、增加工具列按鈕、與背景服務互動,提升使用者體驗。
manifest.json:描述擴充功能名稱、版本、權限與入口。popup.html:點擊工具列圖示後顯示的介面。background.js:背景常駐腳本,用於處理事件與邏輯。content.js:注入到網頁的腳本,可直接操作 DOM。chrome://extensions/ 載入。manifest.json 與相關檔案。my-extension/
├─ manifest.json
├─ popup.html
├─ popup.js
└─ icon.png
{
"manifest_version": 3,
"name": "Hello Extension",
"version": "1.0",
"description": "一個簡單的 Chrome 擴充功能範例",
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
}
},
"permissions": []
}
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
<style>
body { width:200px; font-family:sans-serif; text-align:center; }
button { margin-top:10px; padding:5px 10px; }
</style>
</head>
<body>
<h3>Hello from Chrome Extension</h3>
<button id="btn">Click me</button>
<script src="popup.js"></script>
</body>
</html>
document.getElementById("btn").addEventListener("click", () => {
alert("按鈕已點擊!");
});
放置一張 PNG 圖示(建議 128x128 大小),供工具列顯示。
chrome://extensions/Chrome 擴充功能是建立在標準網頁技術之上,因此其核心語言與網頁開發完全一致,這是必備的基礎。
為了建立更複雜和反應式的使用者介面(特別是彈出視窗和選項頁面),可以使用常見的前端框架。但要注意,在擴充功能的背景服務工作程式中,通常不建議使用重量級框架。
為了簡化開發流程、處理程式碼打包、熱重載(Hot Reloading)和跨瀏覽器相容性,使用現代化的開發工具是趨勢。
| 工具/套件 | 用途 | 優勢 |
|---|---|---|
| Vite / Webpack | 打包工具(Bundler) | 將多個程式碼檔案、樣式和資源打包成瀏覽器可讀取的格式,優化程式碼大小。Vite 因其快速的熱模組替換(HMR)而非常流行。 |
| WXT / Plasmo | 擴充功能專用框架 | 專門為擴充功能開發設計的框架,可自動處理 Manifest 檔案生成、TypeScript 配置、熱重載和跨瀏覽器支援(Chrome、Firefox、Edge 等),大幅簡化開發體驗。 |
| pnpm / npm / yarn | 套件管理工具 | 管理專案依賴的第三方 JavaScript 函式庫和工具。 |
這是所有 Chrome 擴充功能開發中不可或缺的工具。
php.ini 是 PHP 的主要設定檔,影響 PHP 的執行方式與功能。
透過修改此檔案,可以調整錯誤輸出、記憶體限制、上傳大小、時區等。
C:\php\php.ini 或安裝目錄下。/etc/php/版本號/apache2/php.ini 或 /etc/php/版本號/cli/php.ini。php --ini 查詢 CLI 使用的設定檔。-c 參數指定,會優先載入該路徑的 php.ini。PHPRC 所指定的目錄。php --ini 或 phpinfo() 查詢)。/etc/php.ini 或 C:\Windows。; 設定時區
date.timezone = Asia/Taipei
; 顯示錯誤
display_errors = On
error_reporting = E_ALL
; 上傳檔案大小
upload_max_filesize = 20M
post_max_size = 25M
; 記憶體限制
memory_limit = 256M
; 最大執行時間 (秒)
max_execution_time = 30
php.ini 後,需重新啟動 Apache、Nginx 或 PHP-FPM 才會生效。phpinfo() 檢查目前載入的 php.ini 路徑與參數值。php.ini。.user.ini 或 .htaccess 覆蓋部分設定。布林值是 PHP 中最簡單的資料類型,它只能表示兩種狀態:**真 (true)** 或 **假 (false)**。布林值經常用於控制流程(例如 if/else 條件判斷)和邏輯運算。
您可以使用關鍵字 true 或 false 來定義一個布林變數。這些關鍵字不區分大小寫(例如,TRUE、True 或 true 都是一樣的),但通常建議使用小寫 true 和 false 以符合標準慣例。
$is_active = true;
$is_logged_out = FALSE; // 雖然大寫也可以,但不建議
// 條件判斷
if ($is_active) {
echo "<p>使用者目前是活躍狀態。</p>";
} else {
echo "<p>使用者目前不活躍。</p>";
}
當 PHP 需要一個布林值,但給定的卻是其他資料類型時(例如在 if 條件中),PHP 會自動將該值轉換(或稱為類型強制轉換)為布林值。
以下值會被視為 **假 (false)**(稱為 Falsy 值):
false 本身。0(零)。0.0(零)。"" 或 ''。"0"(包含單一數字零的字串)。array() 或 []。NULL。所有其他值,包括任何非零數、任何非空字串(即使是 "false" 或 " " [含空格]),都會被視為 **真 (true)**(稱為 Truthy 值)。
$a = 0;
$b = "Hello";
$c = "";
$d = [1, 2];
$e = null;
echo "<h3>Falsy 值範例</h3>";
if (!$a) { // $a (0) 轉換為 false
echo "<p>$a (整數 0) 被視為 FALSE。</p>";
}
if (!$c) { // $c (空字串) 轉換為 false
echo "<p>$c (空字串) 被視為 FALSE。</p>";
}
if (!$e) { // $e (NULL) 轉換為 false
echo "<p>NULL 被視為 FALSE。</p>";
}
echo "<h3>Truthy 值範例</h3>";
if ($b) { // $b ("Hello") 轉換為 true
echo "<p>$b (非空字串) 被視為 TRUE。</p>";
}
if ($d) { // $d (非空陣列) 轉換為 true
echo "<p>非空陣列被視為 TRUE。</p>";
}
您也可以使用 (bool) 或 boolval() 函式明確地將變數轉換為布林值,這在除錯或需要嚴格檢查類型時非常有用。
$f = "0"; // 數字零的字串 $g = 100; // 正整數 $f_bool = (bool)$f; $g_bool = boolval($g); echo "<p>字串 '0' 轉換為: "; var_dump($f_bool); // 輸出: bool(false) echo "</p>"; echo "<p>整數 100 轉換為: "; var_dump($g_bool); // 輸出: bool(true) echo "</p>";
在 PHP 中,陣列是一個非常有用的複合資料類型,它可以儲存多個值在一個單一變數中。PHP 的陣列實際上是一個有序的地圖 (Ordered Map)。地圖是一種將**值 (Values)** 與**鍵 (Keys)** 關聯起來的類型。
PHP 的陣列可以根據其鍵的類型,大致分為三種常見形式:
使用連續的整數作為鍵。如果您在建立陣列時未指定鍵,PHP 會從 0 開始自動賦予整數索引。
// 建立索引式陣列
$fruits = array("蘋果", "香蕉", "橘子");
// 或使用簡寫語法 (PHP 5.4 之後)
$colors = ["紅", "綠", "藍"];
// 存取元素
echo $fruits[0]; // 輸出: 蘋果
echo $colors[2]; // 輸出: 藍
使用字串作為鍵(鍵名)。這允許您使用有意義的名稱來存取陣列中的值。
// 建立關聯式陣列
$person = array(
"name" => "小明",
"age" => 25,
"city" => "台北"
);
// 或使用簡寫語法
$scores = [
"Math" => 90,
"English" => 85
];
// 存取元素
echo $person["name"]; // 輸出: 小明
echo $scores["English"]; // 輸出: 85
陣列中的值又是另一個陣列。這通常用於儲存表格狀或層次性的資料。
$students = [
[
"name" => "Alice",
"scores" => ["Math" => 95, "Science" => 88]
],
[
"name" => "Bob",
"scores" => ["Math" => 78, "Science" => 92]
]
];
// 存取元素 (存取 Bob 的 Science 成績)
echo $students[1]["scores"]["Science"]; // 輸出: 92
PHP 提供了數百個內建函式來處理陣列,以下是一些最常用的:
count($array):計算陣列中元素的數量。print_r($array):以人類可讀的格式顯示陣列的結構和值 (通常用於除錯)。array_push($array, $value1, ...):將一個或多個元素推入(添加到)陣列的末尾。array_pop($array):彈出(移除並回傳)陣列中的最後一個元素。array_keys($array):回傳陣列中所有鍵名組成的新陣列。array_values($array):回傳陣列中所有值組成的新陣列。in_array($needle, $haystack):檢查陣列中是否存在特定的值。array_key_exists($key, $array):檢查陣列中是否存在特定的鍵。通常使用 foreach 迴圈來遍歷陣列中的所有元素:
$staff = ["經理" => "張三", "工程師" => "李四", "設計師" => "王五"];
// 遍歷關聯式陣列 (取得鍵和值)
foreach ($staff as $title => $name) {
echo $title . ": " . $name . "<br>";
}
// 遍歷索引式陣列 (只取得值)
$items = ["A", "B", "C"];
foreach ($items as $item) {
echo $item . "<br>";
}
| 優先順序 | 說明 |
|---|---|
| 1. | 指定的絕對路徑或相對路徑:直接指定檔案的路徑,PHP 會首先尋找此路徑下的檔案。 |
| 2 | 執行檔案的當前目錄:如果沒有指定完整路徑,PHP 會先在執行檔案所在的目錄尋找檔案。 |
| 3 | include_path 設定:若在執行目錄下找不到檔案,PHP 將檢查 php.ini 中設定的 include_path 路徑。 |
| 4 | 檔案不存在:若以上所有路徑均無法找到檔案,則會產生一個警告(Warning),並返回 false。 |
strcasecmp() 進行不分大小寫的字串比較。
若回傳值為 0,表示兩個字串相等。
<?php
$str1 = "Hello";
$str2 = "hello";
if (strcasecmp($str1, $str2) === 0) {
echo "字串相等 (忽略大小寫)";
} else {
echo "字串不相等";
}
?>
str_ireplace()。
<?php
$text = "Hello World";
$result = str_ireplace("hello", "Hi", $text);
echo $result; // 輸出 "Hi World"
?>
i 來忽略大小寫。
<?php
if (preg_match("/hello/i", "HeLLo PHP")) {
echo "比對成功";
}
?>
可以透過 $_SERVER['PHP_SELF'] 獲取當前執行的檔案路徑,然後使用 basename 取得檔案名稱:
<?php
$current_page = basename($_SERVER['PHP_SELF']);
echo "當前頁面名稱:" . $current_page;
?>
透過 $_SERVER['REQUEST_URI'] 獲取完整的URL路徑,然後使用 parse_url 和 basename 解析檔案名稱:
<?php
$url_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$current_page = basename($url_path);
echo "當前頁面名稱:" . $current_page;
?>
如果URL包含查詢參數,可以分開處理:
<?php
$url_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$query_string = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
$current_page = basename($url_path);
echo "檔案名稱:" . $current_page . "<br>";
echo "查詢參數:" . $query_string;
?>
假設當前URL是 https://example.com/page.php?id=123:
當前頁面名稱:page.php
檔案名稱:page.php
查詢參數:id=123
debug_backtrace() 是 PHP 用來取得目前執行堆疊(呼叫路徑)的函式,常用於除錯、記錄呼叫來源。
$trace = debug_backtrace();
print_r($trace);
debug_backtrace() 會回傳一個陣列,依照呼叫順序排列:
file:檔案路徑line:程式行號function:呼叫的函式名稱class:所屬類別名稱(若有)args:傳入參數
function showCaller() {
$trace = debug_backtrace();
echo '來自:' . $trace[1]['file'] . ' 第 ' . $trace[1]['line'] . ' 行';
}
include 'subpageA.php'; // subpageA.php 內呼叫 showCaller()
main.php include subA.phpsubA.php include subB.phpsubB.php 使用:
$trace = debug_backtrace();
echo '目前檔案:' . $trace[0]['file'] . "\n";
echo '上一層:' . $trace[1]['file'] . "\n"; // subA.php
echo '主頁:' . $trace[2]['file'] . "\n"; // main.php
debug_backtrace() 是查看包含/函式呼叫來源最實用的方法error_log() 是 PHP 用來記錄錯誤或自訂訊息的函式,
常用於除錯、追蹤程式流程、記錄例外狀況,而不影響畫面輸出。
<?php
error_log("這是一則測試訊息");
?>
<?php
$data = ["a" => 1, "b" => 2];
error_log(print_r($data, true));
?>
<?php
error_log("寫入自訂檔案", 3, "D:/logs/php_debug.log");
?>
<?php
error_log("[INFO] 程式開始執行");
error_log("[WARN] 參數為空");
error_log("[ERROR] 資料庫連線失敗");
?>
php.ini 控制:
log_errors = On
error_log = "D:/logs/php_errors.log"
若未設定 error_log,預設會:
<?php
try {
throw new Exception("發生例外");
} catch (Exception $e) {
error_log($e->getMessage());
}
?>
display_errors,只使用 error_log當你在 PHP 中使用 error_log() 記錄包含中文的字串時,日誌檔案常會出現如 \xe6\xb2\x92 格式的十六進制編碼。這通常不是程式錯誤,而是伺服器(Apache/Nginx)為了安全或相容性,將非 ASCII 字元強制轉義的結果。
這是最簡單且有效的做法。透過直接操作檔案,可以繞過伺服器對 error_log() 的過濾機制。
function write_utf8_log($message) { $log_file = DIR . '/debug.log'; $timestamp = date('Y-m-d H:i:s'); // 使用 FILE_APPEND 模式寫入原始位元組 file_put_contents($log_file, "[$timestamp] $message" . PHP_EOL, FILE_APPEND | LOCK_EX); }
// 調用範例 $title = $_GET["title"]; write_utf8_log("標題內容: " . $title);
如果你希望保留 error_log() 的路徑設定,可以使用 json_encode 並配合 JSON_UNESCAPED_UNICODE 參數,這會強制 PHP 以 UTF-8 格式輸出,但在某些伺服器環境下仍可能被二次轉義。
$title = $_GET["title"]; error_log('title: ' . json_encode($title, JSON_UNESCAPED_UNICODE));
如果使用的是 Apache 2.4+,可以嘗試修改 ErrorLogFormat 來調整日誌輸出的轉義行為。
1. 在 Apache 設定檔(如 httpd.conf)中定義格式:
ErrorLogFormat "[%t] [%l] [pid %P] %F: %E: [client %a] %M"
2. 確保作業系統語系支援 UTF-8。在 Ubuntu 中修改 /etc/apache2/envvars:
export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8
如果日誌已經產生且你無法更改伺服器設定,可以使用以下方式查看原始中文:
| 方法 | 優點 | 缺點 |
|---|---|---|
| 自定義日誌 (file_put_contents) | 100% 顯示中文,不需權限 | 需手動管理檔案大小與權限 |
| JSON_UNESCAPED_UNICODE | 程式碼改動最小 | 仍受伺服器全局配置影響 |
| 修改 Apache 配置 | 從根源解決所有系統日誌問題 | 需要管理員權限,重啟服務 |
parse_url() 函式搭配 PHP_URL_QUERY 常數,從完整的網址中擷取查詢字串部分。例如:
$url = "https://example.com/page.php?name=John&age=30&lang=php";
$query = parse_url($url, PHP_URL_QUERY); // 結果為 "name=John&age=30&lang=php"
parse_str() 函式可以將查詢字串解析為關聯式陣列,其中每一組名稱與數值都會自動解碼並轉換為陣列元素:
parse_str($query, $params);
此時 $params 的內容為:
[
"name" => "John",
"age" => "30",
"lang" => "php"
]
<?php
$url = "https://example.com/page.php?name=John&age=30&lang=php";
$query = parse_url($url, PHP_URL_QUERY);
parse_str($query, $params);
foreach ($params as $key => $value) {
echo "$key = $value\n";
}
?>
name = John
age = 30
lang = php
parse_url() 只會擷取查詢部分,不會解析內容。parse_str() 可處理 URL 編碼,例如 %20 會轉為空格。tags[]=php&tags[]=html 將轉為陣列。使用 JavaScript 獲取 <h1> 的內容,然後將其發送到 PHP:
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>獲取 H1 內容</title>
<script>
function sendH1Content() {
var h1Content = document.querySelector('h1').innerText;
var xhr = new XMLHttpRequest();
xhr.open('POST', 'process.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('h1Content=' + encodeURIComponent(h1Content));
}
window.onload = sendH1Content;
</script>
</head>
<body>
<h1>這是頁面的標題</h1>
</body>
</html>
在 process.php 中接收並處理 H1 內容:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$h1Content = $_POST['h1Content'] ?? '';
// 處理 $h1Content,例如儲存到資料庫或進行其他操作
echo "收到的 H1 內容: " . htmlspecialchars($h1Content);
}
?>
如果 HTML 文件是靜態的,可以使用 PHP 來讀取該文件並解析它:
<?php
$htmlContent = file_get_contents('path/to/your/file.html');
preg_match('/<h1>(.*?)<\/h1>/', $htmlContent, $matches);
$h1Content = $matches[1] ?? '';
// 處理 $h1Content
echo "H1 內容: " . htmlspecialchars($h1Content);
?>
最常見的方式是使用 JavaScript 在客戶端獲取內容,然後透過 AJAX 將其發送給 PHP 進行處理。伺服器端解析通常用於靜態內容,不建議用於動態生成的頁面。
abc.php 已印出多個 <h2>,再 include subpage.php 時,無法從輸出的 HTML 抓內容。
正確作法是──**在 include 前由 abc.php 把最後一個 <h2> 的內容傳入 subpage.php**(變數),由 subpage.php 使用。
<!-- abc.php -->
<h2>AAA</h2>
<h2>BBB</h2>
<h2>lasth2</h2>
<?php
$last_h2 = "lasth2";
include "subpage.php";
?>
<!-- subpage.php -->
<?php
echo "主頁最後的 H2 是: " . $last_h2;
?>
<!-- abc.php -->
<?php
ob_start(); // 開始緩衝
?>
<h2>AAA</h2>
<h2>BBB</h2>
<h2>lasth2</h2>
<?php
$html = ob_get_clean(); // 取得全部輸出並清空緩衝
preg_match_all('/<h2>(.*?)<\\/h2>/i', $html, $matches);
$last_h2 = end($matches[1]); // 最後一個 h2 的內容
include "subpage.php";
// 最後把原本的 HTML 印出
echo $html;
?>
<!-- abc.php -->
<?php
$h2_list = ["AAA", "BBB", "lasth2"];
?>
<h2>AAA</h2>
<h2>BBB</h2>
<h2>lasth2</h2>
<?php
$last_h2 = end($h2_list);
include "subpage.php";
?>
ob_start() + 正規表示式解析urlencode() 函式用於對字串進行 URL 編碼 (Percent-encoding)。在將資料作為 URL 參數傳遞時,這是必須執行的步驟,以確保所有特殊字元都能被正確傳輸和解析。
string urlencode(string $string): string此函式會將給定的字串轉換為 URL 安全的格式。它替換了所有非字母或數字的字元,除了減號 (-)、底線 (_)、句點 (.) 和波浪號 (~) 之外。
被替換的特殊字元會被替換為百分比符號 (%) 後跟隨該字元 ASCII 值的兩位十六進制表示法(例如,空格 $\rightarrow$ %20)。
?、&、=、/)具有特殊含義。如果參數中包含這些字元,瀏覽器或伺服器可能會誤解 URL 的結構。urlencode() 會以 UTF-8 格式對這些字元進行編碼。urlencode() 會將空格轉換為加號 (+) 或 %20 (雖然 urlencode() 產生的是 +,但在 URL 參數中,這兩種表示都表示空格)。$data = "PHP 陣列與 $100 & 50% 優惠"; $encoded_data = urlencode($data); echo "<h3>特殊字元與中文範例</h3>"; echo "<p>原始字串: " . htmlspecialchars($data) . "</p>"; echo "<p>編碼結果: " . htmlspecialchars($encoded_data) . "</p>"; // 輸出結果類似: PHP+陣列與+%24100+%26+50%25+優惠 // (中文 '陣列' 會被編碼為 %E9%99%A3%E5%88%97 等)
假設您需要將查詢字串 q_str 安全地附加到 API 網址。
$api_base = "http://api.example.com/search"; $search_query = "手機攝影技巧 (2025 年)"; // 確保查詢字串安全 $encoded_query = urlencode($search_query); // 建構最終 URL $final_url = $api_base . "?q_str=" . $encoded_query; echo "<h3>建構 URL 範例</h3>"; echo "<p>查詢字串: " . htmlspecialchars($search_query) . "</p>"; echo "<p>最終 URL: " . htmlspecialchars($final_url) . "</p>"; // 最終 URL 類似: http://api.example.com/search?q_str=%E6%89%8B%E6%A9%9F%E6%94%9D%E5%BD%B1%E6%8A%80%E5%B7%A7+%282025+%E5%B9%B4%29
PHP 還有另一個類似的函式 rawurlencode(),它們之間的主要區別在於對**空格**的處理:
urlencode():將空格轉換為加號 (+)。這通常用於編碼 **GET 參數的值** (例如 ?key=value 中的 value 部分)。
rawurlencode():將空格轉換為 %20。這符合 RFC 3986 中更嚴格的定義,通常用於編碼 URL 的**路徑部分** (Path Segments),或當您需要精確的 %20 而非 + 時。
$space_test = "This is a test"; echo "<h3>urlencode() vs rawurlencode()</h3>"; echo "<p>urlencode(): " . urlencode($space_test) . "</p>"; // 輸出: This+is+a+test echo "<p>rawurlencode(): " . rawurlencode($space_test) . "</p>"; // 輸出: This%20is%20a%20test
ob_start():啟用輸出緩衝。ob_get_contents():取得目前緩衝的內容。ob_end_clean():結束緩衝,並清除不輸出。ob_end_flush():結束緩衝並輸出內容。ob_get_clean():取得內容並結束緩衝。<?php
ob_start(); // 啟動輸出緩衝
echo "Hello World!";
$content = ob_get_clean(); // 取得並清除緩衝內容
echo strtoupper($content); // 輸出修改後內容:HELLO WORLD!
?>
<?php
ob_start();
register_shutdown_function(function () {
$html = ob_get_clean();
echo "<div class='container'>" . $html . "</div>";
});
?>
<h1>內容標題</h1>
<p>這是內容段落。</p>
<h2> 標題及其下方區塊,並在右側加入對應的圖檔。圖檔依序命名為 h2-1.jpg、h2-2.jpg... 以此類推,需預先放在指定圖檔資料夾中。
h2-image-right.php):
<?php
ob_start();
function add_images_next_to_h2($html) {
$pattern = '/(<h2.*?>.*?<\/h2>)(.*?)(?=<h2|\z)/is';
$index = 1;
return preg_replace_callback($pattern, function($matches) use (&$index) {
$h2 = $matches[1];
$body = $matches[2];
$imgPath = "/images/h2-$index.jpg";
$imgFile = $_SERVER['DOCUMENT_ROOT'] . $imgPath;
if (file_exists($imgFile)) {
$output = "<div style='display: flex; align-items: flex-start; gap: 20px; margin-bottom: 1.5em;'>";
$output .= "<div style='flex: 1;'>" . $h2 . $body . "</div>";
$output .= "<img src='$imgPath' style='max-width: 200px; height: auto; border: 1px solid #ccc;'>";
$output .= "</div>";
} else {
$output = $h2 . $body;
}
$index++;
return $output;
}, $html);
}
register_shutdown_function(function() {
$html = ob_get_clean();
echo add_images_next_to_h2($html);
});
?>
/images/ 資料夾中,例如:
/images/h2-1.jpg/images/h2-2.jpg/images/h2-3.jpg<?php include "h2-image-right.php"; ?>
<h2>第一段標題</h2>
<p>這是第一段的內容。</p>
<h2>第二段標題</h2>
<p>這是第二段的內容。</p>
<h2> 出現的順序對應,請依此命名。<h2> ~ 下方內容 為一個 flex 區塊,適合純內容頁。ob_start()。檢查伺服器日誌是辨識搜尋引擎機器人的一個重要方法。以下是一些搜尋引擎機器人的User-Agent範例:
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)
Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)檢查網站的 robots.txt 文件,可以了解哪些搜尋引擎機器人被允許或禁止訪問網站的某些部分。例如:
User-agent: *
Disallow: /private/
搜尋引擎機器人的IP地址通常來自特定範圍。你可以比對訪問者的IP地址是否屬於這些範圍。例如:
可使用工具來驗證訪問者的IP是否真的來自搜尋引擎。
以下是如何在伺服器端使用PHP進行User-Agent檢查的範例:
<?php
$userAgent = $_SERVER['HTTP_USER_AGENT'];
if (strpos($userAgent, 'Googlebot') !== false) {
echo "這是Googlebot的流量";
} else {
echo "這不是搜尋引擎機器人的流量";
}
?>
使用Google Analytics或其他網站分析工具,可以幫助追蹤流量來源。這些工具通常會自動過濾大部分的機器人流量。
URL 的 Hash 值是指位於 URL 中「#」符號後面的部分,又稱為片段識別符(Fragment Identifier)。它通常用於標識頁面內的某個位置或狀態。
以下是一些常見的 URL Hash 範例:
https://example.com/page#section1https://example.com/app#/dashboardhttps://example.com/search#filter=activeid。
<script>
// 獲取 URL 的 Hash 值
const hashValue = window.location.hash.substring(1);
// 顯示 Hash 值
console.log("Hash 值是:", hashValue);
</script>
<?php
// 模擬 URL
$url = "http://example.com/page#section2";
// 使用 parse_url 解析 Hash 值
$parsedUrl = parse_url($url);
$hash = isset($parsedUrl['fragment']) ? $parsedUrl['fragment'] : null;
// 輸出 Hash 值
if ($hash) {
echo "Hash 值是: " . htmlspecialchars($hash, ENT_QUOTES, 'UTF-8');
} else {
echo "URL 中沒有 Hash 部分。";
}
?>
window.location.hash。name 屬性雖然較少使用,但可以將 name 屬性加入一個 <a> 標籤,並將其放在目標標籤前。
<a name="specific-heading1"></a> <h3>目標標題1</h3> <a href="#specific-heading1">連結到特定標題</a>
效果:
data-attribute 配合 JavaScript透過設定自訂屬性,例如 data-anchor,並使用 JavaScript 處理跳轉行為。
<h3 data-anchor="heading-1">目標標題3</h3>
<a href="#" onclick="jumpToHeading('heading-1')">連結到特定標題</a>
<script>
function jumpToHeading(anchor) {
const target = document.querySelector(`[data-anchor="${anchor}"]`);
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
}
</script>
效果:
h2 的文字內容配合 JavaScript透過查找 h2 的文字內容來定位,使用 JavaScript 動態定位到目標。
<h3>目標標題5</h3>
<a href="#" onclick="jumpToText('目標標題')">連結到特定標題</a>
<script>
function jumpToText(text) {
const target = Array.from(document.querySelectorAll('h2')).find(el => el.textContent === text);
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
}
</script>
效果:
cURL 是 PHP 中用來發送 HTTP 請求的函式庫,支援 GET、POST、PUT、DELETE 等方法,常用於 API 資料存取。
curl_init()curl_setopt()curl_exec()curl_close()<?php
$url = "https://api.example.com/data";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
<?php
$url = "https://api.example.com/post";
$data = ['name' => '測試', 'email' => '[email protected]'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
<?php
$url = "https://api.example.com/json";
$data = json_encode(['id' => 123]);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($data)
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
CURLOPT_URL:設定目標網址CURLOPT_RETURNTRANSFER:是否回傳結果(不直接輸出)CURLOPT_POSTFIELDS:送出的資料CURLOPT_HTTPHEADER:自訂 HTTP 標頭<?php
if(curl_errno($ch)) {
echo '錯誤:' . curl_error($ch);
}
?>
在 PHP 中,檢查一個特定的網路埠 (Port) 是否正在監聽,通常是為了確認本機或遠端的服務是否正在運行並接受連線。最常用的方法是使用 Socket 相關的函式來嘗試建立一個 TCP 連線。
fsockopen() 函式用於開啟一個網路連線或 Unix 域 Socket 連線。如果連線成功,則表示該埠正在監聽;如果連線失敗,通常表示該埠未開放或被防火牆阻擋。
/**
* 檢查指定的 IP/主機名稱上的埠是否正在監聽
*
* @param string $host 要檢查的主機名稱或 IP 地址
* @param int $port 要檢查的埠號
* @param int $timeout 連線逾時秒數
* @return bool 如果連線成功,返回 true;否則返回 false
*/
function is_port_listening(string $host, int $port, int $timeout = 2): bool {
// 建立 Socket 連線。@ 符號用於抑制連線錯誤訊息。
$connection = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (is_resource($connection)) {
// 如果 $connection 是資源類型,表示連線成功
fclose($connection); // 成功後立即關閉連線
return true;
} else {
// 連線失敗,埠可能未開放或服務未運行
// 可選:在這裡可以列印 $errstr 和 $errno 進行除錯
// echo "連線失敗: $errstr ($errno)<br>";
return false;
}
}
// --- 測試範例 ---
$remote_host = 'www.google.com';
$remote_port = 80; // 檢查 Google 的 HTTP 埠
$local_host = '127.0.0.1';
$local_web_port = 80; // 檢查本機 Web 服務器的埠
$unoccupied_port = 65432; // 假設這個埠未被使用
echo "<h3>埠監聽檢查結果</h3>";
// 檢查遠端服務
if (is_port_listening($remote_host, $remote_port)) {
echo "<p>{$remote_host}:{$remote_port} 正在監聽 (連線成功)。</p>";
} else {
echo "<p>{$remote_host}:{$remote_port} 未開放或連線失敗。</p>";
}
// 檢查本機 Web 服務器 (如果您的 Web 伺服器運行在 80 埠)
if (is_port_listening($local_host, $local_web_port)) {
echo "<p>{$local_host}:{$local_web_port} 正在監聽 (Web 伺服器運行)。</p>";
} else {
echo "<p>{$local_host}:{$local_web_port} 未監聽。</p>";
}
// 檢查一個未被佔用的埠
if (is_port_listening($local_host, $unoccupied_port)) {
echo "<p>{$local_host}:{$unoccupied_port} 正在監聽。</p>";
} else {
echo "<p>{$local_host}:{$unoccupied_port} 未監聽 (預期結果)。</p>";
}
如果需要更底層的 Socket 操作(例如設置選項、非阻塞模式等),可以使用 PHP 的 Socket 函式庫(需要啟用 php_sockets 擴展)。
function is_port_listening_socket(string $host, int $port): bool {
// 建立一個 TCP/IP Socket
$socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
// echo "socket_create 失敗: " . socket_strerror(socket_last_error()) . "<br>";
return false;
}
// 嘗試連線
// socket_connect() 會嘗試在指定的 host 和 port 上建立連線
$result = @socket_connect($socket, $host, $port);
socket_close($socket);
return ($result !== false);
}
$timeout 值(例如 2-5 秒)。如果沒有設定,當目標主機無法到達時,您的腳本可能會長時間阻塞。
fsockopen() 仍然會失敗。
fsockopen() 函式通常不受 PHP 設定檔中 allow_url_fopen 指令的限制,因為它直接操作網路 Socket。
$host 設為 127.0.0.1 或 localhost。
在 PHP 中,要取得 Web API 的結果並解析 JSON,通常會使用 file_get_contents() 或 cURL 函式庫來發送 HTTP 請求,然後使用 json_decode() 函式來解析回傳的 JSON 字串。
對於簡單的 GET 請求,file_get_contents() 是一個快速的方法。但如果 API 需要特定的 HTTP headers 或更複雜的請求,則 cURL 更為適用。
$api_url = 'https://jsonplaceholder.typicode.com/posts/1'; // 範例 API 網址
// 取得 API 回傳的內容 (JSON 格式字串)
$json_data = @file_get_contents($api_url);
if ($json_data === FALSE) {
echo "無法取得 API 資料或 API 請求失敗。";
} else {
// 將 JSON 字串解析成 PHP 陣列或物件
// 第二個參數設為 true 會解析成關聯陣列 (associative array),
// 設為 false (預設) 則解析成物件 (object)。
$data = json_decode($json_data, true);
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
echo "JSON 解析錯誤:" . json_last_error_msg();
} else {
echo "<h3>API 回傳資料 (關聯陣列)</h3>";
// 範例:存取資料中的特定欄位
echo "<p>標題 (title): " . htmlspecialchars($data['title']) . "</p>";
echo "<p>內容 (body): " . htmlspecialchars($data['body']) . "</p>";
// 輸出完整結構以便檢視
echo "<h4>完整資料結構 (print_r)</h4>";
echo "<pre>";
print_r($data);
echo "</pre>";
}
}
cURL 提供了更多的控制權,例如設定請求方法 (POST, PUT, DELETE)、headers、逾時等。
$api_url = 'https://jsonplaceholder.typicode.com/posts/2';
// 初始化 cURL session
$ch = curl_init();
// 設定 cURL 選項
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 將結果以字串形式回傳,而不是直接輸出
// 執行 cURL session 並取得結果
$json_data = curl_exec($ch);
// 檢查是否有錯誤發生
if (curl_errno($ch)) {
echo "cURL 錯誤:" . curl_error($ch);
$data = null;
} else {
// 解析 JSON
$data = json_decode($json_data, true);
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
echo "JSON 解析錯誤:" . json_last_error_msg();
}
}
// 關閉 cURL session
curl_close($ch);
if ($data !== null) {
echo "<h3>API 回傳資料 (cURL 範例)</h3>";
echo "<p>標題 (title): " . htmlspecialchars($data['title']) . "</p>";
echo "<p>使用者 ID (userId): " . htmlspecialchars($data['userId']) . "</p>";
}
json_decode($json_string, $associative)true,則回傳關聯陣列;如果為 false (預設),則回傳物件。PHP 的 exec() 函式與 shell_exec()、system() 類似,本質上是**同步阻塞**的,即 PHP 腳本會等待外部命令執行完畢才會繼續。若要在不阻塞 PHP 主程式的情況下執行外部命令(實現「非同步」或「背景執行」),我們必須依賴 Shell 本身的特性來將命令分離出來。
實現非阻塞執行的關鍵是在您傳遞給 exec() 的命令字串中,加入特定的 Shell 語法:
&:** 這是將命令推到背景執行的核心符號。> /dev/null 2>&1:** 這是防止 PHP 阻塞的關鍵。它將標準輸出 (STDOUT) 和標準錯誤輸出 (STDERR) 都重定向到空裝置 /dev/null。如果沒有重定向,即使使用了 & 符號,某些版本的 PHP 仍可能等待 I/O 串流關閉,導致假性阻塞。nohup (可選但推薦):** 在 Linux/Unix 系統上,使用 nohup 可以確保進程在 Web 伺服器終止(例如 HTTP 請求結束或使用者關閉瀏覽器)後仍能繼續運行。此範例適用於不需要在 HTTP 請求結束後仍需運行的短暫背景任務。
$command_to_run = 'php /path/to/my_long_task.php arg1 arg2'; // 組合非阻塞命令 // 1. 命令本身 // 2. 將 STDOUT 導向 /dev/null // 3. 將 STDERR (2) 導向 STDOUT (&1) // 4. 使用 & 符號推入背景 $async_command = $command_to_run . ' > /dev/null 2>&1 &'; $output = []; $return_var = 0; // 執行非阻塞命令 exec($async_command, $output, $return_var); echo "<h3>非同步執行結果 (exec)</h3>"; echo "<p>命令已送出至背景執行。</p>"; echo "<p>PHP 腳本會立即繼續執行,不會等待外部腳本完成。</p>"; echo "<p>exec() 回傳的輸出陣列 (\$output) 幾乎會是空的。</p>";
此範例使用 nohup 來確保背景任務與 PHP Web 服務器的父進程完全分離,即使 Web 伺服器關閉,任務也會繼續運行。
$command_to_run = '/usr/bin/python3 /path/to/process_data.py'; $log_file = '/var/log/my_app/task_log.log'; // 專門的日誌檔案 // 確保命令參數經過適當的轉義,以防止 Shell 注入攻擊 $safe_command = escapeshellcmd($command_to_run); // 將輸出和錯誤輸出導向日誌檔案,而不是 /dev/null $detached_command = 'nohup ' . $safe_command . ' >> ' . escapeshellarg($log_file) . ' 2>&1 &'; exec($detached_command); echo "<h3>使用 nohup 分離進程</h3>"; echo "<p>命令已使用 nohup 啟動,並將輸出導向到日誌檔案。</p>"; echo "<p>任務將在背景中持續運行,直到完成。</p>";
當您執行外部命令時,安全性是最重要的考量。**永遠不要**將未經淨化或轉義的使用者輸入傳遞給 exec() 或其他 Shell 執行函式。PHP 提供了兩個函式來協助處理:
escapeshellcmd($command):轉義**整個命令字串**,防止惡意使用者插入多個命令。escapeshellarg($arg):轉義**單一參數**,確保參數被視為單一字串。
$user_input = "test; rm -rf /"; // 潛在的惡意輸入
// 錯誤:不安全的做法
// exec('my_script.sh ' . $user_input);
// 正確:安全的做法
$safe_input = escapeshellarg($user_input);
$safe_command = 'my_script.sh ' . $safe_input;
// 執行結果: my_script.sh 'test; rm -rf /' (被視為單一參數)
exec()shell_exec()passthru()proc_open()(進階)<?php
$cmd = "python3 myscript.py 123";
$output = shell_exec($cmd);
echo $output;
?>
<?php
$arg1 = 100;
$arg2 = "hello";
$cmd = "python3 script.py " . escapeshellarg($arg1) . " " . escapeshellarg($arg2);
$result = shell_exec($cmd);
echo $result;
?>
# script.py
import sys
a = sys.argv[1]
b = sys.argv[2]
print(f"輸入參數: {a}, {b}")
<?php
exec("python3 test.py", $output_lines, $status);
echo "<pre>";
print_r($output_lines);
echo "</pre>";
?>
<?php
$descriptor = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
];
$process = proc_open("python3 calc.py", $descriptor, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "50\n");
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$error = stream_get_contents($pipes[2]);
fclose($pipes[2]);
proc_close($process);
echo "輸出: $output<br>";
echo "錯誤: $error<br>";
}
?>
/usr/bin/python3 或 C:\\Python39\\python.exeescapeshellarg() 防止命令注入php -m 確認有載入 php-gtk 模組。<?php
if (!class_exists('gtk')) {
die("PHP-GTK 尚未安裝\n");
}
$window = new GtkWindow();
$window->set_title("PHP-GTK 範例");
$window->set_size_request(300, 200);
$window->connect_simple('destroy', array('Gtk', 'main_quit'));
$button = new GtkButton("點我");
$button->connect_simple('clicked', function() {
echo "按鈕被點擊!\n";
});
$window->add($button);
$window->show_all();
Gtk::main();
?>
Electron (基於 Chromium 與 Node.js) 建立桌面 GUI,後端仍可呼叫 PHP 程式。
這樣可以用 HTML/CSS/JavaScript 建立漂亮的跨平台應用程式,並透過 API 與 PHP 溝通。php -S localhost:8000),
再用瀏覽器打開頁面,效果上就像一個桌面應用程式,搭配 PWA (Progressive Web App) 還能安裝成桌面圖示。單頁應用程式(Single Page Application,簡稱 SPA)是一種網頁應用程式或網站的模型,它透過動態重寫當前頁面與使用者互動,而非從伺服器載入整個新頁面。這種方法避免了頁面切換間的打斷,讓使用者體驗更接近桌面應用程式。
在傳統模型中,每次點擊連結都會向伺服器請求一個全新的 HTML 檔案;而在 SPA 中,只有在第一次載入時會下載必要的 HTML、CSS 和 JavaScript,後續的資料交換則透過 AJAX 或 Fetch API 完成。
| 特性 | SPA (單頁應用) | MPA (多頁面應用) |
|---|---|---|
| 頁面載入 | 僅載入一次,局部更新 | 每次操作皆重新載入整頁 |
| 使用者體驗 | 流暢、無跳轉感 | 有明顯的白屏與等待感 |
| 伺服器負載 | 較低(僅傳輸 JSON 資料) | 較高(每次都要渲染 HTML) |
| SEO 優化 | 較困難,需額外處理 | 容易,天生對搜尋引擎友好 |
這是一個不依賴框架,僅使用原生 JavaScript 模擬 SPA 切換內容的邏輯:
<!-- index.html -->
<nav>
<a href="#home" onclick="route()">首頁</a>
<a href="#trading" onclick="route()">交易圖表</a>
</nav>
<div id="content">
<!-- 動態內容將注入此處 -->
</div>
<script>
const routes = {
"#home": "<h2>歡迎回來</h2><p>這是您的儀表板。</p>",
"#trading": "<h2>即時行情</h2><div id='tv-container'>圖表載入中...</div>"
};
function route() {
const hash = window.location.hash || "#home";
const contentDiv = document.querySelector("#content");
contentDiv.innerHTML = routes[hash];
// 如果切換到交易頁面,可以在此處初始化之前的 TradingView Widget
if (hash === "#trading") {
// initTradingView();
}
}
// 監聽網址變化
window.addEventListener("hashchange", route);
// 初始載入
route();
</script>
優點: 速度快、減少頻寬消耗、前後端分離開發(後端只需提供 API)。
缺點: 初始載入時間較長(First Load)、SEO 需要透過 SSR(伺服器端渲染)或預渲染技術來補足。
在開發單頁應用程式(SPA)時,選擇合適的框架解決方案能顯著提升開發效率與程式碼的可維護性。目前的市場由三大主流框架主導,並有許多新興的後起之秀。
由 Meta (Facebook) 開發並維護,是目前全球使用率最高的 UI 函式庫。它引入了虛擬 DOM (Virtual DOM) 與 JSX 語法,強調組件化與單向資料流。
由尤雨溪開發,以「漸進式」框架著稱。它的學習曲線相對平緩,且官方提供了非常完整的工具鏈,非常適合追求開發效率的專案。
由 Google 維護的完整框架(Framework),採用 TypeScript 撰寫。它提供了「全家桶」式的解決方案,包含了路由、表單驗證、HTTP 客戶端等所有必要功能。
除了上述三大框架,近年來也出現了許多針對性能優化的替代方案:
| 框架名稱 | 特點 |
|---|---|
| Svelte | 無虛擬 DOM,在編譯階段將程式碼轉為高效的原生 JS。 |
| SolidJS | 與 React 語法相似,但擁有極致的運行性能。 |
| Preact | React 的輕量化版本,體積僅約 3KB,且高度相容。 |
選擇框架時,通常建議考慮以下基準點:
在 Python 生態系中,SPA(單頁應用程式)的開發方案主要分為兩類:一種是將 Python 作為後端 API,另一種則是利用特定框架,讓開發者只需撰寫 Python 程式碼,就能自動生成具有 SPA 特性的前端介面。
這類框架最受歡迎,因為它們不需要開發者具備 HTML/CSS/JS 知識,就能開發出即時互動的單頁網頁。
這類框架試圖挑戰傳統的 React/Vue + FastAPI 模式,讓全端開發更為統一。
| 框架名稱 | 特點 | 技術架構 |
|---|---|---|
| Reflex (原名 Pynecone) | 純 Python 撰寫,編譯成 React 應用。 | Python + Next.js / Tailwind |
| NiceGUI | 極其簡單的語法,適合控制硬體、小工具或簡單網頁。 | Python + Vue / Quasar |
| Flet | 基於 Flutter 的 Python 框架,可同時發布為網頁 SPA 與桌面應用。 | Python + Flutter |
這是最標準的商業軟體開發模式。Python 只負責邏輯與資料,前端則交給專業的 JavaScript 框架。
// 架構示意 後端 (Python): FastAPI / Django Ninja / Flask 傳輸協定: RESTful API 或 WebSocket 前端 (JavaScript): Vue.js / React / Svelte (負責渲染 SPA)
這是一個近年非常流行的趨勢。HTMX 讓傳統的 Python Web 框架(如 Django 或 Flask)不需要寫複雜的 JavaScript,就能實現 AJAX 局部更新頁面的效果。
# Django + HTMX 範例 (局部更新按鈕)
# 點擊後僅替換 #result 區域,不重新整理網頁
<button hx-post="/get-data/" hx-target="#result">
點我獲取數據
</button>
<div id="result">等待中...</div>
這類方案的核心理念是「回歸 HTML」。它們主張不需要編寫複雜的 JavaScript 框架,就能實現 SPA 的動態效果。
WebAssembly 允許開發者使用 C++、Rust 或 Go 等高效能語言編寫瀏覽器端邏輯,打破了 JavaScript 的壟斷。
| 語言 | 主要框架 | 特色 |
|---|---|---|
| C# / .NET | Blazor WebAssembly | 讓 .NET 開發者能用 C# 取代 JS 編寫前端。 |
| Rust | Yew / Leptos | 極致的執行效能與強大的型別安全。 |
| Go | Hugo / TinyGo | 編譯極速,適合執行邏輯複雜的運算。 |
這類方案在「建構時」就將所有頁面產生成靜態 HTML 檔案,載入速度極快且對 SEO 極為友善。
Google 推出的方案,使用 Dart 語言。它不使用標準的 HTML/CSS 渲染路徑,而是透過 Canvas 繪製整個介面。
// Flutter 邏輯範例
void main() {
runApp(const MyApp());
}
// 介面完全由 Canvas 繪製,適合高度視覺一致性的 App 移植
針對企業內部工具或快速上線需求,不需手寫程式碼即可建立 SPA。
| 方案 | 適用情境 | 技術門檻 |
|---|---|---|
| HTMX | 傳統網站升級互動感 | 低 (只需後端語言) |
| WebAssembly | 影像處理、複雜運算、遊戲 | 高 (需編譯語言基礎) |
| Astro (SSG) | 部落格、文件、官方網站 | 中 |
| Flutter for Web | App 同步發布至網頁 | 中 (需學 Dart) |
傳統 Web 架構如 PHP + JavaScript 常見於伺服器端部署,但無法有效防止原始碼被存取或被複製,因此不適合用於商業授權與部署在非受信環境。為解決此問題,「自我封裝 Web 應用(Self-hosted Web Application)」架構應運而生,它將後端邏輯編譯為不可讀的執行檔,內建 HTTP 服務能力,可讓前端仍以 HTML/JS 互動,而後端則能實現加密、安全、授權控制等需求。
| 名稱 | 使用語言 | 是否支援 License 管理 | 是否支援 C# | 可產生 Binary | 適合用途 | 使用率 / 市佔 |
|---|---|---|---|---|---|---|
| Electron | JavaScript + Node.js | 可搭配驗證系統 | 否 | 可(桌面應用) | 跨平台桌面 Web App | 非常高 |
| Node.js + pkg | JavaScript | 可搭配 JWT / Key 驗證 | 否 | 是 | 封裝 Web API 應用 | 高 |
| ASP.NET Core + Kestrel | C# | 原生支援 | 是 | 是(.exe/.dll) | 企業 Web API / 工具 | 高 |
| Go HTTP Server | Go | 內建或自訂 | 否 | 是 | 高效能 Web 後端 | 高 |
| Blazor WebAssembly + ASP.NET | C# | 支援授權驗證 | 是 | 是 | SPA + Web API 整合 | 中偏高 |
| Gradio | Python | 可搭配第三方 | 否 | 可(PyInstaller) | ML/AI Web UI | 中 |
| Streamlit | Python | 可搭配第三方 | 否 | 可(PyInstaller) | 資料視覺化 Web UI | 中 |
| Tauri | Rust + JS | 支援自訂驗證 | 否 | 是 | 超輕量桌面 App | 中 |
| Mongoose / Civetweb | C / C++ | 可嵌入驗證邏輯 | 可(搭配 CLI) | 是 | 嵌入式 Web Server | 中 |
ASP.NET Core 是由 Microsoft 開發的跨平台、開放原始碼的高效能 Web 應用框架,支援 Windows、Linux 與 macOS。它是 .NET 平台的一部分,專為現代 Web API、Web App 與微服務設計。相較於傳統 ASP.NET,Core 版本更輕量、模組化,並可編譯為獨立執行檔(.exe),非常適合用於自我封裝的 Web 應用架構。
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
這個指令可將 ASP.NET Core 應用編譯成一個獨立執行檔(例如 `MyApp.exe`),可直接在目標系統執行,不需安裝 .NET Runtime。
ASP.NET Core 可作為 .NET MAUI + Blazor 的後端,提供資料與 API。
若已有 Blazor Web 專案,幾乎可無痛搬移至 MAUI 應用,重用大量程式碼。
ASP.NET Core 專注於後端與 Web 架構,適合開發雲端與 API。
.NET MAUI + Blazor 專注於用戶端跨平台應用,特別適合已有 Blazor 前端技術棧的團隊。
兩者結合時,可達到「後端服務 + 前端跨平台 App」的完整解決方案。
| IDE | 支援平台 | 特色 | 是否免費 | 適合對象 |
|---|---|---|---|---|
| Visual Studio | Windows | 完整功能,最佳官方支援 | Community 免費(其他付費) | 企業與全端開發者 |
| Visual Studio Code | Windows / Linux / macOS | 輕量、擴充性高 | 免費 | 跨平台開發者、學生 |
| JetBrains Rider | Windows / Linux / macOS | ReSharper 整合、跨平台 | 需付費 | 進階開發者與跨平台團隊 |
Ctrl + `)。dotnet new webapp -o MyAspNetApp
webapp:建立含 Razor Pages 的 ASP.NET Core Web 應用程式。-o MyAspNetApp:指定專案資料夾名稱。cd MyAspNetApp
dotnet run
https://localhost:5001 或 http://localhost:5000 執行。檔案 → 開啟資料夾,載入剛建立的 MyAspNetApp。dotnet add package 套件名稱
dotnet new webapp → Razor Pages Web Appdotnet new mvc → MVC 架構dotnet new webapi → Web API 專案Ctrl + Shift + P,輸入並選擇 Debug: Open launch.json。.vscode/launch.json。{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/MyAspNetApp.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
}
]
}
.vscode 資料夾生成 tasks.json。{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MyAspNetApp.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
F5 或從左側「執行與偵錯」面板選擇 .NET Core Launch (web)。https://localhost:5001。dotnet new webapi -o MyWebApiApp
cd MyWebApiApp
code .
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web API)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/MyWebApiApp.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
}
]
}
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MyWebApiApp.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
F5 或從「執行與偵錯」面板選擇 .NET Core Launch (web API)。https://localhost:5001 或 http://localhost:5000。.http 或 .rest 檔案測試 API。### 測試 GET API
GET https://localhost:5001/weatherforecast
Accept: application/json
###
### 測試 POST API 範例
POST https://localhost:5001/api/sample
Content-Type: application/json
{
"id": 1,
"name": "測試資料"
}
test.http。Send Request 按鈕。Kestrel 是 ASP.NET Core 的內建跨平台 Web 伺服器,由 Microsoft 所開發。它以高效能、輕量化為設計目標,支援 HTTP/1.1 與 HTTP/2,是 ASP.NET Core 應用的預設伺服器。使用 Kestrel 可讓開發者直接將 Web 應用包裝為 自我封裝的執行檔,無需依賴 IIS、Apache 或 Nginx 等外部伺服器,即可本機啟動並提供 HTTP 服務。
UseUrls 設定服務的本機埠與路徑public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel()
.UseUrls("http://localhost:5000");
var app = builder.Build();
app.MapGet("/", () => "Hello from Kestrel!");
app.Run();
}
}
dotnet publish 將應用與 Kestrel 一併包裝為單一執行檔| 項目 | Kestrel | IIS / Nginx |
|---|---|---|
| 是否內建 | 是(ASP.NET Core 內建) | 否(需另行安裝) |
| 支援平台 | Windows / Linux / macOS | 依伺服器而異 |
| 是否需管理員權限 | 否 | 視平台與設定而定 |
| 是否適合自我封裝 | 非常適合 | 不適合 |
| 效能表現 | 高 | 高(但設定較複雜) |
ASP.NET Core 支援將前端資源(HTML、CSS、JavaScript、SPA 等)與後端 API 服務整合成單一應用,並部署為一個獨立的執行單元(Self-contained application),達到部署簡單、原始碼保護與 License 管控的目的。
wwwroot/ 資料夾,供 ASP.NET Core 作為靜態資源提供。wwwroot/。wwwroot,例如:
npm run build 後的 dist/ 複製進 wwwroot/npm run build 後的 build/ 複製進 wwwroot/Program.cs 中有啟用靜態檔案服務:
app.UseStaticFiles();
app.MapFallbackToFile("index.html");
dotnet publish -c Release -r win-x64 --self-contained
ASP.NET Core MVC 是 Microsoft 推出的跨平台 Web 應用框架,基於 Model-View-Controller(模型-視圖-控制器)設計模式,提供結構化且模組化的方式來開發動態網站與 Web API。
// Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// Controller
public class ProductsController : Controller
{
public IActionResult Index()
{
var products = new List<Product>
{
new Product { Id = 1, Name = "筆記型電腦", Price = 29999 },
new Product { Id = 2, Name = "滑鼠", Price = 499 },
new Product { Id = 3, Name = "鍵盤", Price = 899 }
};
return View(products);
}
}
// View (Index.cshtml)
@model List<Product>
<table border="1" cellpadding="6" cellspacing="0">
<thead>
<tr>
<th>編號</th>
<th>名稱</th>
<th>價格</th>
</tr>
</thead>
<tbody>
@foreach(var item in Model)
{
<tr>
<td>@item.Id</td>
<td>@item.Name</td>
<td>@item.Price.ToString("C")</td>
</tr>
}
</tbody>
</table>
<!-- Product.cs - 資料模型 -->
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
<!-- ProductsController.cs - 控制器(ASP.NET Core MVC) -->
public class ProductsController : Controller
{
public IActionResult Index()
{
var products = new List<Product>
{
new Product { Id = 1, Name = "筆記型電腦", Price = 29999 },
new Product { Id = 2, Name = "滑鼠", Price = 499 },
new Product { Id = 3, Name = "鍵盤", Price = 899 }
};
return View(products);
}
}
<!-- Views/Products/Index.cshtml - Razor View 頁面 -->
@model List<Product>
<table border="1" cellpadding="6" cellspacing="0">
<thead>
<tr>
<th>編號</th>
<th>名稱</th>
<th>價格</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.Id</td>
<td>@item.Name</td>
<td>@item.Price.ToString("C")</td>
</tr>
}
</tbody>
</table>
Razor 是由 Microsoft 為 ASP.NET Core 開發的一種標記語言,讓開發者可以在 HTML 頁面中嵌入 C# 程式碼。它結合了標準的 HTML 與 C#,使得動態產生網頁變得簡單且直覺。
@page
@model IndexModel
<h1>歡迎使用 Razor 頁面</h1>
<p>今天日期:@DateTime.Now.ToString("yyyy-MM-dd")</p>
@if(Model.Items != null && Model.Items.Any())
{
<ul>
@foreach(var item in Model.Items)
{
<li>@item</li>
}
</ul>
}
else
{
<p>沒有資料顯示</p>
}
在 Razor 檢視中使用:
@foreach(var item in Model)
{
<div>@item</div>
}
若出現以下錯誤:
System.NullReferenceException: Object reference not set to an instance of an object.
表示 Model 為 null,導致無法進行迴圈。
public IActionResult Index()
{
var data = new List<string> { "A", "B", "C" };
return View(data);
}
@model IEnumerable<string>
@if (Model != null)
{
foreach (var item in Model)
{
<div>@item</div>
}
}
else
{
<div>沒有資料</div>
}
最佳做法是 在控制器確保 Model 一定有值(即使是空集合),避免 View 處理 null 的情況。例如:
return View(data ?? new List<string>());
在 ASP.NET Core MVC 中,一個專案可以有多個 Controller,每個 Controller 都負責不同的功能或領域。 例如:
HomeController — 處理首頁、靜態頁面ProductController — 處理商品管理
每個 Controller 必須繼承 Controller 類別,並且存放在 Controllers 資料夾中。
// HomeController.cs
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
// ProductController.cs
public class ProductController : Controller
{
public IActionResult List()
{
var products = new List<string> { "電腦", "手機", "平板" };
return View(products);
}
}
MVC 的 View 預設會根據 Controller 名稱建立資料夾,例如:
Views/Home/Index.cshtmlViews/Product/List.cshtml
可以使用 asp-controller 和 asp-action 標籤輔助標記 (Tag Helpers):
<a asp-controller="Home" asp-action="Index">回首頁</a>
<a asp-controller="Product" asp-action="List">商品列表</a>
在 Program.cs 或 Startup.cs 中,預設的路由通常為:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
這表示如果網址是 /,會自動導向 HomeController.Index()。
如果網址是 /Product/List,則會執行 ProductController.List()。
MVC 中可以同時擁有多個 Controller,只要:
ControllerViews 中建立對應的資料夾與檔案asp-controller 與 asp-action 正確呼叫Model(或一個合成 ViewModel),但可以在同一頁面顯示或連結到多個 Controller 的資料/Action。// Models/ViewModels.cs
public class HomePageViewModel {
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Order> RecentOrders { get; set; }
}
// HomeController.cs
public IActionResult Index() {
var vm = new HomePageViewModel {
Products = productService.GetTopProducts(),
RecentOrders = orderService.GetRecentOrders()
};
return View(vm);
}
// Views/Home/Index.cshtml
@model HomePageViewModel
<h2>商品</h2>
@foreach(var p in Model.Products){ <div>@p.Name</div> }
<h2>近期訂單</h2>
@foreach(var o in Model.RecentOrders){ <div>@o.Id</div> }
// Views/Product/_ProductList.cshtml (partial)
@model IEnumerable<Product>
@foreach(var p in Model){ <div>@p.Name</div> }
// Views/Home/Index.cshtml (父 View)
@model HomePageViewModel
<div>
@Html.Partial("_ProductList", Model.Products)
</div>
// Components/ProductListViewComponent.cs
public class ProductListViewComponent : ViewComponent {
private readonly IProductService _svc;
public ProductListViewComponent(IProductService svc){ _svc = svc; }
public IViewComponentResult Invoke(int count) {
var products = _svc.GetTopProducts(count);
return View(products); // Views/Shared/Components/ProductList/Default.cshtml
}
}
// 使用於 Razor
@await Component.InvokeAsync("ProductList", new { count = 5 })
<!-- Views/Home/Index.cshtml -->
<div id="product-area">載入中...</div>
<script>
fetch('/api/product/top5')
.then(r => r.json())
.then(data => {
document.getElementById('product-area').innerHTML =
data.map(p => `${p.name}`).join('');
});
</script>
<a asp-controller="Product" asp-action="List">商品列表</a>
<a asp-controller="Order" asp-action="History">訂單紀錄</a>
ViewModel 或 ViewComponent(較模組化)。Partial View 或 ViewComponent。Java Applet 是一種以 Java 語言編寫的小型應用程式,可透過網頁瀏覽器執行,並嵌入至 HTML 頁面中。它最早於 1990 年代被廣泛應用於製作互動式網頁,例如動畫、遊戲及圖表視覺化。
Applet 必須在支援 Java Plugin 的瀏覽器上執行,透過 <applet> 或 <object> 標籤嵌入於 HTML 中,由使用者端 JVM(Java Virtual Machine)負責載入與執行。
由於 Applet 可在用戶端執行,因此為了防止惡意行為,執行環境採取了「沙盒機制」進行安全隔離,例如禁止存取本機檔案系統或執行外部程式。
Oracle 自 Java 9 起即宣佈移除 Applet API,並在 Java 11 完全廢止 Applet。Java Web Start(另一種桌面執行技術)也一併終止維護。
Java Applet 已進入淘汰階段,現代網頁開發趨勢傾向使用 HTML5、JavaScript 與 WebAssembly 等開放標準技術。開發者應避免再使用 Applet,並將現有應用程式轉移至現代技術平台。
以下是顯示一個簡單訊息的 Java Applet 範例:
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>Java Applet 範例</title>
</head>
<body>
<h1>Java Applet 示例</h1>
<applet code="HelloWorldApplet.class" width="300" height="300>
<param name="message" value="你好,世界!">
您的瀏覽器不支持 Java Applet。
</applet>
</body>
</html>
import java.applet.Applet;
import java.awt.Graphics;
public class HelloWorldApplet extends Applet {
String message;
public void init() {
message = getParameter("message");
}
public void paint(Graphics g) {
g.drawString(message, 20, 20);
}
}
1. 將 Java 代碼保存為 HelloWorldApplet.java,並在命令行中執行 javac HelloWorldApplet.java 來編譯。
2. 確保生成的 .class 文件與 HTML 文件在同一個目錄下。
3. 使用支持 Java Applet 的環境來執行 HTML 文件。
由於大多數現代瀏覽器不再支持 Java Applet,建議考慮使用其他技術(如 JavaFX 或網頁應用程式)來實現類似的功能。
email: [email protected]