news 2026/8/30 15:32:54

VC++集成Edge WebView2:现代Web技术与传统桌面应用的无缝融合

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VC++集成Edge WebView2:现代Web技术与传统桌面应用的无缝融合

简介:本资源是一套面向Windows桌面应用开发者的VC++ WebView2集成实战方案,专为需要在原生C++界面中嵌入现代Chromium内核浏览器功能的中高级开发者设计,解决传统IE内核WebView兼容性差、性能弱、API陈旧等核心痛点。压缩包共448个文件,涵盖75个头文件(h)、44个C++源码(cpp)、51张界面截图与图标(png)、24个HTML示例页、19个XAML/MD文档及15个动态链接库(dll)等,完整呈现环境初始化、URL导航、JS双向通信、权限控制、DevTools调试及错误处理等关键模块实现。资源大小35.99MB,结构清晰,含多版本工程(sln/vcxproj)、配置文件(json/config)与说明文档(md),便于按需裁剪与快速验证。目前已有280人学习下载,提供可直接编译运行的全链路代码样例与典型场景实践路径,助开发者高效落地安全、高性能、标准兼容的Web嵌入能力。

1. 项目概述:为什么要在VC++界面中集成Edge内核?

在桌面应用开发领域,尤其是使用经典的VC++(Visual C++)进行Win32或MFC项目开发时,我们常常会遇到一个核心需求:如何在传统的C++界面中嵌入一个现代化、高性能且功能丰富的Web渲染引擎。过去,我们可能会选择IE的WebBrowser控件,或者尝试集成开源的CEF(Chromium Embedded Framework)。但随着微软Edge浏览器基于Chromium内核的重生,一个更“原生”、更易维护的选项摆在了我们面前——直接使用Edge WebView2控件。

这个项目的核心,就是解决如何在VC++开发的应用程序窗口中,无缝地嵌入一个Edge Chromium内核的浏览器实例。这不仅仅是显示一个网页那么简单,它意味着你的传统桌面应用立刻获得了现代Web技术的全部能力:支持最新的HTML5、CSS3、JavaScript(包括ES6+),可以流畅运行复杂的Web应用(如Vue3、React项目),并能实现C++原生代码与Web前端脚本之间的双向通信。对于那些需要混合本地性能与Web灵活性的项目,比如软件内嵌帮助文档、实时数据仪表盘、基于Web的配置界面,或者像一些监控软件(如涉及大华摄像头配置界面)需要内嵌Web管理页面的场景,这个技术方案极具吸引力。

我之所以花时间深入研究这个方案,是因为在实际项目中,客户的老旧MFC程序需要增加一个功能模块,该模块的UI交互复杂且迭代频繁。用C++重写UI成本太高,而用Web技术开发则高效灵活。WebView2完美地充当了这座桥梁。接下来,我将从设计思路到避坑细节,完整拆解在VC++中集成并使用Edge WebView2的全过程。

2. 整体方案设计与环境准备

2.1 技术选型:WebView2 vs. 其他方案

在决定使用Edge WebView2之前,我们需要理清市面上几种主流的内嵌浏览器方案,并理解为什么WebView2是当前VC++环境下的优选。

1. 传统方案:WebBrowser控件(IE内核)这是MFC/Win32中最“古老”的内置方案。它的优点是零依赖,开箱即用。但缺点致命:内核老旧,对现代Web标准支持极差(很多Vue3、React应用无法运行),安全性和性能都无法满足当前需求。从热词中“edge兼容模式设置没有‘internet explorer 模式下重新加载’提示”也能看出,微软正在极力引导开发者远离IE模式。对于新项目,这个方案基本可以排除。

2. 开源方案:CEF(Chromium Embedded Framework)CEF功能强大、高度可定制,是许多专业桌面应用(如客户端游戏、开发工具)的选择。它提供了完整的Chromium能力。但其缺点也很明显:体积庞大(动辄上百MB),集成过程相对复杂,需要自行处理分发和更新。对于希望保持应用轻量、或者希望更紧密跟随Windows系统更新的项目来说,CEF的维护成本较高。

