JavaScript手錄18-ajax:異步請求與項目上線部署

畫板

前言:軟件開發流程

AJAX:前端與后端的數據交互

前后端協作基礎

Web應用的核心是“數據交互”,前端負責展示與交互,后端負責處理邏輯與數據存儲,二者通過網絡請求協作。

(1)項目開發流程與崗位分工
  • 核心流程:產品經理(需求)→ UI設計師(界面)→ 前端(可視化)→ 后端(邏輯)→ 測試(驗證)→ 運維(上線)。
  • 核心崗位
    • 前端:負責用戶可見的界面(HTML/CSS/JS/Vue/React/UI框架例如element-ui/Ant-Design等)、數據展示、用戶交互。
    • 后端:處理業務邏輯(如登錄驗證、訂單處理)、操作數據庫、提供數據接口。
    • 數據庫:存儲結構化數據(如用戶信息、商品庫存),僅允許后端訪問。

畫板

(2)前后端職責邊界
前端職責后端職責
構建用戶界面(靜態+動態渲染)處理業務邏輯(如權限校驗、計算)
收集用戶操作(如表單輸入、點擊)操作數據庫(增刪改查數據)
發送請求到后端,接收并展示數據提供API接口,返回格式化數據(JSON)
處理前端驗證(如表單格式)處理后端驗證(如數據合法性、權限)
數據流轉全流程

用戶操作觸發的數據交互需經過“前端→后端→數據庫→后端→前端”的完整鏈路:

  1. 用戶操作:如點擊“查詢商品”按鈕。
  2. 前端處理:收集參數(如商品ID),通過AJAX發送請求到后端接口(如/api/goods)。
  3. 后端處理
    • 接收請求,驗證參數(如用戶權限)。
    • 向后端數據庫發送查詢命令(如SQL語句)。
  4. 數據庫響應:返回符合條件的數據(如商品詳情)。
  5. 后端響應:將數據庫數據格式化(如JSON),返回給前端。
  6. 前端渲染:接收數據,通過DOM操作更新頁面(如展示商品信息)。

畫板

一、服務器與云端基礎

服務器:數據存儲與處理的核心

服務器是支撐Web應用運行的硬件基礎,其本質是一臺高性能計算機,負責接收前端請求、處理業務邏輯并返回數據。

(1)服務器類型與特點
類型定義與適用場景優勢劣勢
物理服務器獨立的實體計算機,硬件資源獨占(如企業自建機房服務器)性能穩定、安全性高、可完全定制成本高(幾萬元起)、需專人維護、擴容困難
云服務器基于云計算的虛擬服務器(如阿里云ECS、騰訊云CVM),資源按需分配成本低(入門級100元/月起)、彈性擴容、無需維護硬件性能受限于服務商配置、依賴網絡穩定性
虛擬主機一臺物理服務器分割出的多個虛擬空間(共享硬件資源)價格極低(幾十元/年)、操作簡單資源受限(帶寬、存儲)、自主性差
(2)云服務器核心特性
  • 彈性計算:可根據訪問量實時調整CPU、內存、帶寬(如促銷活動時臨時擴容)。
  • 按需付費:支持按小時、按月付費,避免資源浪費(適合個人開發者和初創項目)。
  • 高可用性:云服務商提供多地域部署、容災備份,降低服務器宕機風險(如阿里云的多可用區)。
(3)主流云服務商對比
服務商優勢領域適合場景
阿里云國內市場份額最高、生態完善(ECS+OSS+CDN)、支持中小到大型項目國內業務、需要穩定備案服務的項目
騰訊云社交場景優化好(如小程序對接)、價格親民、新手友好小程序/公眾號關聯項目、初創團隊
AWS(亞馬遜云)全球節點最多、技術成熟、支持復雜架構(如跨境業務)海外業務、大型企業級項目
華為云政務項目優勢明顯、安全合規性強政企合作項目、對安全性要求高的場景

域名:服務器的“門牌號”

域名是服務器的人類可讀標識(如baidu.com),用于替代難記的IP地址(如180.101.50.242),是用戶訪問網站的入口。

(1)域名結構與類型
  • 結構:由“主機名.二級域名.頂級域名”組成(如blog.baidu.com中,blog是主機名,baidu是二級域名,com是頂級域名)。
  • 類型
    • 頂級域名(TLD):如.com(商業)、.cn(中國)、.org(非營利)、.xyz(通用)。
    • 二級域名:如baidu.com(需注冊),三級域名:如map.baidu.com(由二級域名衍生,無需額外注冊)。
(2)域名注冊與定價
  • 注冊流程:通過域名服務商(如阿里云萬網、騰訊云域名)查詢→購買→實名認證→解析(綁定IP)。
  • 定價因素
    • 頂級域名后綴:.com(約60元/年)、.cn(約30元/年)較貴,.xyz(約10元/年)等小眾后綴便宜。
    • 域名長度與含義:短域名(如jd.com)、有特殊含義的域名(如chat.com)價格極高(萬元至千萬元級)。
  • 搶注風險:域名過期后會進入贖回期(通常30天),未續費則被公開搶注,需及時續費。
(3)域名解析與備案
  • 解析:將域名指向服務器IP的過程,通過DNS服務器完成。常用解析記錄:
    • A記錄:直接指向IP地址(如127.0.0.1)。
    • CNAME記錄:指向另一個域名(如將www.abc.com指向abc.com)。
  • 備案:國內服務器使用域名必須備案(免費,約1-2周),否則無法通過域名訪問;海外服務器可免備案,但訪問速度較慢。

服務器空間:項目的“存儲單元”

服務器空間是服務器中劃分出的專用存儲區域,用于存放網站文件(HTML、CSS、JS、圖片等),類比“公寓中的單間”。

