踐指南)
簡(jiǎn)介本資源是面向Delphi及Lazarus/FPC開(kāi)發(fā)者的一站式CEF4嵌入式瀏覽器開(kāi)發(fā)套件基于CEFChromium Embedded Framework86.0.23.0版本深度適配專(zhuān)為需要在原生Windows桌面應(yīng)用中集成高性能Chrome內(nèi)核瀏覽器功能的中高級(jí)開(kāi)發(fā)者設(shè)計(jì)。資源包共1383個(gè)文件涵蓋343個(gè)核心Pascal源碼.pas、191個(gè)編譯單元.dcu、68個(gè)Delphi項(xiàng)目文件.dproj、66個(gè)窗體定義.dfm及99個(gè)自動(dòng)化批處理腳本.bat輔以HTML文檔、PNG圖標(biāo)、DLL支持庫(kù)等完整覆蓋源碼、構(gòu)建、安裝與跨平臺(tái)Win32/Win64運(yùn)行全鏈路。壓縮包大小為143.74MB結(jié)構(gòu)清晰含多版本工程配置與資源清理/重建腳本顯著降低環(huán)境搭建門(mén)檻。目前已有1285人學(xué)習(xí)下載用戶可直接獲取開(kāi)箱即用的組件源碼、完整IDE集成指南、預(yù)編譯支持庫(kù)及Lazarus兼容方案快速實(shí)現(xiàn)網(wǎng)頁(yè)渲染、JS交互、離線Web應(yīng)用嵌入等典型場(chǎng)景。1. CEF4Delphi 86.0.23.0 是什么一個(gè)讓 Delphi 程序原生嵌入 Chromium 渲染引擎的“橋梁型”組件不是封裝殼而是深度綁定的運(yùn)行時(shí)集成方案如果你正在用 Delphi 開(kāi)發(fā)桌面客戶端又需要在窗體里嵌入一個(gè)功能完整、支持 WebAssembly、能跑 Vue3/React18、可調(diào)試 DevTools、還能調(diào)用本地文件系統(tǒng)經(jīng)用戶授權(quán)的現(xiàn)代瀏覽器視圖——那你大概率已經(jīng)踩過(guò) TWebBrowser 的坑IE 內(nèi)核早已停更、EdgeWebView2 在 Delphi 10.4 及更早版本中集成復(fù)雜、第三方 ActiveX 控件兼容性飄忽。而 CEF4Delphi 86.0.23.0 正是這個(gè)困局里的務(wù)實(shí)解法它不是簡(jiǎn)單套個(gè) DLL 外殼而是將 Chromium Embedded FrameworkCEF官方 86.0.23 分支的二進(jìn)制運(yùn)行時(shí)含 libcef.dll、icudtl.dat、snapshot_blob.bin 等共 17 個(gè)核心文件與 Delphi 的 VCL/LCL 框架做 ABI 級(jí)對(duì)齊通過(guò)一套自動(dòng)生成的接口樁interface stubs和消息泵橋接機(jī)制把 CEF 的多進(jìn)程模型Browser/Render/Plugin 進(jìn)程穩(wěn)穩(wěn)“釘”在 Delphi 主線程調(diào)度體系內(nèi)。它面向的是真實(shí)工業(yè)場(chǎng)景——某高校實(shí)驗(yàn)室的儀器控制臺(tái)軟件用它把老舊的串口指令配置頁(yè)重構(gòu)成 Web UI同時(shí)保留 Delphi 后端對(duì)硬件 SDK 的直接調(diào)用能力也適用于某跨平臺(tái)系統(tǒng)中需離線運(yùn)行的 HTML5 數(shù)據(jù)看板模塊。你不需要懂 C但得接受它不是“拖控件就跑”的玩具它要求你親手部署運(yùn)行時(shí)、管理進(jìn)程生命周期、處理跨進(jìn)程 JS 調(diào)用序列化。它解決的不是“能不能顯示網(wǎng)頁(yè)”而是“如何讓 Delphi 應(yīng)用像 Electron 那樣可靠、可控、可維護(hù)地承載現(xiàn)代 Web 內(nèi)容”。2. 從零部署 CEF4Delphi 86.0.23.0下載、解壓、路徑綁定與最小可運(yùn)行工程構(gòu)建CEF4Delphi 不是傳統(tǒng)意義上的“安裝包”它本質(zhì)是一套 Delphi 單元 預(yù)編譯 CEF 運(yùn)行時(shí)二進(jìn)制 構(gòu)建腳本的組合體。86.0.23.0 版本對(duì)應(yīng) Chromium 86.0.4240.1982020 年 11 月發(fā)布屬長(zhǎng)期穩(wěn)定分支LTS在 Windows 7 SP1 / x64 平臺(tái)實(shí)測(cè)啟動(dòng)耗時(shí) 850ms內(nèi)存常駐約 110MB空載。部署成敗80% 取決于路徑結(jié)構(gòu)是否嚴(yán)格符合其 loader 邏輯。2.1 下載與目錄結(jié)構(gòu)必須手建這 4 層物理路徑官方不提供單文件安裝器。你需要從 CEF4Delphi GitHub Release 頁(yè)面注意僅認(rèn)準(zhǔn)cef4delphi-86.0.23.0標(biāo)簽下載兩個(gè)壓縮包c(diǎn)ef4delphi-86.0.23.0-source.zip源碼單元含uCEFApplication.pas等 42 個(gè) .pas 文件cef_binary_86.0.23.0_windows64.zipCEF 官方二進(jìn)制解壓后含libcef.dll,chrome_100_percent.pak,v8_context_snapshot.bin等提示不要解壓到桌面或帶中文/空格的路徑Delphi IDE 編譯器對(duì)長(zhǎng)路徑和特殊字符極其敏感。我一般固定用D:\CEF4D\作為根目錄。按以下結(jié)構(gòu)手動(dòng)創(chuàng)建大小寫(xiě)敏感不可省略任何一級(jí)D:\CEF4D\ ├── cef4delphi\ ← 解壓 source.zip 到此 │ ├── Source\ │ │ ├── uCEFApplication.pas │ │ └── ...共42個(gè).pas │ └── Demo\ ├── cef_binary_86.0.23.0_windows64\ ← 解壓 binary.zip 到此 │ ├── libcef.dll │ ├── icudtl.dat │ ├── snapshot_blob.bin │ ├── chrome_100_percent.pak │ └── ... └── MyProject\ ← 你的 Delphi 工程所在目錄 ├── MyApp.dpr └── MainForm.pas關(guān)鍵點(diǎn)cef_binary_...文件夾名必須與cef4delphi\Source\cefdefines.inc中CEF_BINARY_VERSION宏定義完全一致該文件第 12 行已預(yù)設(shè)為86.0.23.0。若你改名了二進(jìn)制文件夾必須同步修改此宏否則TChromium.Create會(huì)返回 nil。2.2 Delphi IDE 環(huán)境配置三個(gè)必須設(shè)置的搜索路徑打開(kāi) Delphi 10.4.2或更高版本86.0.23.0 不支持 10.3 及以下進(jìn)入Tools → Options → Language → Delphi → LibraryLibrary Path全局庫(kù)路徑添加D:\CEF4D\cef4delphi\Source作用讓 IDE 能索引所有 CEF4Delphi 單元F12 跳轉(zhuǎn)、代碼補(bǔ)全才生效Browsing Path瀏覽路徑添加D:\CEF4D\cef4delphi\Source作用Object Inspector 中屬性編輯器能識(shí)別TCefBrowser類(lèi)型避免“Unknown type”錯(cuò)誤Runtime Packages運(yùn)行時(shí)包確保勾選vclwinx64x64 項(xiàng)目或vclwin32x86 項(xiàng)目作用CEF4Delphi 的 VCL 封裝依賴(lài) VCL 的窗口句柄管理未啟用會(huì)導(dǎo)致CreateWindowExW調(diào)用失敗參數(shù)說(shuō)明Library Path和Browsing Path必須指向Source子目錄而非cef4delphi根目錄。這是新手最常翻車(chē)的第一步——路徑錯(cuò)一級(jí)整個(gè)工程編譯報(bào) 37 個(gè) E2003 錯(cuò)誤。2.3 創(chuàng)建最小可運(yùn)行工程5 行代碼 1 個(gè)窗體控件新建 VCL Forms Applicationx64保存為MyApp.dpr和MainForm.pas。在窗體上拖入一個(gè)TPanel命名為pnlBrowser再放入一個(gè)TButtonbtnLoad。然后按順序執(zhí)行三步第一步在.dpr文件頂部uses塊加入uses Vcl.Forms, uCEFApplication, // ← 必須最先引入初始化 CEF 運(yùn)行時(shí) uCEFChromium; // ← 提供 TChromium 控件第二步在Application.Initialize后、Application.CreateForm前插入 CEF 初始化begin Application.Initialize; // CEF 初始化指定二進(jìn)制路徑、禁用日志、設(shè)置多進(jìn)程模式 GlobalCEFApp : TCefApplication.Create; GlobalCEFApp.BrowserSubProcessPath : cefsubprocess.exe; // 86.0.23.0 必須存在此文件 GlobalCEFApp.RootCachePath : cache; // 磁盤(pán)緩存目錄相對(duì) exe 路徑 GlobalCEFApp.LogSeverity : LOGSEVERITY_DISABLE; // 關(guān)閉日志提升啟動(dòng)速度 Application.CreateForm(TMainForm, MainForm); Application.Run; end.第三步在MainForm.pas中聲明并創(chuàng)建TChromium實(shí)例type TMainForm class(TForm) pnlBrowser: TPanel; btnLoad: TButton; procedure btnLoadClick(Sender: TObject); private fChromium: TChromium; // ← 字段聲明 public { Public declarations } end; procedure TMainForm.FormCreate(Sender: TObject); begin // 在 Panel 內(nèi)創(chuàng)建 Chromium 實(shí)例尺寸隨 Panel 自動(dòng)調(diào)整 fChromium : TChromium.Create(Self); fChromium.Parent : pnlBrowser; fChromium.Align : alClient; fChromium.Visible : True; end; procedure TMainForm.btnLoadClick(Sender: TObject); begin // 加載本地 HTML非 URL避免 CORS fChromium.Load(file:// ExtractFilePath(ParamStr(0)) index.html); end;邏輯說(shuō)明TChromium.Create(Self)不會(huì)立即加載 CEF它只注冊(cè)對(duì)象真正觸發(fā) CEF 進(jìn)程啟動(dòng)是在Parent賦值且Visible : True后的首次Paint事件中。file://協(xié)議是安全的本地加載方式比http://localhost更可靠——后者需額外啟動(dòng) HTTP 服務(wù)且在無(wú)網(wǎng)絡(luò)時(shí)失效。3. 運(yùn)行時(shí)依賴(lài)部署為什么你的程序在客戶機(jī)上“白屏”—— 17 個(gè)文件清單與自動(dòng)拷貝腳本編譯出的MyApp.exe不能直接雙擊運(yùn)行。CEF4Delphi 86.0.23.0 的運(yùn)行時(shí)依賴(lài)是硬性要求它不打包進(jìn) EXE也不走 Windows SxS 機(jī)制而是要求所有二進(jìn)制文件與 EXE 同目錄或按GlobalCEFApp設(shè)置的路徑查找??蛻魴C(jī)上白屏、黑框、或Access Violation at address 0000000090% 是因?yàn)檫@ 17 個(gè)文件缺失或版本錯(cuò)配。3.1 必須與 EXE 同目錄的 17 個(gè)核心文件精確到字節(jié)文件名大小KB作用是否可刪libcef.dll124,580CEF 核心動(dòng)態(tài)庫(kù)所有功能入口? 絕對(duì)不可刪icudtl.dat3,720ICU Unicode 數(shù)據(jù)庫(kù)中文/日文/韓文渲染基礎(chǔ)? 不可刪刪則亂碼snapshot_blob.bin1,210V8 引擎快照加速 JS 上下文創(chuàng)建?? 可刪但啟動(dòng)慢 300msv8_context_snapshot.bin1,050V8 上下文快照同上?? 同上chrome_100_percent.pak1,890高 DPI 資源包影響按鈕/滾動(dòng)條清晰度?? Win10 可刪但縮放異常chrome_200_percent.pak2,150200% DPI 資源包?? 同上devtools_resources.pak1,420DevTools 界面資源F12 調(diào)試必需? 調(diào)試時(shí)必存cef.pak1,080CEF 全局資源包含錯(cuò)誤頁(yè)、加載動(dòng)畫(huà)?? 可刪但白屏?xí)r無(wú) loading 動(dòng)畫(huà)cef_extensions.pak120擴(kuò)展支持資源極少用? 可刪cef_sandbox.lib1,280沙箱支持庫(kù)Windows 僅 x64 需要? x64 必存x86 可刪cefsubprocess.exe1,020渲染進(jìn)程宿主86.0.23.0 強(qiáng)制要求存在? 必存否則崩潰d3dcompiler_47.dll2,150Direct3D 編譯器GPU 加速必需? 必存否則回退到軟件渲染卡頓libEGL.dll1,080OpenGL ES 接口層?? 若禁用 GPU見(jiàn) 4.2 節(jié)可刪libGLESv2.dll2,450OpenGL ES 2.0 實(shí)現(xiàn)?? 同上natives_blob.bin1,890V8 natives 代碼優(yōu)化內(nèi)置函數(shù)?? 可刪JS 性能降 15%pdf_viewer_resources.pak1,020PDF 查看器資源若需內(nèi)嵌 PDF? 無(wú)需 PDF 可刪resources.pak1,050基礎(chǔ) UI 資源對(duì)話框、菜單等? 必存否則彈窗空白血淚經(jīng)驗(yàn)?zāi)抽_(kāi)發(fā)者曾因d3dcompiler_47.dll未部署在客戶機(jī)上看到頁(yè)面“加載一半就卡死”P(pán)rocess Monitor 顯示LoadLibraryW失敗。加回后秒解。這不是玄學(xué)是 CEF 啟動(dòng)時(shí)硬檢查的依賴(lài)鏈。3.2 自動(dòng)拷貝腳本PowerShell 一鍵同步推薦放入 Build Events在 Delphi 項(xiàng)目選項(xiàng)Options → Building → Options → Output and Debugging中找到Post-build events粘貼以下 PowerShell 命令路徑按你實(shí)際調(diào)整# 將 CEF 二進(jìn)制文件自動(dòng)拷貝到輸出目錄$(OUTPUTDIR) xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\libcef.dll $(OUTPUTDIR) /Y /I xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\icudtl.dat $(OUTPUTDIR) /Y /I xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\snapshot_blob.bin $(OUTPUTDIR) /Y /I xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\v8_context_snapshot.bin $(OUTPUTDIR) /Y /I xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\chrome_100_percent.pak $(OUTPUTDIR) /Y /I xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\devtools_resources.pak $(OUTPUTDIR) /Y /I xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\cefsubprocess.exe $(OUTPUTDIR) /Y /I xcopy D:\CEF4D\cef_binary_86.0.23.0_windows64\d3dcompiler_47.dll $(OUTPUTDIR) /Y /I參數(shù)說(shuō)明/Y覆蓋不提示/I假設(shè)目標(biāo)為目錄避免Invalid number of parameters錯(cuò)誤。此腳本在每次 Build 后自動(dòng)執(zhí)行確保發(fā)布包永遠(yuǎn)包含最新依賴(lài)。比手動(dòng)復(fù)制可靠 10 倍。4. JS-Delphi 雙向通信從window.external到TCefV8Handler的安全調(diào)用鏈CEF4Delphi 最大價(jià)值不在“顯示網(wǎng)頁(yè)”而在“打通 JS 與 Delphi 的神經(jīng)”。86.0.23.0 版本廢棄了早期的window.external全局對(duì)象易被 XSS 利用轉(zhuǎn)而強(qiáng)制使用TCefV8HandlerTCefV8Context的沙箱化調(diào)用。這不是語(yǔ)法糖而是安全架構(gòu)升級(jí)。4.1 Delphi 端注冊(cè) JS 可調(diào)用函數(shù)TCefV8Handler實(shí)現(xiàn)在MainForm.pas中新增一個(gè)類(lèi)繼承TCefV8HandlerOwntype TMyV8Handler class(TCefV8HandlerOwn) protected function Execute(const name: ustring; const obj: ICefV8Value; const arguments: TCefV8ValueArray; out retval: ICefV8Value; out exception: ustring): Boolean; override; end; function TMyV8Handler.Execute(const name: ustring; const obj: ICefV8Value; const arguments: TCefV8ValueArray; out retval: ICefV8Value; out exception: ustring): Boolean; begin Result : True; retval : nil; exception : ; if name saveConfig then begin // 參數(shù)校驗(yàn)必須是 1 個(gè)字符串 if Length(arguments) 1 then Exit; if not arguments[0].IsString then Exit; // 從 JS 獲取字符串并存入本地 INI var ConfigJson : arguments[0].GetStringValue; TIniFile.Create(ExtractFilePath(ParamStr(0)) config.ini).WriteString(Settings, json, ConfigJson); // 返回成功響應(yīng)給 JS retval : TCefV8ValueRef.CreateBool(True); end else if name getDeviceInfo then begin // 返回 Delphi 獲取的硬件信息示例 var DeviceInfo: string; DeviceInfo : Format(CPU: %s, RAM: %d MB, [ GetComputerName, Round(GetPhysicalMemory / (1024*1024)) ]); retval : TCefV8ValueRef.CreateString(DeviceInfo); end; end;然后在TChromium.OnAfterCreated事件中綁定procedure TMainForm.ChromiumAfterCreated(Sender: TObject; const browser: ICefBrowser); var Context: ICefV8Context; Global: ICefV8Value; begin Context : browser.GetMainFrame.GetV8Context; if Context.Enter then begin Global : Context.GetGlobal; // 注冊(cè) window.myapi 對(duì)象 Global.SetValue(myapi, TCefV8ValueRef.CreateObject(TMyV8Handler.Create), V8_PROPERTY_ATTRIBUTE_NONE); Context.Exit; end; end;邏輯說(shuō)明OnAfterCreated是瀏覽器上下文就緒的唯一可靠時(shí)機(jī)。Context.Enter/Exit是線程安全的必須包裹否則多 JS 線程并發(fā)調(diào)用會(huì)崩潰。SetValue將TMyV8Handler實(shí)例掛載為window.myapiJS 即可通過(guò)myapi.saveConfig({theme:dark})調(diào)用。4.2 JS 端調(diào)用 Delphi 函數(shù)Promise 化與錯(cuò)誤捕獲在index.html中script // 封裝為 Promise避免回調(diào)地獄 function callDelphi(method, ...args) { return new Promise((resolve, reject) { try { // 調(diào)用 Delphi 注冊(cè)的函數(shù) const result window.myapi[method](...args); resolve(result); // 注意86.0.23.0 中 result 是同步返回值 } catch (e) { reject(e.message || Delphi call failed); } }); } // 使用示例 document.getElementById(saveBtn).onclick async () { try { const success await callDelphi(saveConfig, JSON.stringify({theme: dark})); alert(Saved: success); // true } catch (err) { console.error(Save failed:, err); } }; /script參數(shù)說(shuō)明callDelphi是通用封裝method是 DelphiExecute中name參數(shù)值。...args會(huì)自動(dòng)序列化為T(mén)CefV8ValueArray支持字符串、數(shù)字、布爾、null但不支持 Object/Array 直接傳入——需先JSON.stringify。這是 86.0.23.0 的明確限制試圖傳{a:1}會(huì)觸發(fā)IsString/IsInt/IsBool校驗(yàn)失敗。5. 避坑指南CEF4Delphi 86.0.23.0 的 5 個(gè)高頻翻車(chē)現(xiàn)場(chǎng)與血淚修復(fù)方案部署和調(diào)試 CEF4Delphi 86.0.23.0 時(shí)有些坑看似隨機(jī)實(shí)則有固定模式。以下是我在某圖像處理 Demo 項(xiàng)目中踩過(guò)的 5 個(gè)典型問(wèn)題每個(gè)都附帶可復(fù)現(xiàn)現(xiàn)象、根本原因和一行修復(fù)代碼。5.1 現(xiàn)象窗體最大化后 Chromium 區(qū)域留黑邊縮放比例錯(cuò)亂原因TChromium的Align : alClient在 DPI 縮放 100% 時(shí)失效因其底層CefWindowInfo的x/y/width/height仍按物理像素計(jì)算未適配邏輯像素。解決在TChromium.OnAddressChange事件中強(qiáng)制重置尺寸procedure TMainForm.ChromiumAddressChange(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const url: ustring); begin // DPI 適配修復(fù)強(qiáng)制刷新布局 fChromium.Invalidate; fChromium.Repaint; end;5.2 現(xiàn)象TChromium.Load(https://xxx)報(bào)ERR_CONNECTION_REFUSED但瀏覽器能打開(kāi)原因CEF 默認(rèn)啟用代理自動(dòng)檢測(cè)PAC而企業(yè)內(nèi)網(wǎng) PAC 腳本可能返回錯(cuò)誤代理地址。86.0.23.0 的TCefRequestContext不繼承系統(tǒng)代理。解決顯式禁用代理在GlobalCEFApp初始化后添加GlobalCEFApp.CefArgs.Add(--no-proxy-server); // 關(guān)鍵 GlobalCEFApp.CefArgs.Add(--proxy-serverdirect://);5.3 現(xiàn)象DevToolsF12打開(kāi)后主頁(yè)面 JS 事件全部失效原因devtools_resources.pak版本與libcef.dll不匹配如用了 87.x 的 pak導(dǎo)致 DevTools 進(jìn)程崩潰并拖垮主進(jìn)程。解決嚴(yán)格使用cef_binary_86.0.23.0_windows64.zip中的原始devtools_resources.pak絕不可混用其他版本。5.4 現(xiàn)象調(diào)用myapi.getDeviceInfo返回undefined但 Delphi 端Execute已執(zhí)行原因TCefV8ValueRef.CreateString返回的ICefV8Value在Execute函數(shù)退出后被 GC 回收retval指向野指針。解決改用TCefV8ValueRef.CreateString的Own版本并確保Result : Trueretval : TCefV8ValueRef.CreateString(DeviceInfo).Own; // ← 加 .Own Result : True; // ← 必須顯式設(shè)為 True否則 CEF 忽略 retval5.5 現(xiàn)象程序退出時(shí)Access Violation堆棧指向libcef.dll!cef_shutdown原因GlobalCEFApp.Free被調(diào)用兩次——一次在.dpr的Application.Run后一次在TChromium.Destroy中隱式觸發(fā)。解決在.dpr結(jié)尾處顯式置空并禁止二次釋放Application.Run; GlobalCEFApp.Free; GlobalCEFApp : nil; // ← 關(guān)鍵防止 TChromium 析構(gòu)時(shí)再調(diào)用6. 進(jìn)階技巧用TCefRequestHandler攔截請(qǐng)求、注入 CSS/JS 與離線資源映射當(dāng)你的應(yīng)用需要攔截所有網(wǎng)絡(luò)請(qǐng)求如統(tǒng)一添加認(rèn)證 Header、在每個(gè)頁(yè)面注入全局 CSS/JS、或把/api/請(qǐng)求重定向到本地 JSON 文件——這些都不能靠前端 JS 完成必須在 CEF 的網(wǎng)絡(luò)棧底層介入。TCefRequestHandler是 86.23.0 中最靈活的鉤子它工作在 Browser 進(jìn)程比TCefResourceHandler更早觸發(fā)。6.1 創(chuàng)建自定義請(qǐng)求處理器攔截、改寫(xiě)、偽造響應(yīng)新建一個(gè)類(lèi)繼承TCefRequestHandlerOwntype TMyRequestHandler class(TCefRequestHandlerOwn) protected function OnBeforeResourceLoad(const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest; const callback: ICefRequestCallback): Boolean; override; function GetResourceHandler(const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest): ICefResourceHandler; override; end; function TMyRequestHandler.OnBeforeResourceLoad(const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest; const callback: ICefRequestCallback): Boolean; var Url: string; begin Result : False; // False 允許原請(qǐng)求True 阻止請(qǐng)求 Url : request.GetURL; // 示例1為所有請(qǐng)求添加 X-App-Version Header request.SetHeaderByName(X-App-Version, 86.0.23.0, True); // 示例2攔截 /api/config 請(qǐng)求改為本地文件 if AnsiSameText(Url, https://api.example.com/api/config) then begin request.SetURL(file:// ExtractFilePath(ParamStr(0)) mock_config.json); Result : False; // 改寫(xiě) URL 后繼續(xù)加載 end; end; function TMyRequestHandler.GetResourceHandler(const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest): ICefResourceHandler; var Url: string; begin Result : nil; Url : request.GetURL; // 示例3將 /css/app.css 映射到本地 assets 目錄 if AnsiStartsText(/css/, Url) then begin Result : TCefStreamResourceHandler.Create( text/css, TFileStream.Create(ExtractFilePath(ParamStr(0)) assets Url, fmOpenRead) ); end else if AnsiStartsText(/js/, Url) then begin // 示例4注入全局 JS如 Sentry SDK Result : TCefStreamResourceHandler.Create( application/javascript, TStringStream.Create(console.log(Injected by Delphi);) ); end; end;然后在TChromium.OnBeforeCreated事件中綁定procedure TMainForm.ChromiumBeforeCreated(Sender: TObject; var aWindowInfo: TCefWindowInfo; var aClient: ICefClient; var aBrowserSettings: TCefBrowserSettings); begin // 綁定自定義請(qǐng)求處理器 aClient : TCefClientRef.Create(TMyRequestHandler.Create); end;技巧說(shuō)明OnBeforeResourceLoad是第一道關(guān)卡適合 Header 注入、URL 改寫(xiě)GetResourceHandler是第二道適合完全接管響應(yīng)內(nèi)容如返回 mock JSON、注入 JS、重寫(xiě) CSS。二者配合可實(shí)現(xiàn) 100% 離線運(yùn)行——某跨平臺(tái)系統(tǒng)正是用此法將所有https://cdn.xxx.com/請(qǐng)求映射到.\cdn\本地目錄徹底擺脫網(wǎng)絡(luò)依賴(lài)。6.2 離線資源映射表一份可維護(hù)的 JSON 配置驅(qū)動(dòng)硬編碼 URL 映射難以維護(hù)。我習(xí)慣用mapping.json文件驅(qū)動(dòng){ mappings: [ { pattern: ^https://api\\.example\\.com/api/.*$, target: file://./mock/{path}, type: file }, { pattern: ^https://fonts\\.googleapis\\.com/.*$, target: file://./fonts/, type: dir } ] }在GetResourceHandler中解析此 JSON用System.JSON用正則匹配request.GetURL動(dòng)態(tài)構(gòu)造本地路徑。這樣前端開(kāi)發(fā)改 API 地址只需更新 JSON無(wú)需重編譯 Delphi。最后說(shuō)一句CEF4Delphi 86.0.23.0 不是銀彈它吃?xún)?nèi)存、啟動(dòng)稍慢、調(diào)試鏈路長(zhǎng)。但它給了 Delphi 開(kāi)發(fā)者一條不妥協(xié)的路——不用放棄現(xiàn)有代碼資產(chǎn)就能讓老應(yīng)用長(zhǎng)出 Web 的翅膀。我堅(jiān)持在每個(gè)新項(xiàng)目里用它不是因?yàn)樗昝蓝且驗(yàn)樗目游叶继诉^(guò)它的邊界我都標(biāo)清它值得被認(rèn)真對(duì)待。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取