3. 微软官方方案:WebView2WebView2是微软推出的现代Web控件,它共享了系统已安装的Edge浏览器(基于Chromium)的核心组件。其核心优势在于:

  • 现代性:基于Chromium,支持最新的Web标准。
  • 轻量集成:应用本身不携带巨大的浏览器内核,依赖系统运行时,安装包小。
  • 易于分发:支持“固定版本”的运行时分发模式,可以确保用户环境一致,避免出现热词中“已安装32位浏览器内核组件”带来的兼容性问题。
  • 原生集成:与Windows系统集成度最高,在通信、安全策略等方面有天然优势。
  • 持续更新:随着Edge浏览器自动更新,WebView2的能力和安全补丁也会同步更新。

注意:选择WebView2意味着你的应用依赖于一个外部运行时。你需要仔细规划运行时分发策略,这是项目初期就必须决定的关键点。

2.2 环境准备与运行时策略

在开始编码前,必须搞定运行时环境。这是第一个容易踩坑的地方。

1. 运行时获取方式WebView2运行时有两种提供方式:

  • 常青版运行时:用户机器上安装的Microsoft Edge浏览器(稳定版、Beta版等)本身就包含了WebView2运行时。如果你的目标用户群体大概率已安装新版Edge,可以依赖这种方式。但你需要处理用户未安装或版本过低的情况。
  • 固定版本运行时:你可以将一个特定版本的WebView2运行时和你的应用一起打包分发。这保证了应用在任何Windows机器上都有完全一致且已知可用的WebView2环境,避免了因Edge自动升级带来的潜在兼容性问题。对于企业级、需要严格环境控制的商用软件,这是推荐的方式。

2. 实操:获取固定版本运行时

  1. 访问 Microsoft WebView2 官方网站 。
  2. 下载“固定版本运行时”的安装程序包。通常是一个.cab文件和一个.exe安装程序。
  3. 规划你的安装流程。通常做法是:在你的应用安装程序中,先检测目标机器是否存在所需版本的WebView2运行时(通过注册表或API查询)。如果不存在,则静默运行你打包好的固定版本运行时安装程序。
    • 避坑提示:静默安装参数通常是/silent /install。务必在你的测试机上反复验证静默安装流程,确保不会弹出用户界面或请求重启。

3. VC++项目配置创建一个新的(或打开现有的)VC++ Win32或MFC项目。

  1. 引入头文件与库:你需要下载WebView2的SDK。最简单的方式是通过Visual Studio的NuGet包管理器。
    • 在项目中右键点击“引用” -> “管理NuGet程序包”。
    • 搜索Microsoft.Web.WebView2,选择稳定版本(如1.0.xxxx.x)进行安装。NuGet会自动为你配置好包含目录和库目录。
  2. 链接库:确保你的项目链接了WebView2Loader.dllWebView2Loader.lib。通过NuGet安装后,这一步通常是自动完成的,但建议在项目属性 -> 链接器 -> 输入中确认一下。

3. 核心实现:在VC++窗口中创建与操控WebView2

3.1 创建WebView2控件实例

这里以Win32 API窗口为例,MFC的CWnd派生类中原理类似,只是窗口句柄的获取方式不同。

#include <WebView2.h> #include <WebView2EnvironmentOptions.h> #pragma comment(lib, "WebView2Loader.lib") // 假设 hWnd 是你的父窗口句柄 HWND hWnd = ...; WCHAR dataPath[MAX_PATH]; // 指定用户数据文件夹路径,用于存储缓存、Cookie等。 // 热词中提到的 `C:\Users\XiaoD\AppData\Local\Microsoft\Edge` 是Edge浏览器自身的数据路径。 // 我们应该为我们的应用指定一个独立路径,避免冲突和数据混乱。 GetCurrentDirectory(MAX_PATH, dataPath); // 示例:使用当前目录下的一个子文件夹 PathAppend(dataPath, L"WebView2Cache"); ICoreWebView2Environment* pEnvironment = nullptr; ICoreWebView2Controller* pController = nullptr; ICoreWebView2* pWebView = nullptr; // 1. 创建WebView2环境 HRESULT hr = CreateCoreWebView2EnvironmentWithOptions( nullptr, // 使用默认的运行时创建方式(先找常青版,没有再找固定版) dataPath, // 用户数据文件夹路径 nullptr, // ICoreWebView2EnvironmentOptions,可用于配置额外的命令行参数等 Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>( [hWnd, &pController, &pWebView](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { if (!SUCCEEDED(result)) { // 环境创建失败,可能是运行时未安装 MessageBox(hWnd, L"WebView2运行时未安装或创建失败。", L"错误", MB_OK); return result; } // 2. 在环境中创建WebView2控件 env->CreateCoreWebView2Controller(hWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>( [&pController, &pWebView, hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { if (SUCCEEDED(result)) { pController = controller; pController->get_CoreWebView2(&pWebView); // 3. 调整WebView2控件大小,使其充满父窗口客户区 RECT bounds; GetClientRect(hWnd, &bounds); pController->put_Bounds(bounds); // 4. 导航到初始页面 pWebView->Navigate(L"https://example.com"); // 或 file:/// 本地路径 } return S_OK; }).Get()); return S_OK; }).Get());