(1)空間類型對比
類型定義適用場景
虛擬主機多人共享一臺服務器的硬件資源(CPU、內存、帶寬),僅提供基礎存儲和運行環境個人博客、小型靜態網站(日訪問量<1000)
VPS(虛擬專用服務器)虛擬出獨立的服務器環境,資源隔離,可自主配置(如安裝軟件、修改端口)中小型動態網站(日訪問量1000-10000)
獨立服務器完全獨占一臺服務器的所有資源大型網站、高并發業務(日訪問量>10萬)
(2)空間核心參數
  • 存儲容量:影響可存放文件的大小(如1GB空間可存放約500張高清圖片)。
  • 帶寬:決定數據傳輸速度,帶寬越低,多人同時訪問時越容易卡頓(推薦個人項目至少1M帶寬)。
  • 操作系統:Windows(支持ASP、.NET)或Linux(支持PHP、Python,更穩定、開源)。

云端服務:靈活高效的資源方案

“云端”指通過互聯網訪問的遠程服務器資源,無需自建機房,按需使用,是現代Web開發的主流選擇。

(1)主流云服務類型
  • 云服務器(ECS/EC2):彈性計算服務,提供虛擬服務器實例,可完全定制(適合中大型項目)。
  • 輕量應用服務器:簡化版云服務器,預裝常用環境(如LAMP、Node.js),操作簡單(適合新手)。
  • 對象存儲(OSS/S3):專門存儲靜態資源(圖片、視頻、文檔),支持CDN加速(提升訪問速度)。
  • 數據庫服務(RDS):托管的數據庫(MySQL、MongoDB等),自動備份、高可用(無需手動維護)。
(2)云端服務優勢
  • 低成本:無需購買硬件,按使用量付費(如1核2G云服務器約500元/年)。
  • 高可靠:服務商提供99.9%以上的運行保障,多地域備份避免數據丟失。
  • 易擴展:業務增長時可快速提升配置(如從1核2G升級到4核8G),無需停機。

二、AJAX:前端與后端的異步交互

AJAX(Asynchronous JavaScript and XML)是一種在不刷新頁面的情況下,通過JavaScript與后端進行數據交互的技術,核心是“異步請求+局部更新”。

AJAX的核心特性:異步執行

  • 異步含義:請求發送后,瀏覽器不會等待響應,而是繼續執行后續代碼(避免頁面卡頓)。
  • 執行順序:同步代碼(如console.log)優先于異步代碼(AJAX回調)執行,即使AJAX寫在前面。

示例

console.log("1. 開始發送請求");// 創建AJAX請求(異步)
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/data", true);
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {console.log("3. 請求成功,收到數據"); // 最后執行}
};
xhr.send();console.log("2. 請求已發送,繼續執行"); // 先于回調執行

輸出順序:1 → 2 → 3(體現異步特性)

AJAX的完整使用步驟

(1)創建核心對象(XMLHttpRequest)

XMLHttpRequest是瀏覽器內置的AJAX核心對象,負責發送請求和接收響應:

// 兼容IE6及以下(現代瀏覽器直接用new XMLHttpRequest())
const xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
(2)創建請求(open方法)

xhr.open(method, url, async)用于初始化請求,參數說明:

  • method:請求方式(GET/POST/PUT/DELETE,大小寫不敏感)。
  • url:請求地址(服務器接口URL,如"http://localhost:8080/api/user")。
  • async:是否異步(必須為true,否則失去AJAX意義)。
xhr.open("GET", "/api/user?id=1", true); // GET請求示例
// 或
xhr.open("POST", "/api/login", true); // POST請求示例
(3)設置請求頭(POST專用)

POST請求需手動設置Content-Type請求頭,否則后端無法正確解析參數:

xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
(4)發送請求(send方法)

xhr.send(data)用于發送請求,data為請求參數(僅POST需要):

  • GET請求:參數拼接在URL后,sendnull
  • POST請求:參數以key=value&key2=value2格式傳入send
// GET請求(無參數)
xhr.send(null);// POST請求(帶參數)
xhr.send("username=admin&password=123456");
(5)監聽響應(onreadystatechange事件)

當請求狀態(readyState)變化時觸發,需同時滿足“請求完成(readyState=4)”和“請求成功(status=200)”才處理響應:

readyState狀態描述
0請求未初始化(未調用open)
1正在發送請求(已調用open,未調用send)
2請求發送完成(已調用send)
3正在接收響應(部分數據已返回)
4響應接收完成(可處理數據)

示例

xhr.onreadystatechange = function() {// 僅當請求完成且成功時處理if (xhr.readyState === 4) {if (xhr.status === 200) {const data = xhr.responseText; // 獲取響應數據(文本格式)console.log("響應數據:", data);} else {console.error("請求失敗,狀態碼:", xhr.status);}}
};
(6)解析響應數據

xhr.responseText返回純文本數據,需根據后端格式解析(通常為JSON):

if (xhr.status === 200) {const data = JSON.parse(xhr.responseText); // 轉換為JSON對象console.log("用戶姓名:", data.name);
}

3. GET與POST請求的核心區別

