去年接手公司一个老项目时,发现核心业务系统仍在使用2018年开发的Vue2管理后台。这套系统原本只部署在Web服务器上,但随着业务部门提出"支持离线操作"、"需要桌面快捷方式"等需求,传统的浏览器访问方式已无法满足实际场景。更棘手的是,部分车间工控机仍在使用Windows 7系统,而现代浏览器对旧系统的支持越来越差。
经过技术评估,我们决定将现有Web系统封装为Windows原生应用。这样不仅能解决浏览器兼容性问题,还能实现:
在Windows平台封装Web应用主要有三种技术路线:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Electron | 生态完善、跨平台 | 打包体积大(>100MB) | 复杂桌面应用 |
| NW.js | 兼容旧版Windows | 文档较少 | 需要XP兼容的项目 |
| WebView2 | 原生集成、体积小(<10MB) | 需预装运行时 | 轻量级封装 |
考虑到车间电脑配置普遍较低(4GB内存+机械硬盘),且部分设备无法连接外网,我们最终选择基于WebView2的方案:
实测数据:在Dell OptiPlex 3040(i5-6500/4GB)上,Electron应用启动需4.3秒,WebView2仅1.8秒
首先确保开发机满足:
bash复制# 检查WebView2是否可用
reg query "HKLM\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"
由于封装后不再有服务器,需将vue-router的history模式改为hash模式:
javascript复制// router/index.js
export default new VueRouter({
mode: 'hash', // 重要修改!
routes: [...]
})
修改vue.config.js确保资源路径正确:
javascript复制module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? './'
: '/',
assetsDir: 'static'
}
创建Win32项目,主要实现以下功能:
cpp复制// 初始化WebView2环境
HRESULT CreateWebViewEnvironmentWithOptions(
PCWSTR browserExecutableFolder,
PCWSTR userDataFolder,
ICoreWebView2EnvironmentOptions* environmentOptions,
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* environment_created_handler);
// 处理窗口消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_SIZE:
if (g_webviewController) {
RECT bounds;
GetClientRect(hwnd, &bounds);
g_webviewController->put_Bounds(bounds);
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
使用Inno Setup制作安装程序时需特别注意:
ini复制[Setup]
AppName=生产管理系统
AppVersion=2.1
DefaultDirName={pf}\ProdManager
OutputDir=.\Output
SetupIconFile=.\assets\icon.ico
[Files]
Source: "dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Source: "WebView2Runtime.exe"; DestDir: "{tmp}"; Check: not IsWebView2Installed
[Run]
Filename: "{tmp}\WebView2Runtime.exe"; Parameters: "/silent /install"; \
StatusMsg: "正在安装WebView2运行时..."; Check: not IsWebView2Installed
通过自定义API实现增量更新:
javascript复制// 在Vue中增加版本检查
setInterval(() => {
axios.get('/api/version').then(res => {
if (res.data.version > currentVersion) {
showUpdateNotification()
}
})
}, 3600000) // 每小时检查一次
在部分Win7设备上出现字体模糊,解决方案:
css复制/* 全局字体设置 */
body {
font-family: "Microsoft YaHei", sans-serif;
-webkit-font-smoothing: antialiased;
}
WebView2的localStorage与浏览器独立,需要处理数据迁移:
javascript复制// 首次启动时迁移数据
if (!localStorage.getItem('legacyDataMigrated')) {
const legacyData = callChromeExtensionAPI()
Object.entries(legacyData).forEach(([key, val]) => {
localStorage.setItem(key, val)
})
localStorage.setItem('legacyDataMigrated', 'true')
}
通过以下手段将冷启动时间从3.2秒降至1.5秒:
javascript复制// webpack.dll.config.js
module.exports = {
entry: {
vendor: ['vue', 'vue-router', 'axios']
},
output: {
path: path.join(__dirname, 'dll'),
filename: '[name].dll.js',
library: '[name]_library'
}
}
最终打包时发现一个有趣现象:虽然WebView2方案初始体积小,但在包含所有polyfill后,与Electron的差距缩小到约20MB。不过内存占用优势依然明显,在8小时连续运行测试中,WebView2方案平均内存稳定在280MB左右,而Electron则波动在450-600MB之间。