关键点解析

  • 用户数据文件夹dataPath至关重要。它决定了浏览器缓存、Cookie、本地存储数据的位置。务必为你的应用指定一个专属、可写的路径。如果多个实例共用路径或路径不可写,会导致无法创建环境或数据混乱。这也是热词中“edge缓存位置修改”所涉及的用户需求在自家应用中的实现。
  • 异步创建CreateCoreWebView2EnvironmentWithOptions是异步操作。所有后续操作(如创建控制器、导航)都必须在其完成回调中进行。这是与旧版同步API最大的不同,也是新手容易出错的地方。
  • 控件大小:创建控制器后,需要手动调用put_Bounds来设置其显示区域,否则控件可能不可见。

3.2 实现C++与JavaScript双向通信

这是内嵌浏览器能力的灵魂所在。我们既需要让Web页面能调用C++函数(例如,让网页上的一个按钮触发本地文件操作),也需要让C++能调用页面中的JavaScript函数(例如,向页面推送实时数据)。

1. C++注入对象供JavaScript调用(AddHostObjectToScript)

假设我们想在网页中调用一个C++对象,该对象有一个ShowMessage方法。

// 首先,定义一个实现 IDispatch 接口的COM对象(简化示例,实际项目可能需要更复杂的实现) class NativeObject : public IDispatch { public: // ... 省略 IUnknown 和 IDispatch 的标准实现(QueryInterface, AddRef, Release, GetTypeInfoCount等) // 这是关键方法,用于被JavaScript调用 STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override { if (dispIdMember == 1) { // 假设 dispId 1 对应 ShowMessage if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR) { MessageBox(nullptr, pDispParams->rgvarg[0].bstrVal, L"来自网页的消息", MB_OK); return S_OK; } } return DISP_E_MEMBERNOTFOUND; } // 在 GetIDsOfNames 中需要将 "showMessage" 这个名字映射到 dispId = 1 }; // 创建对象并注入到WebView2 NativeObject* pNativeObj = new NativeObject(); VARIANT variant; VariantInit(&variant); variant.vt = VT_DISPATCH; variant.pdispVal = pNativeObj; // 将对象以名称 `nativeHost` 注入到JavaScript的 `window.chrome.webview` 下 hr = pWebView->AddHostObjectToScript(L"nativeHost", &variant); pNativeObj->Release(); // WebView2会持有引用 VariantClear(&variant); // 在网页的JavaScript中,就可以这样调用: // window.chrome.webview.hostObjects.sync.nativeHost.ShowMessage("Hello from JS!");

2. C++调用JavaScript函数(ExecuteScript)

这是更常用的操作,用于从C++端向页面传递数据或触发动作。

// 假设我们要调用页面中的一个全局函数 `updateData(data)` void CallJsUpdateData(const std::wstring& jsonData) { if (pWebView) { std::wstring script = L"if (window.updateData) { updateData(" + jsonData + L"); }"; pWebView->ExecuteScript(script.c_str(), Callback<ICoreWebView2ExecuteScriptCompletedHandler>( [](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT { // 可以在这里处理JavaScript执行后的返回值 if (SUCCEEDED(errorCode)) { // resultObjectAsJson 包含了JS函数的返回值(JSON字符串格式) } return S_OK; }).Get()); } }

3. JavaScript向C++发送消息(add_WebMessageReceived)

