ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

HTML转EXE与跨平台打包:Electron/Tauri实战指南

HTML转EXE与跨平台打包:Electron/Tauri实战指南 1. 先澄清一个前提EXE只是Windows的容器别被标题带偏HTML 转 EXE 这件事我前后折腾了差不多五年。最早是给一个内部工具做外壳后来是帮朋友把做好的单页网页套成桌面软件再到最近两年做跨端Windows、Android、iOS 三端一起铺。踩过的坑足够写一本小册子。所以先泼一盆冷水所谓“各平台版本”严格来说只有 Windows 端存在 EXE。iOS 上根本不存在 EXE 这个格式Android 上也不存在。你在搜索引擎里看到“HTML 转 EXE iOS 版”这种说法绝大多数是把“桌面端打包”和“移动端打包”混在一个词条里卖了。真正的情况是同一个 HTML 项目在 Windows 上最终产物是.exe或者.msi安装包在 Android 上是.apk/.aab在 iOS 上是.ipa。三者共用的东西只有一个就是你那堆 HTML、CSS、JavaScript 代码以及内嵌资源。壳工程、构建工具链、运行容器、签名体系完全是三套独立的东西。理解这一点特别重要因为它决定了你的项目该怎么组织目录结构以及后面要不要为三端分别写桥接代码。那为什么大家还是习惯统称“HTML 转 EXE”呢我观察下来有两个原因。一是搜索习惯很多人第一次接触打包就是从 Windows 桌面端开始的EXE 成了“把网页变成软件”的代名词。二是很多打包工具的宣传口径就是这么写的比如当年那批把网址一键打包成桌面程序的工具全都主打“生成 EXE”。但如果你真按这个思路去理解 Android 和 iOS方向一开始就歪了后面会浪费大量时间在错误的技术选型上。这篇文章我打算按平台拆开讲Windows 讲透几条技术路线的取舍和完整实操Android 和 iOS 各讲一套能真正跑起来的最小可用方案最后统一收一节真实踩坑记录。适合的人群很明确手上有现成的 HTML 项目想变成“像软件的东西”的前端、需要给客户交一个离线可运行工具的自由职业者、以及想把内部网页系统装进手机或桌面的运维和产品同学。基础要求不高会写 HTML能看懂命令行就行。1.1 三端安装包格式的真实差异把三端的产物摆在一起对比很多疑惑会立刻消失。Windows 的 EXE 本质是一个 PE 格式的可执行文件系统加载它之后由它自己去启动一个渲染引擎Chromium、WebView2 或者系统自带内核。Android 的 APK 是一个 ZIP 压缩包里面装着 DEX 字节码、资源文件、清单文件安装时由系统的包管理器解压并注册。iOS 的 IPA 同样是个压缩包里面有 Mach-O 格式的可执行文件和资源目录安装必须经过签名校验未经签名的包连装都装不上。这三者的共同点是都包含一个“壳”壳里跑一个 WebView 或者完整的浏览器内核你的 HTML 就跑在里面。区别在于壳的编写语言、构建工具、以及系统对它的约束强度。Windows 几乎没有约束你想怎么写就怎么写Android 有权限模型和后台限制iOS 的约束最严沙箱最死动态执行 JavaScript 以外的东西基本别想。所以同一个功能在 Windows 上随手就写到 iOS 上可能直接做不了这是必须提前想清楚的。1.2 谁适合走这条打包路线不是所有 HTML 项目都值得打包。我个人的判断标准有三条第一是否需要脱离浏览器独立存在比如要发给不懂技术的同事双击就能用第二是否需要访问本地文件、串口、打印机这类浏览器给不了的系统能力第三是否需要离线环境运行网络完全没有或者很差的场景。三条里满足一条以上打包就有价值。反过来如果你的项目本来就是个联网展示页用户有浏览器那你打包出来的东西除了多占几十兆到上百兆硬盘并没有带来实际收益。我见过有团队把官网首页打包成 EXE 发给客户结果客户打开发现就是个浏览器窗口体验还不如直接发链接。这种投入产出比就很低。打包的价值在于“系统集成”和“离线可用”不在于“看起来像软件”。2. Windows端把HTML变成EXE的几条主流路线怎么选Windows 是最容易做、方案最杂的一端。我按体积和上手难度排一下基本是 Electron、Tauri、NW.js、Nativefier、Python 本地 WebView 这五种。它们背后的核心原理其实就两类一类自带完整浏览器内核Electron、NW.js、Nativefier一类复用系统已有的 WebViewTauri、pywebview、C# WebView2。自带内核的好处是渲染行为绝对一致你用什么版本的 Chromium 就是什么版本不用担心客户机器上的系统组件差异。代价是体积Electron 打出来的空壳 EXE 起步就是 60MB 到 80MB加上资源轻松破 150MB。复用系统 WebView 的好处是体积小Tauri 打出来经常只有 3MB 到 10MB缺点是要依赖系统的 WebView2 运行时Windows 10 1803 以后一般都有但企业内网的老机器或者精简版系统上偶尔会遇到缺失。这里有个很多人忽略的细节Windows 上的 WebView2 Runtime 虽然现在装机率很高但它仍然是一个“可以被卸载的组件”。如果你的目标环境是完全不可控的客户电脑自带内核的方案在稳定性上更保险。反过来如果是公司内部统一发的电脑或者你能在安装包里顺带装上运行时那 Tauri 这类方案就非常香。选型的核心其实是在“体积”和“环境确定性”之间做取舍没有一边倒的答案。2.1 Electron最稳但最重什么时候值得忍Electron 的优势在于生态。你要托盘图标、开机自启、全局快捷键、系统通知、自动更新、崩溃上报这些全都有现成的成熟库文档齐全社区问题一搜就有答案。我第一次做桌面壳就是用它一个内部报表系统从零到能交付大概只花了半天。package.json 里配好入口主进程建一个 BrowserWindow加载本地 index.html然后用 electron-builder 出包流程非常顺。它的代价就是体积和内存。一个空壳打包出来 150MB 起步运行时内存占用也不低因为塞了一整个 Chromium 加 Node 运行时。如果你的用户对硬盘空间不敏感或者你确实需要那些系统集成能力这个代价值得付。但如果只是想“把网页装进窗口里”那就有点杀鸡用牛刀了。安全配置是要重点说的。默认模板里有些老教程会教你把nodeIntegration打开这等于让页面里的任意 JavaScript 都能直接调用 Node 的 fs、child_process一旦页面里引入了不受控的第三方脚本风险极大。我现在的固定做法是nodeIntegration: false、contextIsolation: true需要系统能力就通过preload脚本用contextBridge暴露一个白名单接口只放行明确需要的方法。2.2 Tauri体积和性能优先时的首选Tauri 的架构是 Rust 写后端前端用系统 WebView。Windows 上走 WebView2macOS 上走 WKWebViewLinux 上走 WebKitGTK。所以它的产物极小我最近一个项目前端资源加起来 4MB打包出来的 EXE 是 6.8MB装上之后整个目录不到 12MB。启动速度也比 Electron 快一截因为不用初始化一整个 Chromium。它的上手门槛在环境准备。你需要装 Rust 工具链Windows 上还需要 MSVC 的 C 构建工具Visual Studio Build Tools 里勾选“使用 C 的桌面开发”。第一次编译会比较慢因为要拉一堆 crate 编译十几分钟很正常但之后就增量了。如果你的团队里没人碰过 Rust这块要预留一点学习时间好在业务代码基本不用写 Rust改的主要是tauri.conf.json。Tauri 的权限模型比 Electron 严谨得多它有一个 capabilities 概念默认什么系统能力都不给你要显式声明允许前端调用哪些命令。新手最常见的困惑就是“为什么我的 API 调不通”九成是因为没在配置里开权限。刚上手时建议先把默认模板跑通再一点点加能力别一上来就把权限全开。2.3 Nativefier 与 NW.js把网址一键装成桌面程序Nativefier 是典型的“一行命令出 EXE”底层还是 Electron只是把配置都封装好了。npm install -g nativefier然后nativefier https://你的地址 --name 我的工具 --platform windows几分钟就能出一个能双击运行的目录。它特别适合把内部管理后台、在线文档、监控面板这类现成网址快速变成桌面入口省得每次开浏览器找书签。但它有两个明显局限。一是它加载的是远程网址断网就白屏除非你额外处理离线缓存。二是它本质上还是 Electron体积该大还是大而且定制能力受封装层限制想改窗口行为、加原生菜单往往要绕到它生成的源码里手改维护成本不低。我的定位很清楚临时用、内部用、不交付给外部客户就用 Nativefier 图快。要正式交付还是老老实实自己搭 Electron 或者 Tauri 工程。NW.js 是 Electron 之前的老牌方案和 Electron 同源都出自 Chromium 的内核方案。现在新项目基本不推荐了社区活跃度、文档更新都不如 Electron。但如果你接手的是一个历史项目里面用的是 NW.js 的专属 API那就别急着迁移成本可能比收益大。2.4 Python 系方案pywebview、PyInstaller 与 GraalVM如果团队的技术栈是 Python完全可以不碰 Node用 pywebview 起一个窗口加载 HTML再用 PyInstaller 打包成 EXE。代码量极小一个几十行的脚本就能搞定。它的原理是调用系统 WebViewWindows 上走 WebView2 或旧的 MSHTML所以体积比 Electron 小得多--onefile打包出来通常 15MB 到 40MB。PyInstaller 有几个必须注意的点。--noconsole一定要加否则运行时会弹一个黑色命令行窗口交付给客户看着很业余。资源文件要显式用--add-data带进去打包后路径会变代码里必须用sys._MEIPASS去取运行时目录写死相对路径必然找不到文件。另外 PyInstaller 产出的 EXE 被杀毒软件误报是高频事件我遇到过好几次客户装完就被拦这个后面单独讲怎么缓解。GraalVM Native Image 是另一条路把 Java 应用直接编译成本地可执行文件。如果你的项目本来就是 Java 生态比如用 JavaFX 的 WebView 做壳那用 GraalVM 编译出来的是真正的原生二进制启动极快体积也可控。但它对反射、动态代理这类特性支持有限需要写配置文件调起来比较磨人。适合有 Java 底子、且对启动速度有硬要求的场景不适合拿来当通用外包方案。2.5 五条路线的横向对比表方案产物体积是否自带内核上手难度适合场景Electron150MB是低需要系统集成、环境不可控Tauri5-15MB否用 WebView2中需 Rust 环境体积敏感、机器环境可控Nativefier150MB是极低内网网址快速变桌面入口pywebview PyInstaller15-40MB否低Python 团队、轻量工具GraalVM Native Image30-80MB否高Java 生态、启动速度优先提示这张表里的体积是空壳加少量资源的估算值。你的前端资源一旦包含视频、字体、地图瓦片三端都要重新评估体积可以轻松翻好几倍。3. 实操一份HTML项目打包成Windows EXE的完整流程这一节我用 Tauri 走一遍全流程因为它体积优势明显而且步骤足够完整换成 Electron 只是命令和配置文件的差别思路完全一致。假设你手上有一个纯静态的 HTML 项目目录结构是 index.html、style.css、app.js外加一个 assets 文件夹。3.1 环境准备与项目初始化先装 Rust 和 Node。Rust 从官网下载安装器Windows 下它会提示你安装 MSVC 构建工具跟着走就行。Node 建议用 LTS 版本别用太新的实验版某些构建插件会挑版本。装完之后开一个新终端用rustc -V和node -v各自验证一下能输出版本号说明环境通了。然后创建工程。Tauri 官方提供了脚手架npm create tauri-applatest之后按提示选择前端框架。如果你就是纯静态页面选“Vanilla”那个模板最省事它会生成一个最小工程。生成的目录里src放前端资源src-tauri放 Rust 侧配置和代码tauri.conf.json是核心配置文件。最关键的一步是改build.frontendDist指向你的静态资源目录。很多人第一次打包出白屏就是因为这个路径没对它默认指向脚手架的示例页面你把自己的页面拷进去但没改配置程序当然找不到。改完配置把原有的示例文件清掉保持目录干净。3.2 关键配置项与参数说明配置文件里有几个字段我建议你重点看一眼。productName决定最终 EXE 的文件名别写中文某些构建链路对中文路径支持不好容易出玄学问题。identifier是应用唯一标识用反写域名的格式比如com.yourname.toolname它会影响安装注册表和后续自动更新的识别一旦发布就别再改了。窗口尺寸在windows数组里配width、height、resizable、minWidth这几个都要设。我踩过的坑是只设了初始宽高没设最小值用户把窗口拖到极小之后布局直接崩掉因为我的页面是按 1280 宽度设计的。现在的习惯是minWidth设 1000minHeight设 700保证布局底线。图标要提前准备。Windows 用.ico格式里面至少包含 16、32、48、256 四种尺寸。Tauri 提供了npx tauri icon 源图.png命令会自动生成全套。源图建议 1024×1024 的透明背景 PNG边缘留一点内边距因为不同尺寸缩小时边角容易被切掉。不做图标的话打包出来是默认的 Tauri 图标交付时很掉价。{ productName: MyOfflineTool, identifier: com.example.mytool, build: { frontendDist: ../src }, app: { windows: [ { title: 离线工具, width: 1280, height: 800, minWidth: 1000, minHeight: 700, resizable: true } ], security: { csp: default-src self; img-src self data:; style-src self unsafe-inline } }, bundle: { active: true, targets: [nsis], icon: [icons/icon.ico] } }上面这段里的csp字段值得单独说。它是内容安全策略限制页面只能加载哪些来源的资源。默认配置比较宽松我建议收紧到self这样即使页面里不小心混进了外链脚本浏览器也会拒绝执行。代价是你页面里所有 CDN 资源都得下载到本地这本来就是离线打包应该做的事。3.3 构建、单文件与签名开发阶段用npm run tauri dev它会热重载改前端代码即时生效。正式出包用npm run tauri build产物在src-tauri/target/release/bundle下面会有 EXE 和 NSIS 安装包两种。如果你只需要一个免安装的单 EXE改bundle.targets里去掉 nsis 只留可执行文件目标就行。体积优化有几个实用手段。前端资源压缩混淆是基础图片转 WebP字体只保留用到的字符子集。Rust 侧的 release 配置里可以开 LTO 和 strip能再砍掉几兆。实测一个 4MB 前端的项目优化之后单文件能压到 7MB 以内对比 Electron 的一百多兆差距是数量级的。代码签名这块如果你要分发给外部客户强烈建议买一张代码签名证书。没有签名的 EXE 在别人电脑上运行Windows SmartScreen 会弹蓝色警告写着“无法识别的应用”很多用户看到这个直接就不点了。签名之后这个警告会消失安装成功率明显提升。个人开发者可以考虑 OV 类型的证书价格相对能接受。如果只是内部用跳过这步问题不大但要提前跟同事说明可能会看到警告。4. Android端HTML装进APK的两条路Android 这边没有 EXE最终产物是 APK 或者 AAB。核心逻辑和桌面端一样都是壳加 WebView但 Android 有它自己的一套资源加载机制和权限体系踩坑点完全不同。两条主流路线是原生 WebView 壳和 Capacitor。4.1 原生 WebView 壳最少依赖的做法最省依赖的做法是用 Android Studio 新建一个 Empty Views Activity然后在布局里放一个 WebView在 Activity 里加载 assets 目录下的 HTML 文件。整个工程除了 Android SDK 本身不需要额外引入任何库构建快产物小一个简单页面打出来 APK 只有 2MB 到 5MB。关键代码很短。先把 HTML 资源放进app/src/main/assets/www目录然后在 Activity 里做几件事开启 JavaScript、开启 DOM Storage否则 localStorage 不生效、设置 WebViewClient 拦截页面跳转让站内链接留在应用内。这三项不开页面基本没法正常用。WebView webView findViewById(R.id.webview); WebSettings settings webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setAllowFileAccess(true); webView.setWebViewClient(new WebViewClient()); webView.loadUrl(file:///android_asset/www/index.html);这里有个大坑file://协议在 Android 上属于不透明来源页面里发起的 fetch、XHR 请求会被 CORS 直接拦掉读本地 JSON 会失败。老教程会让你去关掉setAllowFileAccessFromFileURLs之类的开关来绕过但那样做是拿安全性换便利Google Play 现在对这类配置查得很严上架审核会卡。正确做法是用官方的 WebViewAssetLoader它给本地资源提供一个虚拟的 https 域名页面里的相对路径请求就变成同源请求了跨域问题自然消失安全性也保住了。多写十几行代码能省掉后面一堆麻烦非常值。WebViewAssetLoader loader new WebViewAssetLoader.Builder() .addPathHandler(/assets/, new WebViewAssetLoader.AssetsPathHandler(this)) .build(); webView.setWebViewClient(new WebViewClientCompat() { Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { return loader.shouldInterceptRequest(request.getUrl()); } }); webView.loadUrl(https://appassets.androidplatform.net/assets/www/index.html);4.2 Capacitor前端同学最顺手的选择如果你本来就熟悉 npm 那一套Capacitor 会舒服很多。它把 Android 工程封装成android子目录你写完前端之后执行npx cap sync它自动把资源拷进原生工程再用 Android Studio 打开android目录编译签名。整个过程不需要你手动维护 assets 目录的拷贝。初始化流程是npm install capacitor/core capacitor/cli然后npx cap init填应用名和包名再npx cap add android。之后每次前端有改动npm run build加上npx cap sync android两条命令搞定。它还提供了一批插件相机、文件、存储、推送都有现成的桥接比自己在 WebView 里手写 JSBridge 靠谱得多。要注意的是包名applicationId一旦定了就不能改改了等于换了一个应用老版本没法覆盖升级。而且包名必须全局唯一不能用com.example.xxx这种默认的Play 商店直接拒。我的习惯是发布前反复确认三遍包名和签名文件这两样弄错补救成本极高。4.3 签名、权限与几个高频坑Android 的 release 包必须签名才能安装。用keytool生成 keystore然后在build.gradle里配置signingConfigs。keystore 文件、密码、别名这三样东西一定要单独备份丢了就再也没法给同一个应用发更新只能换个包名重新上架之前的用户全丢。我见过有人把 keystore 随手放桌面重装系统之后欲哭无泪。网络权限要在AndroidManifest.xml里显式声明不加INTERNET权限页面里任何联网请求都会静默失败而且日志里不一定有明显报错排查起来很费劲。如果是 Android 9 以上走 http 明文请求还需要配networkSecurityConfig允许特定域名否则请求也会被拦。还有一个特别常见的现象网页里上传文件、调用相机、下载文件在浏览器里好好的装进 APK 就失灵。原因是这些行为都需要重写WebChromeClient的onShowFileChooser方法让原生代码去拉起系统选择器再把结果回传给 WebView。不写这段点击上传按钮就是毫无反应。这是移动端壳工程里最容易被忽略的一块建议一开始就把它接上。另外提一句content://这种 URI。从 Android 7.0 开始应用之间共享文件必须走 FileProvider也就会生成content://开头的地址。你在处理下载、分享、拍照回传时一定会遇到它。直接用file://路径去读会抛异常必须通过 ContentResolver 去打开输入流。这个机制很多人第一次碰会懵记住一句话就行跨应用的文件一律用 content URI别用文件路径。5. iOS端没有EXE只有IPAiOS 是最容易让人放弃的一端因为它没有任何“一键打包”的舒适路径。你必须有一台 Mac必须装 Xcode必须处理签名必须在真机上测试。Windows 上想做 iOS 打包基本只能走云构建服务体验会打折扣。5.1 WKWebView 壳工程的最小实现用 Xcode 新建一个 App 工程语言选 Swift。然后把你的 HTML 资源拖进工程目录注意要勾选“Create folder references”这样目录结构会被保留而不是被拍平成散文件。接着在 ViewController 里创建 WKWebView加载 Bundle 里的 index.html。import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let config WKWebViewConfiguration() config.preferences.setValue(true, forKey: allowFileAccessFromFileURLs) webView WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) if let fileURL Bundle.main.url(forResource: index, withExtension: html, subdirectory: www) { webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL.deletingLastPathComponent()) } } }loadFileURL的第二个参数一定要给而且要给到资源目录的根也就是www那一层。如果你只给到具体文件本身页面引用同目录下的 css、js 就会加载失败表现是白屏或者样式全丢。这个参数的含义是“允许读取的目录范围”范围给太小资源就访问不到。iOS 上同样有跨域问题。WKWebView 加载file://时页面的 fetch 请求会被拦。我的应对方式有两种一是把数据直接内联进 JS 文件用变量声明的方式写死彻底避开请求二是在 App 内部起一个轻量本地服务把资源通过 http 暴露出去。前者简单适合数据量小的场景后者灵活但要处理端口占用和生命周期复杂度上来了。5.2 签名、开发者模式与分发路径iOS 的签名是最劝退的环节。免费 Apple ID 就可以签名但证书有效期只有 7 天过期之后应用直接打不开需要重新连电脑刷一遍。付费开发者账号是 99 美元一年签名有效期一年还能用 TestFlight 做内测分发。真机调试前要在手机设置里开启开发者模式路径在隐私与安全性下面。开启之后手机会要求重启重启后还要再确认一次。这个过程我遇到过好几次同事卡住以为是签名失败其实就是开发者模式没开或者开启之后没重启。分发方式主要有三种。TestFlight 适合公开测试最多能加一万名测试员但每个版本都要经过一次审核虽然比正式上架宽松仍然有等待时间。Ad Hoc 适合固定的一批设备需要提前收集设备 UDID 并写进描述文件名额上限一百台。企业内部分发需要专门的企业账号申请门槛高而且对使用范围有严格限制不能拿来做面向公众的分发。如果你只是自己用或者给几个同事用直接连电脑装最省事别折腾分发。5.3 PWA 作为轻量替代方案如果你的诉求只是“在 iPhone 上像 App 一样打开”其实可以考虑 PWA 路线也就是把网页做成可添加到主屏幕的形式。Safari 的分享菜单里有“添加到主屏幕”加完之后会有一个独立图标点开是全屏显示没有浏览器地址栏。它不需要 Xcode不需要签名不需要 Mac成本几乎为零。代价是能力受限。iOS 上的 PWA 对推送通知、后台任务、本地文件系统的支持都比较保守而且清理 Safari 数据时有可能把 PWA 的本地存储一起清掉。它适合“展示型、轻交互”的场景比如产品手册、活动页、内部查询工具。真要做重交互、强离线的应用还是得回到原生壳工程这条路。5.4 三端资源组织的统一约定为了让同一套 HTML 能在三端复用我在项目里定了几条硬规矩。所有资源路径一律用相对路径不用以斜杠开头的绝对路径因为三端的资源根目录不一样。路由一律用 hash 模式不用 history 模式因为file://下 history 路由刷新就 404。所有 CDN 依赖全部下载到本地不放任何外链。字体文件要注意 iOS 的格式偏好优先提供 woff2兜底给 ttf。图片不要用超大分辨率原图三端的内存限制不同iOS 上大图解码占用内存过高会直接触发进程被杀表现是应用突然闪退日志里能看到内存警告。这个坑我在一个含大量高清图的展示项目里踩过最后是把图全部压到适合屏幕尺寸才解决。6. 常见问题与排查技巧实录打包这件事八成的时间不是花在写代码上而是花在排查各种“明明在浏览器里好好的打包就不行”的问题上。我把这些年遇到的高频问题整理成一张速查表遇到症状直接对号入座能省不少时间。6.1 问题速查表症状常见原因排查与解决打包后白屏资源路径错误、入口配置指向示例页打开开发者工具看 404检查 frontendDist 或 assets 路径页面中文乱码HTML 缺 charset 或文件编码非 UTF-8补meta charsetutf-8编辑器另存为 UTF-8 无 BOM打包后体积异常大把 node_modules 或源图一起打进去了检查打包白名单压缩图片开启 release 优化杀软报毒无签名、被打包器的自解压行为触发代码签名减少可疑行为提交误报申诉联网请求全部失败缺网络权限声明或明文请求被拦补 INTERNET 权限配置 networkSecurityConfig点击上传无反应未实现原生文件选择回调重写 onShowFileChooser回传 content URIiOS 启动即闪退大图解码占内存过高压缩图片尺寸降低单屏资源峰值本地 JSON 读取失败file 协议跨域限制改用 WebViewAssetLoader 或内联数据应用启动偶尔失败端口被占用本地服务方案改为动态端口启动时探测可用端口多开导致数据错乱未做单实例限制加单实例锁重复启动时聚焦已有窗口6.2 关于“硬盘里文件夹变成exe”的澄清这个搜索词经常和 HTML 转 EXE 混在一起出现但它们是两码事。正常打包出来的 EXE 是一个独立文件放在它自己的目录里不会影响别的文件夹。如果你发现电脑里的文件夹图标全变成了 EXE双击还打不开原来的内容同时多出一堆同名文件那大概率是遇到了快捷方式类恶意程序它把原文件夹隐藏了再放一个同名的可执行文件冒充。遇到这种情况第一步是打开文件资源管理器的“显示隐藏文件”和“显示文件扩展名”看看原目录是不是还在只是被隐藏了。然后用安全软件做一次全盘扫描重点是 U 盘和移动硬盘。这类问题跟打包技术无关属于终端安全范畴不要试图用打包工具去“修复”它方向完全错了。同样的概念混淆还有bat to exe converter这类工具。它们做的是把批处理脚本编译成 EXE跟 HTML 没有半点关系。搜索的时候容易串到一起但技术栈完全不同别拿批处理的思路去套网页打包。6.3 内容安全策略别等到出问题才补打包之后的应用页面里能加载什么、能执行什么都应该被约束住。我的固定做法是在壳里设置严格的内容安全策略只允许加载本地资源禁止 eval 和内联脚本以外的一切动态执行。这样即使某个第三方组件被替换成了带恶意代码的版本也执行不起来。具体配置就是前面 Windows 那节提到的csp字段Android 和 iOS 也有对应的机制。Android 上可以在 WebView 里通过拦截请求做白名单iOS 上可以在 WKWebView 的导航代理里做同样的判断。多写这几十行能挡掉大量潜在问题尤其是当你的项目会引用外部组件的时候。7. 一些实操心得与选型建议先说选型的判断顺序我自己的习惯是先看目标机器环境是否可控。如果是公司内部统一分发的电脑直接上 Tauri体积小启动快用户口碑最好。如果是要给完全陌生的客户Electron 更保险因为它自带内核不依赖任何系统组件出问题的概率最低。这个顺序不要反先考虑环境确定性再考虑体积很多返工都是因为把顺序搞反了。再说一个特别容易被低估的成本图标和视觉细节。技术上打包只要十分钟但把图标做规范、把启动画面配上、把窗口标题和任务栏名称统一往往要花掉半天。这些细节用户第一眼就能看到做不好会显得整个项目很业余。我现在都是提前把三端的图标尺寸清单列出来一次性生成到位省得来回返工。关于自动更新桌面端和移动端的策略完全不同。桌面端做自动更新比较自由自己起一个静态资源服务器放版本文件就行壳里定期拉取比对版本号有更新就下载替换。移动端要麻烦得多Android 上可以引导用户去下载新 APK但 iOS 上基本只能靠应用商店或者重装没有太多自主空间。所以移动端的功能迭代要更谨慎别指望能像桌面端那样随时热更新。最后聊聊测试。三端各自的行为差异比想象中大尤其是文件读写、网络请求、字体渲染这三块。我的做法是准备一个“三端自检页”里面放几个按钮分别触发读写、请求、显示特殊字符的操作每次打包后先在真机上跑一遍这个页面确认基础能力都在再去测业务功能。这样能把环境问题快速隔离出来不至于业务出问题时在基础层面反复排查。这个习惯帮我省了大量时间尤其是刚接手新项目、对壳工程还不熟的时候。
返回列表