對比維度GET請求POST請求
參數位置拼接在URL后(url?key=value&key2=value2放在請求體中(send方法傳入)
可見性地址欄可見(不安全)不可見(相對安全)
參數長度限制有限制(URL長度通常≤2048字符)無限制(理論上)
緩存可被瀏覽器緩存(如刷新后復用結果)默認不緩存
用途用于查詢數據(如搜索、獲取列表)用于提交數據(如登錄、注冊、上傳)
請求頭無需額外設置必須設置Content-Type: application/x-www-form-urlencoded

4. 常用HTTP狀態碼解析

狀態碼是服務器返回的請求結果標識,掌握常見狀態碼可快速定位問題:

狀態碼含義常見場景與排查方向
200請求成功后端正常處理并返回數據
201創建成功新增資源(如注冊用戶、發布文章)成功
400請求錯誤參數格式錯誤(如缺少必填項、格式不匹配)
401未授權未登錄或登錄過期(需重新登錄)
403禁止訪問無權限訪問(如普通用戶訪問管理員接口)
404資源未找到URL錯誤或資源已刪除(檢查請求地址)
500服務器內部錯誤后端代碼報錯(需后端排查日志)
503服務不可用服務器過載或維護中(稍后重試)

5. AJAX的現代替代方案

傳統XMLHttpRequest語法繁瑣,現代開發中常用更簡潔的方案:

(1)fetch API(瀏覽器內置,基于Promise)
fetch("/api/user?id=1").then(response => {if (!response.ok) throw new Error("請求失敗");return response.json(); // 自動解析JSON}).then(data => console.log("用戶數據:", data)).catch(error => console.error("錯誤:", error));
(2)axios(第三方庫,推薦)

封裝了XMLHttpRequest,支持Promise、攔截器、請求取消等:

// 安裝:npm install axios
import axios from "axios";axios.get("/api/user?id=1").then(response => console.log("用戶數據:", response.data)).catch(error => console.error("錯誤:", error));// POST請求
axios.post("/api/login", { username: "admin", password: "123" }).then(response => console.log("登錄結果:", response.data));

三、本地服務器搭建與項目部署

1. 本地服務器:開發與測試環境

本地服務器是在個人電腦上搭建的模擬服務器環境,用于開發時調試AJAX請求和項目運行效果。可以用于個人學習理解項目開發到上線部署全流程。

(1)常用工具
  • XAMPP:跨平臺(Windows/macOS/Linux),集成Apache(Web服務器)、MySQL(數據庫)、PHP(后端語言),適合新手。
  • WAMP:僅支持Windows,功能與XAMPP類似。
  • MAMP:僅支持macOS,對蘋果設備兼容性更好。
(2)XAMPP搭建步驟
  1. 下載安裝:從XAMPP官網下載對應系統版本,按提示安裝(默認路徑即可)。

  1. 啟動服務:打開XAMPP控制面板,點擊“Start”啟動Apache(Web服務器)。

  1. 驗證運行:瀏覽器訪問http://localhosthttp://127.0.0.1,出現XAMPP默認頁面即成功。

(3)端口號配置

端口號是服務器上區分不同服務的“房間號”,默認端口:

  • HTTP協議:80(可省略,如localhost:80等價于localhost)。
  • HTTPS協議:443。
  • MySQL數據庫:3306。

端口沖突解決:若80端口被占用(如被IIS、迅雷占用),可修改Apache端口:

  1. XAMPP控制面板點擊“Config”→“Apache (httpd.conf)”。
  2. 搜索Listen 80,改為Listen 8080(或其他未占用端口)。
  3. 重啟Apache,訪問時需帶端口:http://localhost:8080

2. 項目部署:從本地到線上

部署是將開發完成的項目文件上傳到服務器,使互聯網用戶可訪問的過程。

(1)本地部署步驟(XAMPP為例)
  1. 放置文件:將項目文件(HTML、CSS、JS等)復制到XAMPP安裝目錄的htdocs文件夾(如C:\xampp\htdocs\myproject)。

  1. 訪問項目:瀏覽器輸入http://localhost/myproject/index.html(或http://127.0.0.1:8080/myproject/index.html,帶端口時)。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>AJAX</title>
</head>
<body>
<p>一個上線的項目</p><script>console.log('一個上線的項目');</script>
</body>
</html>

(2)線上部署步驟(云服務器為例)
  1. 購買云服務器:選擇合適配置(如1核2G,1M帶寬),操作系統選Linux(如CentOS)。
  2. 安裝環境:通過服務器管理面板(如寶塔面板)安裝Apache/Nginx、PHP(如需)。
  3. 上傳文件:用FTP工具(如FileZilla)將項目文件上傳到服務器的網站根目錄(如/www/wwwroot/域名)。
  4. 配置域名:在云服務商控制臺將域名解析到服務器IP,完成備案(國內服務器)。
  5. 測試訪問:通過域名訪問項目,檢查是否正常運行(如http://www.你的域名.com)。
(3)部署注意事項
  • 路徑問題:項目中資源引用需用相對路徑(如./css/style.css),避免絕對路徑(如C:\...)。
  • 權限設置:服務器文件需設置正確權限(如Linux中chmod 755),避免因權限不足導致訪問失敗。
  • 備份:定期備份項目文件和數據庫,防止數據丟失。
  • HTTPS:通過云服務商申請免費SSL證書(如Let’s Encrypt),開啟HTTPS(提升安全性,瀏覽器地址欄顯示鎖圖標)。
(4)常見問題與解決
  • 端口沖突(啟動失敗,日志顯示“Port 80 in use”):
    1. 原因:80端口被其他程序(如IIS、迅雷、Skype)占用;
    2. 解決:
      • 關閉占用端口的程序(通過“任務管理器”結束進程);
      • 修改Apache端口:XAMPP控制面板→“Config”→“Apache (httpd.conf)”,搜索 Listen 80 改為 Listen 8080,重啟Apache后通過 http://localhost:8080 訪問。
  • 權限問題(無法訪問項目文件):
    1. 確保項目文件放在XAMPP的 htdocs 目錄下(如 C:\xampp\htdocs\myproject);
    2. 首次打開 htdocs 時,在VS Code中點擊“信任父文件夾”授權編輯。
  • 開發注意
    • 必須通過服務器IP訪問(而非雙擊本地文件),否則AJAX請求會因“跨域”或“協議限制”失敗;
    • 修改文件后需刷新瀏覽器,確保操作的是 htdocs 目錄下的文件(避免本地副本與服務器文件不一致)。

四、AJAX請求完整流程(結合PHP后端)

AJAX的核心是前端與后端的異步數據交互,以下以“前端發送請求→PHP處理→前端接收響應”為例,詳解完整流程。

1. 后端準備:創建PHP接口

PHP是常用的后端語言,可快速處理前端請求并返回數據(XAMPP默認集成PHP環境)。

(1)PHP文件創建與基礎語法
  • 文件規范
    • 擴展名必須為 .php(如 api.php);
    • 保存路徑:htdocs 目錄下(如 C:\xampp\htdocs\api.php)。

注意調用的路徑

  • 基礎語法
<?php
// 簡單響應示例
echo "Hello, AJAX!"; // 輸出字符串,前端通過responseText接收
?>
- PHP代碼必須包裹在 `<?php ?>` 標簽內;  
- 輸出數據用 `echo` 命令,語句以分號 `;` 結尾(嚴格要求,缺少會報錯)。
(2)PHP接收前端參數

后端通過 $_REQUEST 全局變量接收前端傳遞的參數(支持GET和POST方式),參數名需與前端完全一致。

示例:接收前端的 usernamehobby 參數并返回對應響應

<?php
// 接收參數(前后端參數名必須一致)
$username = $_REQUEST["username"]; // 獲取username參數
$hobby = $_REQUEST["hobby"];       // 獲取hobby參數// 根據參數返回不同響應
if ($hobby === "打籃球") {echo $username . " 喜歡打籃球!";
} else if ($hobby === "踢足球") {echo $username . " 喜歡踢足球!";
} else {echo $username . " 沒有明確的愛好~";
}
?>

2. 前端實現:發送AJAX請求

前端通過 XMLHttpRequest 對象發送請求,需嚴格遵循“創建對象→配置請求→發送→處理響應”步驟。

(1)GET請求實現(帶參數)

GET請求參數拼接在URL后,格式為 ?key1=value1&key2=value2

// 1. 創建XMLHttpRequest核心對象
const xhr = new XMLHttpRequest();// 2. 配置請求(GET方式,帶參數)
// URL格式:接口地址?參數1=值1&參數2=值2
xhr.open("GET", "api.php?username=小明&hobby=打籃球", true);// 3. 發送請求(GET請求send傳null)
xhr.send(null);// 4. 監聽響應狀態變化
xhr.onreadystatechange = function() {// 僅當請求完成(readyState=4)且成功(status=200)時處理if (xhr.readyState === 4 && xhr.status === 200) {const response = xhr.responseText; // 獲取后端返回的文本console.log("響應結果:", response); // 輸出:"小明 喜歡打籃球!"document.getElementById("result").innerHTML = response; // 渲染到頁面}
};
(2)POST請求實現(帶參數)

POST請求參數放在 send 方法中,需額外設置 Content-Type 請求頭,否則后端無法解析。

// 1. 創建核心對象
const xhr = new XMLHttpRequest();// 2. 配置請求(POST方式)
xhr.open("POST", "api.php", true);// 3. 設置請求頭(POST必須,指定參數格式)
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");// 4. 發送請求(參數放在send中,格式同GET)
xhr.send("username=小紅&hobby=踢足球");// 5. 處理響應
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {document.getElementById("result").innerHTML = xhr.responseText; // 輸出:"小紅 喜歡踢足球!"}
};
(3)請求狀態監控詳解
  • readyState:表示請求生命周期狀態(0-4),僅當值為4時表示響應完全接收。
  • status:HTTP狀態碼,200表示成功,404表示接口不存在,500表示后端代碼錯誤。

狀態碼處理最佳實踐

xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {// 成功處理console.log("請求成功:", xhr.responseText);} else if (xhr.status === 404) {console.error("接口不存在,請檢查URL");} else if (xhr.status === 500) {console.error("后端出錯,請聯系開發人員");} else {console.error("請求失敗,狀態碼:", xhr.status);}}
};

開發實踐建議

  • 敏感數據(如密碼、銀行卡號)必須用POST請求,且配合HTTPS加密傳輸;
  • 頻繁查詢(如商品列表)用GET請求,利用瀏覽器緩存提升性能;
  • 文件上傳只能用POST請求(Content-Type: multipart/form-data);
  • 后端接口會明確指定請求方式(如API文檔標注 GET /api/userPOST /api/login),前端需嚴格遵循。

一個前后端交互的-get請求示例

五、JSON:前后端數據交換的標準格式

JSON(JavaScript Object Notation)是輕量級數據交換格式,因簡潔、跨語言、易解析的特性,成為前后端交互的行業標準。

1. JSON基本語法規則

  • 數據結構
    • 對象:用 {} 包裹的鍵值對集合,鍵必須用雙引號 " 包裹(如 {"name": "小明"});
    • 數組:用 [] 包裹的有序值集合(如 ["籃球", "足球"])。
  • 值類型:字符串(雙引號)、數字、布爾值(true/false)、數組、對象、null
  • 語法要求
    • 鍵和字符串值必須用雙引號(單引號無效);
    • 末尾不能有多余逗號(如 {"a": 1,} 錯誤);
    • 不支持注釋(JSON5擴展支持,但不推薦)。

正確示例

{"name": "小明","age": 18,"hobbies": ["打籃球", "聽音樂"],"isStudent": true,"address": null
}

2. JSON與JavaScript對象的轉換

前端需將后端返回的JSON字符串轉為JS對象才能使用,反之,發送復雜數據時需將JS對象轉為JSON字符串。

(1)JSON字符串 → JS對象:JSON.parse()
// 后端返回的JSON字符串
const jsonStr = '{"name": "小明", "age": 18}';// 轉換為JS對象
const user = JSON.parse(jsonStr);
console.log(user.name); // "小明"(可直接訪問屬性)
  • 高級用法:通過 reviver 參數過濾或轉換數據
const user = JSON.parse(jsonStr, (key, value) => {if (key === "age") return value + 1; // 年齡+1return value;
});
console.log(user.age); // 19
(2)JS對象 → JSON字符串:JSON.stringify()
// JS對象
const user = { name: "小紅", age: 17, hobbies: ["踢足球"] };// 轉換為JSON字符串
const jsonStr = JSON.stringify(user);
console.log(jsonStr); // '{"name":"小紅","age":17,"hobbies":["踢足球"]}'
  • 高級用法
// 只轉換name和hobbies,且格式化輸出
const jsonStr = JSON.stringify(user, ["name", "hobbies"], 2); 
/* 輸出:
{"name": "小紅","hobbies": ["踢足球"]
}
*/
- `replacer`:過濾需要轉換的屬性;  
- `space`:格式化輸出(便于閱讀)。

3. 常見JSON解析錯誤及解決

  • 錯誤1:屬性名未用雙引號
{ name: "小明" } // 錯誤,鍵必須用雙引號

解決:改為 {"name": "小明"}

  • 錯誤2:使用單引號
{'name': '小明'} // 錯誤,雙引號是唯一標準

解決:統一替換為雙引號。

  • 錯誤3:尾部多余逗號
{"name": "小明", "age": 18,} // 錯誤,末尾不能有逗號

解決:刪除多余逗號。

  • 錯誤4:包含函數/Symbol
const obj = { fn: () => {}, s: Symbol("id") };
JSON.stringify(obj); // 結果為"{}"(函數和Symbol會被忽略)

解決:避免在JSON中包含這些類型,或提前處理。

五、AJAX實戰:動態數據交互與深復制

1. 動態數據渲染流程

結合JSON實現“前端請求→后端返回JSON→前端解析并渲染”的完整流程:

后端PHP(user_api.php

<?php
// 模擬從數據庫獲取數據
$user = ["name" => $_REQUEST["name"],"age" => (int)$_REQUEST["age"], // 轉換為數字類型"hobbies" => explode(",", $_REQUEST["hobbies"]) // 字符串轉數組
];// 輸出JSON格式字符串(必須用雙引號)
echo json_encode($user); // PHP內置函數,自動轉換為JSON
?>

前端JS

// 獲取用戶輸入
const name = document.getElementById("name").value;
const age = document.getElementById("age").value;
const hobbies = document.getElementById("hobbies").value;// 發送POST請求
const xhr = new XMLHttpRequest();
xhr.open("POST", "user_api.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(`name=${name}&age=${age}&hobbies=${hobbies}`);// 處理響應
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {// 解析JSON字符串為JS對象const user = JSON.parse(xhr.responseText);// 動態渲染到頁面const html = `<h3>${user.name}</h3><p>年齡:${user.age}</p><p>愛好:${user.hobbies.join("、")}</p>`;document.getElementById("userInfo").innerHTML = html;}
};

2. 基于JSON的對象深復制

深復制(完全復制對象,修改副本不影響原對象)可通過“JSON.stringify + JSON.parse”實現,適合簡單對象。

// 原對象
const obj = { name: "小明", info: { age: 18, hobby: "籃球" } };// 深復制
const objCopy = JSON.parse(JSON.stringify(obj));// 修改副本,原對象不受影響
objCopy.info.age = 19;
console.log(obj.info.age); // 18(原對象未變)
console.log(objCopy.info.age); // 19(副本修改成功)

局限性

  • 無法復制函數、SymbolDate(會轉為字符串)、RegExp等特殊類型;
  • 存在循環引用時會報錯(如 obj.self = obj)。

替代方案:遞歸深復制(處理特殊類型)

function deepClone(obj) {if (obj === null || typeof obj !== "object") return obj;let clone = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {clone[key] = deepClone(obj[key]); // 遞歸復制}}return clone;
}

六、AJAX實戰:省市區聯動案例-動態數據交互實踐

省市區聯動是AJAX的經典應用場景,核心是通過用戶選擇觸發異步請求,動態加載對應級別的數據(如選擇省份后加載對應城市,選擇城市后加載對應區縣),避免前端硬編碼數據,適應數據動態變化需求。

1. 案例核心需求與技術方案

  • 需求:實現三級聯動下拉框(省份→城市→區縣),數據從后端動態獲取,支持數據頻繁更新(如新增城市、調整行政區劃)。
  • 技術棧:HTML(select/option)+ JavaScript(AJAX)+ 后端(如PHP)+ 數據接口(省份/城市/區縣接口)。

2. 前端實現步驟

(1)HTML結構:創建聯動下拉框

使用select元素創建三級下拉框,初始選項為“請選擇”,后續通過JS動態填充數據:

<!-- 省市區聯動容器 -->
<div class="location-select"><select id="province"><option value="">請選擇省份</option></select><select id="city"><option value="">請選擇城市</option></select><select id="district"><option value="">請選擇區縣</option></select>
</div>
(2)JS邏輯:通過AJAX動態加載數據

核心思路:監聽下拉框onchange事件,根據選中值發送AJAX請求,獲取對應數據后渲染到下一級下拉框。

① 加載省份數據(頁面初始化時)
// 頁面加載完成后自動加載省份數據
window.onload = function() {loadProvinces();
};// 加載省份數據
function loadProvinces() {const xhr = new XMLHttpRequest();// 請求省份接口(無參數,返回所有省份)xhr.open("GET", "province.php", true);xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {const provinces = xhr.responseText.split(","); // 后端返回"北京,上海,廣東..."const provinceSelect = document.getElementById("province");// 動態創建option并添加到省份下拉框provinces.forEach(province => {const option = document.createElement("option");option.value = province; // 選項值(如"廣東")option.textContent = province; // 顯示文本provinceSelect.appendChild(option);});}};xhr.send(null);
}
② 加載城市數據(省份選擇變化時)
// 監聽省份下拉框變化
document.getElementById("province").addEventListener("change", function() {const province = this.value; // 獲取選中的省份(如"廣東")if (!province) {// 若未選擇省份,清空城市和區縣document.getElementById("city").innerHTML = '<option value="">請選擇城市</option>';document.getElementById("district").innerHTML = '<option value="">請選擇區縣</option>';return;}loadCities(province); // 加載對應省份的城市
});// 加載城市數據
function loadCities(province) {const xhr = new XMLHttpRequest();xhr.open("POST", "city.php", true);// 設置POST請求頭xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {const cities = xhr.responseText.split(","); // 后端返回"廣州,深圳,珠海..."const citySelect = document.getElementById("city");// 清空原有城市選項(保留默認項)citySelect.innerHTML = '<option value="">請選擇城市</option>';// 動態添加城市選項cities.forEach(city => {const option = document.createElement("option");option.value = city;option.textContent = city;citySelect.appendChild(option);});// 清空區縣(城市變化后區縣需重新加載)document.getElementById("district").innerHTML = '<option value="">請選擇區縣</option>';}};// 發送省份參數xhr.send(`province=${province}`);
}
③ 加載區縣數據(城市選擇變化時)

類似城市加載邏輯,監聽城市下拉框change事件,請求區縣接口并渲染:

// 監聽城市下拉框變化
document.getElementById("city").addEventListener("change", function() {const city = this.value;if (!city) {document.getElementById("district").innerHTML = '<option value="">請選擇區縣</option>';return;}loadDistricts(city); // 加載對應城市的區縣
});// 加載區縣數據(實現類似loadCities)
function loadDistricts(city) {// 省略AJAX請求代碼,邏輯同loadCities// 后端接口district.php接收city參數,返回"天河區,越秀區,海珠區..."
}

3. 后端接口實現(PHP示例)

(1)省份接口(province.php

返回所有省份數據(字符串格式,用逗號分隔):

<?php
// 模擬從數據庫獲取省份數據
$provinces = ["北京", "上海", "廣東", "江蘇", "浙江"];
// 轉為逗號分隔的字符串返回(實際開發推薦JSON)
echo implode(",", $provinces);
?>
(2)城市接口(city.php

根據省份參數返回對應城市:

<?php
// 接收省份參數
$province = $_REQUEST["province"];// 模擬數據庫數據(省份→城市映射)
$cityMap = ["廣東" => ["廣州", "深圳", "珠海", "佛山"],"江蘇" => ["南京", "蘇州", "無錫", "常州"],"北京" => ["北京市"] // 直轄市無地級市,直接返回自身
];// 返回對應城市(默認空數組)
$cities = $cityMap[$province] ?? [];
echo implode(",", $cities);
?>

4. 優化技巧與注意事項

(1)性能優化
  • 清空選項:使用innerHTML直接替換比循環刪除option更高效(如citySelect.innerHTML = '<option value="">請選擇城市</option>')。
  • 代碼復用:封裝通用AJAX函數,減少重復代碼:
// 通用AJAX請求函數
function ajax(url, method, data, successCallback) {const xhr = new XMLHttpRequest();xhr.open(method, url, true);if (method === "POST") {xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");}xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {successCallback(xhr.responseText); // 成功回調處理數據}};xhr.send(data);
}// 調用示例(加載省份)
ajax("province.php", "GET", null, function(response) {// 處理省份數據...
});
(2)用戶體驗優化
  • 加載狀態提示:請求過程中顯示“加載中…”,避免用戶誤以為無響應:
function loadCities(province) {const citySelect = document.getElementById("city");citySelect.innerHTML = '<option value="">加載中...</option>'; // 顯示加載狀態// 后續AJAX請求...
}
  • 數據校驗:過濾無效數據(如后端返回空值時顯示“暫無數據”)。
(3)數據格式升級

文檔中使用“逗號分隔字符串”傳遞數據,實際開發推薦JSON格式(更規范,支持復雜結構):

  • 后端(PHP)返回JSON
// province.php(返回JSON)
$provinces = ["北京", "上海", "廣東"];
header("Content-Type: application/json"); // 聲明JSON類型
echo json_encode($provinces); // 返回["北京","上海","廣東"]
  • 前端解析JSON
// 加載省份(JSON格式處理)
ajax("province.php", "GET", null, function(response) {const provinces = JSON.parse(response); // 解析JSON數組// 后續渲染邏輯不變...
});

七、跨域問題:原理與解決方案

在AJAX請求中,若前端頁面與后端接口的“協議、域名、端口”三者有任一不同,會觸發瀏覽器的**同源策略**限制,導致請求失敗(跨域錯誤)。理解跨域的產生原因及解決方案是前端開發的必備技能。

1. 同源策略與跨域判定

(1)同源策略定義

:::danger
同源策略是瀏覽器的安全機制,要求協議、域名、端口三者完全一致的兩個資源才能交互(如發送AJAX請求、操作DOM)。其目的是防止惡意網站竊取其他網站的敏感數據(如Cookie、LocalStorage)。

:::

(2)跨域判定示例
前端頁面URL后端接口URL是否跨域原因分析
http://localhost:80/index.htmlhttp://localhost:80/api協議(http)、域名(localhost)、端口(80)均相同
http://localhost:80/index.htmlhttps://localhost:80/api協議不同(http vs https)
http://localhost:80/index.htmlhttp://127.0.0.1:80/api域名不同(localhost vs 127.0.0.1)
http://localhost:80/index.htmlhttp://localhost:8080/api端口不同(80 vs 8080)
(3)跨域錯誤表現

瀏覽器控制臺會顯示類似錯誤:

Access to XMLHttpRequest at 'http://api.example.com/data' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

(提示:請求被CORS策略阻止,因目標資源未包含Access-Control-Allow-Origin響應頭)

2. 跨域解決方案詳解

(1)CORS(跨域資源共享):生產環境首選

:::danger
CORS(Cross-Origin Resource Sharing)是W3C標準解決方案,通過后端設置響應頭允許指定源訪問,簡單高效。

:::

① 原理

后端在響應中添加Access-Control-Allow-Origin頭,聲明允許跨域的前端域名,瀏覽器驗證通過后放行請求。

② 實現方式(后端配置)
  • 允許單個域名(如允許http://localhost:8080):
// PHP示例
header("Access-Control-Allow-Origin: http://localhost:8080");
  • 允許多個域名(通過條件判斷):
$allowedOrigins = ["http://localhost:8080", "http://example.com"];
$origin = $_SERVER["HTTP_ORIGIN"] ?? "";
if (in_array($origin, $allowedOrigins)) {header("Access-Control-Allow-Origin: $origin");
}
  • 允許所有域名(不推薦,存在安全風險):
header("Access-Control-Allow-Origin: *"); // 生產環境禁止使用
③ 支持復雜請求(如帶Cookie、自定義頭)

若請求包含Cookie或自定義頭,需額外配置:

// 允許帶Cookie
header("Access-Control-Allow-Credentials: true");
// 允許的請求頭(如Content-Type、Authorization)
header("Access-Control-Allow-Headers: Content-Type, Authorization");
// 允許的請求方法(如GET、POST、PUT)
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
(2)JSONP:兼容舊瀏覽器的GET請求方案

:::danger
JSONP利用<script>標簽不受同源策略限制的特性,通過動態創建腳本標簽實現跨域請求(僅支持GET方法)。

:::

① 原理
  1. 前端定義回調函數(如handleData),用于接收后端數據;
  2. 動態創建<script>標簽,src指向后端接口,并傳入回調函數名(如http://api.example.com/data?callback=handleData);
  3. 后端返回handleData(數據)格式的JS代碼,前端腳本執行時自動調用回調函數。
② 實現示例
  • 前端代碼
// 定義回調函數
function handleData(data) {console.log("跨域數據:", data); // 處理后端返回的數據
}// 動態創建script標簽發起JSONP請求
const script = document.createElement("script");
script.src = "http://api.example.com/data?callback=handleData"; // 傳入回調名
document.body.appendChild(script);
  • 后端代碼(PHP)
$callback = $_GET["callback"]; // 獲取回調函數名("handleData")
$data = ["name" => "小明", "age" => 18]; // 要返回的數據
echo $callback . "(" . json_encode($data) . ")"; // 輸出"handleData({"name":"小明","age":18})"
③ 局限性
  • 僅支持GET請求,無法發送POST/PUT等方法;
  • 存在安全風險(若后端未校驗回調函數名,可能遭受XSS攻擊);
  • 無法捕獲HTTP錯誤狀態碼(如404、500)。
(3)請求代理:開發環境常用方案

:::danger
請求代理通過“同源服務器中轉”實現跨域,適用于前端開發階段(如Vue/React項目)。

:::

① 原理
  1. 前端頁面(http://localhost:8080)向本地代理服務器(同源,無跨域)發送請求;
  2. 代理服務器(如Node.js、Nginx)轉發請求到目標接口(http://api.example.com);
  3. 代理服務器將目標接口的響應返回給前端。
② 實現示例(Vue CLI代理配置)

Vue項目中通過vue.config.js配置代理:

// vue.config.js
module.exports = {devServer: {proxy: {"/api": { // 匹配以/api開頭的請求target: "http://api.example.com", // 目標接口域名changeOrigin: true, // 開啟代理,模擬同源請求pathRewrite: { "^/api": "" } // 移除請求路徑中的/api前綴}}}
};

前端請求時直接調用代理路徑:

// 前端請求(實際會被代理到http://api.example.com/data)
axios.get("/api/data").then(response => { ... });
③ 優勢
  • 無需修改后端代碼,前端可直接對接跨域接口;
  • 支持所有HTTP方法(GET/POST/PUT等),無JSONP的限制;
  • 可在代理層添加額外邏輯(如請求攔截、數據轉換)。
(4)其他方案(了解即可)
  • document.domain:適用于主域名相同、子域名不同的場景(如a.example.comb.example.com),通過設置document.domain = "example.com"實現同源。
  • postMessage:用于兩個不同源的頁面(如iframe與父頁面)之間通信,通過window.postMessage發送消息,window.addEventListener("message")接收。

3. 解決方案選擇建議

場景推薦方案原因分析
生產環境、全方法支持CORS標準方案,支持所有HTTP方法,安全性高
舊瀏覽器、僅GET請求JSONP兼容IE等舊瀏覽器,實現簡單但功能有限
開發環境、前端獨立開發請求代理(如Vue CLI代理)無需后端配合,前端自主解決跨域,適合調試
主域名相同、子域不同document.domain簡單高效,僅適用于特定域名結構

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93772.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93772.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93772.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTB 賽季7靶場 - Enviroment

最近所幸得點小閑&#xff0c;補個檔嘞&#xff01;~nmap掃描 nmap -F -A 10.10.11.67dirsearch掃描發現login接口 http://environment.htb/login構造如下payload&#xff0c;讓程序報錯&#xff0c;其原理在于缺失了rember后會導致報錯&#xff0c;從而告訴我們一個新的參數ke…

源碼編譯部署 LAMP 架構詳細步驟說明

源碼編譯部署 LAMP 架構詳細步驟說明 一、環境準備 1. 關閉防火墻和SELinux [roothrz ~]# systemctl stop firewalld [roothrz ~]# systemctl disable firewalld [roothrz ~]# setenforce 02. 配置YUM網絡源 [roothrz ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo https://m…

機器學習----PCA降維

一、PCA是什么&#xff1f;主成分分析&#xff08;Principal Component Analysis&#xff0c;PCA&#xff09;是機器學習中最常用的降維技術之一&#xff0c;它通過線性變換將高維數據投影到低維空間&#xff0c;同時保留數據的最重要特征。PCA由卡爾皮爾遜于1901年發明&#x…

ReactNative開發實戰——React Native開發環境配置指南

一、開發前準備 1. macOS平臺基礎工具安裝 brew install node18 brew install watchman brew install cocoapods2. 代理配置 npm config set proxy http://127.0.0.1:7890 npm config set https-proxy http://127.0.0.1:7890# 新增擴展建議&#xff08;可選配置&#xff09; ec…

差速轉向機器人研發:創新驅動的未來移動技術探索

在科技日新月異的今天&#xff0c;機器人技術作為智能制造與自動化領域的核心驅動力&#xff0c;正以前所未有的速度發展。其中&#xff0c;差速轉向機器人以其獨特的運動機制和廣泛的應用前景&#xff0c;成為了科研與工業界關注的焦點。本文旨在探討差速轉向機器人研發進展&a…

Wireshark捕獲電腦與路由器通信數據,繪制波形觀察

一、準備工作 電腦發出數據的波形圖繪制在我的另一篇博客有詳細介紹&#xff1a; 根據Wireshark捕獲數據包時間和長度繪制電腦發射信號波形-CSDN博客 路由器發送給電腦數據的波形圖繪制也在我的另一篇博客有詳細介紹&#xff1a; 根據Wireshark捕獲數據包時間和長度繪制路由…

汽車ECU實現數據安全存儲(機密性保護)的一種方案

一、 綜述在車輛ECU中總是有一些密鑰或重要數據需進行機密性保護&#xff0c;但因產品選型、成本等考慮&#xff0c;導致一些ECU的芯片不支持硬件安全模塊&#xff08;例如HSM、TEE等&#xff09;。此時&#xff0c;為保障數據的機密性&#xff0c;可考慮通過軟件實現數據的安全…

AI 效應: GPT-6,“用戶真正想要的是記憶”

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

云計算學習100天-第25天

部署LNMP環境安裝軟件#在前一天已經安裝nginx的基礎上安裝MariaDB&#xff0c;php和php-fpm yum -y install mariadb mariadb-server mariadb-devel php php-mysqlnd php-fpm #mariadb&#xff08;數據庫客戶端軟件&#xff09;、mariadb-server&#xff08;數據庫服務器軟件&…

細化的 Spring Boot 和 Spring Framework 版本對應關系

注:本文由ai輔助,個人整理,有問題可留言 Spring Boot 3.x 系列 (基于 Spring Framework 6.x) Spring Boot 版本 對應的 Spring Framework 版本 Java 支持版本 3.1.5 (最新) 6.0.15 Java 17+ 3.1.4 6.0.14 Java 17+ 3.1.3 6.0.12 Java 17+ 3.1.2 6.0.11 Java 17+ 3.1.1 6.0.…

PyTorch API 1

文章目錄torch張量創建操作索引、切片、連接與變異操作加速器生成器隨機采樣原地隨機采樣準隨機采樣序列化并行計算局部禁用梯度計算數學運算常量逐點運算歸約操作比較運算頻譜操作其他操作BLAS 和 LAPACK 運算遍歷操作實用工具符號數字導出路徑控制流優化方法操作符標簽torch.…

基于FPGA的實時圖像處理系統(2)——VGA顯示彩條和圖片

VGA顯示彩條和圖片 文章目錄VGA顯示彩條和圖片一、VGA簡介二、功能設計1、彩條設計2、圖片設計三、結果展示四、代碼一、VGA簡介 VGA(Video Graphics Array)是IBM在1987年隨PS/2機?起推出的?種視頻&#xff0c;具有分辨率?、顯?速率快、顏?豐富等優點&#xff0c;在彩 ?…

【網絡運維】Linux 文本處理利器:sed 命令

Linux 文本處理利器&#xff1a;sed 命令 sed 簡介 sed&#xff08;Stream Editor&#xff09;是一款非交互式的流編輯器&#xff0c;誕生于 1973–1974 年間的貝爾實驗室&#xff0c;由 McMahon 開發。它專為文本處理而生&#xff0c;功能強大&#xff0c;是 Linux 文本處理常…

week2-[一維數組]出現次數

week2-[一維數組]出現次數 題目描述 給定 NNN 個整數A1,A2,…,ANA_1,A_2,\ldots,A_NA1?,A2?,…,AN?。請求出這 NNN 個數中出現次數最多的數的出現次數&#xff0c;以及出現次數最少的數的出現次數。 輸入格式 讀入包括 222 行。第一行只有 111 個整數 NNN&#xff0c;表示數…

力扣 hot100 Day79

215. 數組中的第K個最大元素 給定整數數組 nums 和整數 k&#xff0c;請返回數組中第 k 個最大的元素。 請注意&#xff0c;你需要找的是數組排序后的第 k 個最大的元素&#xff0c;而不是第 k 個不同的元素。 你必須設計并實現時間復雜度為 O(n) 的算法解決此問題。 class…

C++圍繞音視頻相關的資料都有哪些?如何進行學習

音視頻技術涉及的內容廣泛而深入。我會根據自己的知識給你提供一個系統性的音視頻相關資料梳理&#xff0c;主要分為學習路徑與核心知識、開源項目與實戰、開發者資源以及熱點與趨勢幾個方面&#xff0c;希望能幫助你高效地學習和探索。 先用一個表格來概覽主要的學習方向和資…

AI自動化測試,解決傳統自動化測試中??腳本維護成本高、用例覆蓋不全、缺陷發現滯后??等痛點

AI自動化測試&#xff0c;解決傳統自動化測試中??腳本維護成本高、用例覆蓋不全、缺陷發現滯后??等痛點AI自動化測試通過機器學習&#xff08;ML&#xff09;、自然語言處理&#xff08;NLP&#xff09;、計算機視覺&#xff08;CV&#xff09;等技術&#xff0c;解決了傳統…

Laravel 事件與監聽器

下面是一個完整的用戶注冊事件和監聽器的實現示例&#xff0c;包含事件、監聽器、注冊、觸發等完整流程。一、軟件版本 php: 8.2.20laravel: 11mysql: 8.0.29 二、完整實現過程 1.創建事件 1.1 首先創建用戶注冊事件 php artisan make:event UserRegistered1.2 編輯app/Events/…

前端 React 實現數據懶加載-滾動觸底加載數據

在 React 中使用 Intersection Observer API 實現觸底加載分頁&#xff08;無限滾動&#xff09;1.基本實現思路 在列表底部放置一個 哨兵元素&#xff08;Sentinel&#xff09;&#xff08;如 <div>&#xff09;。使用 IntersectionObserver 監聽該元素是否進入視口&…

MySQL 50 道經典練習題及答案

目錄 一、數據表設計與初始化 1. 數據表結構說明 2. 建表語句 3. 插入測試數據 二、練習題及答案 1. 查詢 "01" 課程比 "02" 課程成績高的學生的信息及課程分數 2. 查詢同時存在 "01" 課程和 "02" 課程的情況 3. 查詢存在 &qu…