这是一种更灵活、更现代的方式,通过postMessage进行通信。

// C++端:注册消息接收事件处理器 EventRegistrationToken token; pWebView->add_WebMessageReceived( Callback<ICoreWebView2WebMessageReceivedEventHandler>( [](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT { wil::unique_cotaskmem_string message; args->TryGetWebMessageAsString(&message); // 处理从网页发来的消息,message.get() 是字符串 // 通常约定为JSON格式,便于解析 MessageBox(nullptr, message.get(), L"收到网页消息", MB_OK); // 可以回复消息 sender->PostWebMessageAsString(L"{\"status\": \"received\"}"); return S_OK; }).Get(), &token); // JavaScript端:发送消息 // window.chrome.webview.postMessage('Hello C++!'); // 或发送JSON对象 window.chrome.webview.postMessage(JSON.stringify({cmd: 'refresh', data: 123}));

实操心得:对于复杂的双向通信,我推荐使用“WebMessage + JSON”作为主要通信协议,而将AddHostObjectToScript用于暴露一些功能固定的、类库式的原生方法。因为postMessage是异步的,更适合事件驱动的模型,且数据格式灵活。处理消息时,一定要做好JSON解析的异常处理,防止网页发送非法数据导致C++端崩溃。

4. 高级功能与性能调优

4.1 处理导航、生命周期与事件

一个健壮的内嵌浏览器需要妥善处理各种事件。

// 1. 导航开始事件:可以在此拦截或取消导航 pWebView->add_NavigationStarting( Callback<ICoreWebView2NavigationStartingEventHandler>( [](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { wil::unique_cotaskmem_string uri; args->get_Uri(&uri); // 例如,禁止导航到某些特定网址 if (wcsstr(uri.get(), L"blocked-site.com")) { args->put_Cancel(true); } return S_OK; }).Get(), &token); // 2. 导航完成事件:可以在此执行一些初始化脚本 pWebView->add_NavigationCompleted( Callback<ICoreWebView2NavigationCompletedEventHandler>( [](ICoreWebView2* webview, ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT { BOOL isSuccess; args->get_IsSuccess(&isSuccess); if (isSuccess) { // 导航成功,注入全局JS或CSS webview->ExecuteScript(L"console.log('Page loaded by WebView2');", nullptr); } else { // 处理导航失败(如网络错误) COREWEBVIEW2_WEB_ERROR_STATUS errorStatus; args->get_WebErrorStatus(&errorStatus); } return S_OK; }).Get(), &token); // 3. 新窗口请求事件:控制点击链接是否在新窗口打开 pWebView->add_NewWindowRequested( Callback<ICoreWebView2NewWindowRequestedEventHandler>( [](ICoreWebView2* sender, ICoreWebView2NewWindowRequestedEventArgs* args) -> HRESULT { // 通常我们选择在当前WebView2中打开,而不是弹出新系统窗口 args->put_Handled(TRUE); wil::unique_cotaskmem_string uri; args->get_Uri(&uri); sender->Navigate(uri.get()); // 在当前视图内导航 return S_OK; }).Get(), &token);

4.2 内存管理与性能优化

内嵌浏览器是内存消耗大户,管理不当容易导致应用内存泄漏或占用过高。

  1. 显式释放资源:在关闭父窗口时,必须按顺序释放WebView2资源。

    void CleanupWebView() { if (pController) { pController->Close(); pController->Release(); pController = nullptr; } // 通常不需要显式释放 pWebView 和 pEnvironment,Controller的Close和Release会处理关联关系。 // 但如果你单独AddRef了,也需要Release。 pWebView = nullptr; }
  2. 智能指针:强烈建议使用类似wil::com_ptr的智能指针来管理COM接口指针,可以极大减少因忘记Release而导致的内存泄漏。

    #include <wil/com.h> wil::com_ptr<ICoreWebView2Controller> m_controller; wil::com_ptr<ICoreWebView2> m_webView; // 使用 m_controller.get() 获取原始指针,其生命周期由 wil::com_ptr 自动管理。
  3. 缓存与磁盘管理:如前所述,设置专用的、合理的用户数据文件夹路径。定期清理或限制其大小,可以避免像热词中“edge占用内存过高”的问题蔓延到你的应用中。WebView2本身提供了一些清理缓存的API(如ClearBrowserCache),可以在应用启动或退出时酌情调用。

  4. 禁用非必要功能:如果内嵌的网页不需要某些浏览器功能(如密码保存、自动播放、定位等),可以通过环境选项ICoreWebView2EnvironmentOptions或控制器设置来禁用它们,这有助于提升安全性和轻微的性能。

5. 实战疑难杂症与排查技巧

在实际集成过程中,你几乎一定会遇到下面这些问题。这里记录了我的排查实录。

5.1 常见问题速查表

问题现象可能原因排查步骤与解决方案
环境创建失败(CreateCoreWebView2EnvironmentWithOptions返回失败)1. WebView2运行时未安装。
2. 用户数据文件夹路径无写入权限或路径非法。
3. 系统组件缺失(如VC++运行库)。
1. 检查HRESULT错误码。如果是HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),说明运行时缺失。引导用户安装或打包分发固定版本运行时。
2. 检查dataPath,确保是绝对路径且应用有写入权限。可尝试使用GetTempPath获取临时目录作为测试。
3. 安装最新的VC++可再发行组件包(对应热词“vc++运行库一键修复器”的需求)。
WebView2控件显示为空白1. 未正确设置控件的Bounds(大小和位置)。
2. 父窗口尚未显示或已被销毁。
3. 导航的URL无法访问(如本地文件路径错误)。
1. 在CreateCoreWebView2Controller的成功回调中,立即调用put_Bounds,并传入正确的RECT。
2. 确保在父窗口的WM_SIZE消息中,也调用put_Bounds来响应窗口大小变化。
3. 检查导航URL。本地文件使用file:///协议,注意路径中的斜杠和编码。
JavaScript与C++通信失败1. 注入对象或注册事件处理器的时机不对(如在导航完成前)。
2. JavaScript对象名或函数名拼写错误。
3. 跨域安全策略限制(仅针对网络请求)。
1. 将通信初始化代码(如AddHostObjectToScript,add_WebMessageReceived)放在NavigationCompleted成功事件之后执行,确保页面DOM已就绪。
2. 使用浏览器开发者工具(F12)检查控制台是否有脚本错误。可以通过pWebView->OpenDevToolsWindow()打开。
3. 对于本地文件(file协议),通信通常无障碍。对于网络资源,需注意CORS。
应用崩溃或内存泄漏1. COM接口未正确释放(Release)。
2. 事件回调中使用了无效的指针或捕获了已释放的对象。
3. 在非UI线程中直接调用了WebView2的接口(大部分接口要求在创建它的UI线程上调用)。
1. 使用wil::com_ptr等RAII智能指针。
2. 在事件回调中,使用弱引用或检查对象存活状态。对于类成员,注意生命周期管理。
3. 如果需要从其他线程操作WebView2,必须通过PostMessage或类似机制将任务派发到UI线程执行。
网页内容显示异常(布局错乱、JS不执行)1. WebView2的文档模式或UserAgent被意外修改。
2. 网页代码存在兼容性问题(虽然基于Chromium,但和完整Chrome/Edge仍有细微差异)。
3. 缓存了错误的旧版页面。
1. 避免手动设置过时的文档模式。可通过ICoreWebView2Settings调整设置,但一般保持默认即可。
2. 在Edge浏览器中直接打开该网页,使用F12开发者工具模拟相同的UserAgent和视图端口进行对比测试。
3. 尝试调用pWebView->CallDevToolsProtocolMethod执行Network.clearBrowserCache命令,或直接删除用户数据文件夹下的Cache目录。

5.2 独家避坑技巧

  1. 路径分隔符陷阱:在C++字符串中指定本地文件路径给Navigate时,要使用file:///协议,并且路径中的反斜杠\需要转换为正斜杠/或进行URL编码。例如:file:///C:/MyApp/assets/index.html。直接使用C:\MyApp\assets\index.html会导致导航失败。

  2. DPI感知与高分辨率适配:如果你的应用不是DPI感知的,在高分辨率屏幕上,WebView2内部渲染的内容可能会模糊。确保你的EXE清单文件声明了DPI感知,或者在创建环境时,通过ICoreWebView2EnvironmentOptions设置合适的DPI。

  3. 处理“白屏”与“挂起”:当应用进入后台(如最小化)一段时间后,系统可能会为了省电而挂起WebView2的渲染进程,恢复时可能出现短暂白屏。可以通过监听应用的生命周期事件,在进入后台前主动调用pController->put_IsVisible(FALSE),恢复时再设为TRUE,来优化用户体验。

  4. 利用开发者工具进行调试:这是最重要的调试手段。除了用OpenDevToolsWindow,你还可以通过命令行参数在创建环境时启用远程调试。在环境选项ICoreWebView2EnvironmentOptions中设置AdditionalBrowserArguments--remote-debugging-port=9222。然后你可以在本地的Chrome/Edge浏览器中访问http://localhost:9222来附加调试你的内嵌WebView2实例,功能非常强大。

将Edge浏览器内核嵌入VC++界面,本质上是为传统的桌面应用打开了一扇通往现代Web生态的大门。它平衡了原生应用的性能、系统集成能力和Web开发的效率、跨平台UI表现力。整个集成过程,从环境部署、控件创建到双向通信,虽然步骤清晰,但细节决定成败,尤其是异步编程模型、COM对象生命周期管理和运行时分发策略这几块,需要开发者格外留心。

我个人在多个工业控制和数据可视化项目中采用了此方案,替代了原先的IE控件或第三方库,客户对界面流畅度和功能丰富性的反馈提升非常明显。最大的体会是:前期花一天时间彻底搞定环境部署和通信框架的封装,后期能节省无数天在琐碎的兼容性和调试问题上。对于仍在维护大型VC++遗产代码,又亟需引入现代前端技术的团队来说,WebView2是目前最平滑、最可持续的技术升级路径之一。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 15:29:51

传统搜索获客失效,拓氪科技如何用AI重构获客逻辑?

企业获客行业正迎来一场底层逻辑的颠覆性变革&#xff0c;其变革体量堪比传统线下会销全面转向搜索引擎竞价的时代级迭代。十余年前&#xff0c;国内企业完成了首轮获客数字化迁徙&#xff0c;将核心营销预算从线下户外广告、线下展会地推&#xff0c;转移至搜索引擎关键词竞价…

作者头像 李华
网站建设 2026/8/30 15:29:21

电子维修排查全攻略:从工具准备到实战案例与速查表

1. 排查前的基本功&#xff1a;工具和心态都别凑合 做电子维修这一行&#xff0c;最忌讳一上来就拿烙铁乱戳。拿到一块故障板子&#xff0c;先别急着通电&#xff0c;先把该准备的工具准备好&#xff0c;把该建立的排查思路建立起来。很多新手修不好板子&#xff0c;不是技术不…

作者头像 李华
网站建设 2026/8/30 15:27:28

学习Markdown系列 -- 在 Markdown 中使用 HTML

这听起来有点奇怪,不是吗?学习 Markdown 的全部意义就在于你不需要使用 HTML。问题在于,Markdown 不允许你做许多可以用 HTML 做的事情。诚然,你大多数时候并不需要做这些事情。甚至可能永远不需要。 不过,我发现有三件事我需要在 HTML 中做,而 Markdown 不支持: 在新浏…

作者头像 李华
网站建设 2026/8/30 15:25:16

AI平台腐烂:开发者如何避免被大模型API锁定?

在 AI 热潮中&#xff0c;开发者们争先恐后地接入大模型 API、构建 Agent 应用、搭配合适的提示词链。但很少有人停下来问一个更深层的问题&#xff1a; 当 AI 产业本身正在变成一座新的围墙花园时&#xff0c;开发者辛辛苦苦构建的应用&#xff0c;究竟是在积累自己的资产&am…

作者头像 李华
网站建设 2026/8/30 15:21:15

滴滴测试开发校招笔试全解析:考点梳理与备考攻略

一聊到滴滴出行这类大厂的测试开发校招笔试&#xff0c;很多人第一反应是“是不是又要刷一堆算法题”。以我自己这些年的观察来看&#xff0c;算法确实是绕不开的一道坎&#xff0c;但2018年这份测试开发工程师&#xff08;第一批&#xff09;的网申笔试&#xff0c;给我的整体…

作者头像 李华