当前位置: 首页 > news >正文

Electron 应用中的系统检测方案对比与鸿蒙适配实践

本文面向在鸿蒙(HarmonyOS)场景中移植或运行 Electron 应用的开发者,系统梳理 Electron 中“操作系统类型/平台”检测的五种常用方案,结合本项目的实现细节,给出统一接口、页面展示、预加载容错与主进程兜底策略,并提供适配与安全注意事项。


背景与目标

  • 目标:在 Electron 应用中稳定显示用户设备的操作系统类型与平台信息,并适配鸿蒙容器(例如 ArkWeb/系统 Web 引擎)可能带来的沙箱与权限限制。
  • 痛点:
    • 预加载脚本在部分环境可能无法访问 Node 内置模块(如 os),导致检测失败或页面显示为空。
    • 时序与 CSP(Content Security Policy)可能导致页面在 electronAPI 未就绪时写入“未知”。
    • 跨平台场景下,不同 API 返回值语义各异,需要统一展示与对比。

效果图

鸿蒙PCmac
image-20251110152659778image-20251110152713618

项目结构与关键文件

  • 本文对应目录:ohos_hap/web_engine/src/main/resources/resfile/resources/app
    • 预加载:preload.js
    • 主进程:main.js
    • 页面:index.html

五种检测方式(方案对比)

以下代码在预加载脚本中实现,并通过统一接口暴露给页面。每段代码都附带简述与优缺点,便于按需选择。

方式一:process.platform(附架构)

function getOSMethod1() {const platform = process.platform;      // 运行平台标识const arch = process.arch;              // CPU 架构const osMap = {darwin: 'macOS',win32: 'Windows',linux: 'Linux',freebsd: 'FreeBSD',openbsd: 'OpenBSD',sunos: 'SunOS',aix: 'AIX'};const osName = osMap[platform] || platform;return `${osName} (${arch})`;
}
  • 优点:来源于 Node 运行时,稳定、简单;适合快速判断大平台类别。
  • 缺点:没有版本信息;在容器环境(如鸿蒙)语义可能是“容器所在平台”。

方式二:os.platform()(附版本与架构)

function getOSMethod2() {const platform = os.platform();         // 与 process.platform 一致const arch = os.arch();                 // 架构const release = os.release();           // 系统版本号const osMap = { darwin: 'macOS', win32: 'Windows', linux: 'Linux', freebsd: 'FreeBSD', openbsd: 'OpenBSD', sunos: 'SunOS', aix: 'AIX' };const osName = osMap[platform] || platform;return `${osName} ${release} (${arch})`;
}
  • 优点:信息更完整(版本与架构);定位问题更有帮助。
  • 缺点:依赖 Node 的 os 模块,在沙箱环境可能不可用(后文有容错与兜底)。

方式三:os.type()(原始类型 + 版本 + 主机名)

function getOSMethod3() {const type = os.type();                 // Windows_NT / Darwin / Linuxconst arch = os.arch();const release = os.release();const hostname = os.hostname();return `${type} ${release} (${arch}) @ ${hostname}`;
}
  • 优点:最接近系统原始名称,便于与内核/发行版信息对照。
  • 缺点:同样依赖 Node 的 os 模块。

方式四:navigator.platform(Web 标准 API)

function getOSMethod4() {const platform = navigator.platform || '';let osName = 'Unknown';if (platform.includes('Mac')) osName = 'macOS (via Navigator)';else if (platform.includes('Win')) osName = 'Windows (via Navigator)';else if (platform.includes('Linux')) osName = 'Linux (via Navigator)';else if (platform.includes('iPhone') || platform.includes('iPad')) osName = 'iOS (via Navigator)';return `${osName} - ${platform}`;
}
  • 优点:前端可用,无需 Node;在受限预加载环境下仍可工作。
  • 缺点:浏览器标识可能不严谨(尤其在容器或定制环境)。

方式五:navigator.userAgent(UA 解析)鸿蒙不适用

function getOSMethod5() {const ua = navigator.userAgent || '';if (ua.includes('Mac OS X')) {const match = ua.match(/Mac OS X (\d+[._]\d+[._]\d+)/);return match ? `macOS ${match[1].replace(/_/g, '.')}` : 'macOS (version unknown)';}if (ua.includes('Windows NT')) {const match = ua.match(/Windows NT (\d+\.\d+)/);const map = { '10.0': 'Windows 10/11', '6.3': 'Windows 8.1', '6.2': 'Windows 8', '6.1': 'Windows 7', '6.0': 'Windows Vista', '5.1': 'Windows XP' };return match ? (map[match[1]] || `Windows NT ${match[1]}`) : 'Windows (version unknown)';}if (/Android/i.test(ua)) return 'Android';if (/Linux/i.test(ua)) return 'Linux';if (/iPhone|iPad|iPod/i.test(ua)) return 'iOS';return 'Unknown';
}
  • 优点:可以解析到更细的版本信息(某些平台)。
  • 缺点:UA 格式不稳定,易受浏览器/容器伪装影响;建议仅作辅助。

统一接口与页面展示

预加载统一接口(同步 + 异步兜底)

文件:app/preload.js

// 暴露到渲染进程的统一接口
contextBridge.exposeInMainWorld('electronAPI', {// 同步:优先用预加载直接计算并返回五种结果getPlatformInfo: () => ({method1: getOSMethod1(),method2: getOSMethod2(),method3: getOSMethod3(),method4: getOSMethod4(),method5: getOSMethod5(),raw: {processPlatform: process.platform,processArch: process.arch,osPlatform: os.platform(),osArch: os.arch(),osRelease: os.release(),hostname: os.hostname(),navigatorPlatform: navigator.platform,userAgent: navigator.userAgent}}),// 异步兜底:如预加载受限,走主进程 IPC 获取 Node 能给出的信息fetchPlatformInfo: () => ipcRenderer.invoke('get-platform-info')
});

页面展示逻辑(短轮询 + 兜底)

文件:app/index.html

<script>window.addEventListener('DOMContentLoaded', () => {const setText = (id, v) => {const el = document.getElementById(id);if (el) el.textContent = v ?? '未知';};const tryFill = () => {const api = window.electronAPI;if (api && typeof api.getPlatformInfo === 'function') {const info = api.getPlatformInfo();setText('p-m1', info.method1);setText('p-m2', info.method2);setText('p-m3', info.method3);setText('p-m4', info.method4);setText('p-m5', info.method5);setText('p-process', info.raw.processPlatform);setText('p-os-platform', info.raw.osPlatform);return true;}return false;};if (tryFill()) return;let attempts = 0;const timer = setInterval(() => {if (tryFill() || ++attempts > 30) clearInterval(timer);}, 100);// 超时兜底:向主进程请求平台信息(Node 能提供的部分)setTimeout(async () => {const api = window.electronAPI;if (api && typeof api.fetchPlatformInfo === 'function') {const info = await api.fetchPlatformInfo();setText('p-m1', info.method1);setText('p-m2', info.method2);setText('p-m3', info.method3);setText('p-process', info.raw.processPlatform);setText('p-os-platform', info.raw.osPlatform);}}, 3500);});
</script>

预加载容错与沙箱设置

os 模块安全加载(避免崩溃)

// preload.js 中:优先 node:os,随后 os,最后提供兜底 stub
let os;
try { os = require('node:os'); }
catch (_) {try { os = require('os'); }catch (e) {console.warn('[preload] os module unavailable, falling back to stubs:', e);os = {platform: () => 'Unavailable',arch: () => process.arch || 'Unavailable',release: () => 'Unavailable',type: () => 'Unavailable',hostname: () => 'Unavailable'};}
}

关闭沙箱(确保预加载可用 Node)

文件:app/main.js

webPreferences: {nodeIntegration: false,contextIsolation: true,sandbox: false,           // 明确关闭沙箱preload: path.join(__dirname, 'preload.js')
}

主进程 IPC 兜底

ipcMain.handle('get-platform-info', () => {const method1 = /* 计算 process.platform + arch */;const method2 = /* 计算 os.platform() + release + arch */;const method3 = /* 计算 os.type() + release + arch + hostname */;return {method1, method2, method3,method4: '', method5: '',raw: { processPlatform: process.platform, osPlatform: os.platform() }};
});

鸿蒙平台适配与实践建议

  • 首选策略:以 Node 侧的 process.platform / os.platform() / os.type() 为主,用浏览器侧 navigator.platform / userAgent 作为辅助展示与对比。
  • 容器环境认知:在鸿蒙容器中,Node 返回值代表“容器宿主平台”;如需识别真实设备类型,应结合业务和容器提供的能力(例如 ArkUI/ArkWeb 的扩展接口或系统服务)。
  • 安全与稳定:
    • 保持 nodeIntegration: falsecontextIsolation: true;仅通过 contextBridge 暴露必要接口。
    • 若 CSP 严格,可在页面添加合理的 meta CSP(包含 unsafe-inline 前先权衡风险),或将脚本外置。
  • 体验优化:
    • 页面采用短轮询 + 主进程兜底,避免首次渲染出现“未知”。
    • 可进一步增加“一键复制平台信息/导出 JSON”按钮,便于问题上报与工单沟通。

运行与验证

  1. 进入目录:ohos_hap/web_engine/src/main/resources/resfile/resources/app
  2. 安装依赖(如需要):npm install
  3. 启动应用:npm start
  4. 验证页面:五种方式与原始字段应正常显示;打开 DevTools 可查看日志:
    • [preload] DOMContentLoaded ... info= ...
    • [index] electronAPI available= ... getPlatformInfo()
    • 如预加载受限,还可见 [index] fetchPlatformInfo() 的兜底填充日志。

常见问题(FAQ)

  • 预加载提示 module not found: os:确认 sandbox: false 已设置;预加载中已提供 node:os → os → stub 的容错,页面仍可显示(少量字段可能为 Unavailable),并在 3.5 秒处由主进程补齐。
  • 页面全为短横线:通常是 electronAPI 未就绪或 CSP 阻止脚本执行。检查控制台日志与 CSP 设置;确认 index.html 的短轮询与兜底逻辑已启用。
  • 鸿蒙设备显示与宿主不一致:这是容器语义差异,建议在产品文档中明确“宿主平台 vs 设备平台”的含义,并在需要时结合系统侧接口做二次识别。

结语

通过“同步统一接口 + 页面短轮询 + 主进程 IPC 兜底 + 预加载容错”的组合,Electron 应用在鸿蒙环境中也能稳定展示平台信息。五种检测方式相互补充,既满足工程可用性,也为定位跨平台问题提供足够的细节。

完整代码


- `app/`- `index.html`:本地页面,集中展示平台信息与五种检测方案结果。- `preload.js`:Electron 预加载脚本,暴露统一接口、执行容错与兜底逻辑。- `main.js`:主进程入口,配置窗口、关闭沙箱、提供 IPC 兜底。- `README.md`、`package.json`、`LICENSE`:说明与依赖。

index.html

<!doctype html>
<html lang="zh-CN"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>平台信息 | Electron × 鸿蒙</title><!-- 页面用于单独展示当前运行平台信息。通过 preload 暴露的 electronAPI 读取数据。 --><style>:root {color-scheme: light dark;}body {margin: 0;font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;background: #0f172a;color: #e5e7eb;display: grid;place-items: center;min-height: 100vh;}.card {width: min(720px, 92vw);background: rgba(255,255,255,0.06);backdrop-filter: blur(6px);border-radius: 16px;padding: 24px 28px;box-shadow: 0 10px 30px rgba(0,0,0,0.35);border: 1px solid rgba(255,255,255,0.12);}h1 {margin: 0 0 12px;font-size: 22px;}.desc {margin: 0 0 18px;color: #cbd5e1;font-size: 14px;}.list {display: grid;gap: 10px;}.item {background: rgba(255,255,255,0.08);border: 1px solid rgba(255,255,255,0.14);padding: 12px 14px;border-radius: 10px;}.k {color: #93c5fd;}code {color: #facc15;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;}footer {margin-top: 16px;font-size: 12px;color: #94a3b8;}</style></head><body><main class="card" aria-labelledby="title"><h1 id="title">当前系统平台信息</h1><p class="desc">以下信息来自 Electron 预加载脚本,适配鸿蒙场景。</p><section class="list" aria-describedby="title"><div class="item"><span class="k">方式一 · process.platform</span><code id="p-m1">-</code></div><div class="item"><span class="k">方式二 · os.platform()</span><code id="p-m2">-</code></div><div class="item"><span class="k">方式三 · os.type()</span><code id="p-m3">-</code></div><div class="item"><span class="k">方式四 · navigator.platform</span><code id="p-m4">-</code></div><div class="item"><span class="k">方式五 · navigator.userAgent</span><code id="p-m5">-</code></div><div class="item"><span class="k">原始 · 运行平台(process.platform)</span><code id="p-process">-</code></div><div class="item"><span class="k">原始 · 操作系统平台(os.platform()</span><code id="p-os-platform">-</code></div></section><footer>Electron 页面本地加载;在鸿蒙容器中值以实际设备/容器为准。</footer></main><script>// 读取由 preload 暴露的 getPlatformInfo(含五种方案),在接口就绪后填充到页面window.addEventListener('DOMContentLoaded', () => {const setText = (id, value) => {const el = document.getElementById(id);if (el) el.textContent = value ?? '未知';};const tryFill = () => {try {const api = window.electronAPI;console.log('[index] electronAPI available=', !!api, 'type=', typeof api?.getPlatformInfo);if (api && typeof api.getPlatformInfo === 'function') {const info = api.getPlatformInfo();console.log('[index] getPlatformInfo()', info);setText('p-m1', info?.method1);setText('p-m2', info?.method2);setText('p-m3', info?.method3);setText('p-m4', info?.method4);setText('p-m5', info?.method5);// 兼容旧占位(如果还存在)setText('p-way1', info?.method3);setText('p-way2', info?.method1);setText('p-way3', info?.method5);setText('p-process', info?.raw?.processPlatform);setText('p-os-platform', info?.raw?.osPlatform);return true;}} catch (e) {console.warn('读取平台信息失败:', e);}return false;};// 立即尝试一次;若未就绪,进行短轮询等待预加载接口暴露if (tryFill()) return;let attempts = 0;const timer = setInterval(() => {if (tryFill() || ++attempts > 30) {clearInterval(timer);}}, 100);// 若最终仍未填充,使用异步兜底从主进程拉取setTimeout(async () => {try {const api = window.electronAPI;if (api && typeof api.fetchPlatformInfo === 'function') {const info = await api.fetchPlatformInfo();console.log('[index] fetchPlatformInfo()', info);setText('p-m1', info?.method1);setText('p-m2', info?.method2);setText('p-m3', info?.method3);// m4/m5 主进程不可用,保留原值setText('p-process', info?.raw?.processPlatform);setText('p-os-platform', info?.raw?.osPlatform);}} catch (e) {console.warn('异步兜底获取平台信息失败:', e);}}, 3500);});</script></body></html>

main.js


const { app, BrowserWindow, Tray, nativeImage, Menu, ipcMain } = require('electron');
const path = require('path');
const os = require('os');let mainWindow, tray;function createWindow() {// 创建系统托盘(注意:确保同级目录有electron_white.png图片)const trayIcon = nativeImage.createFromPath(path.join(__dirname, 'electron_white.png'));tray = new Tray(trayIcon);// 创建托盘右键菜单const trayMenu = Menu.buildFromTemplate([{ label: '显示窗口', click: () => {mainWindow.show(); // 显示窗口mainWindow.focus(); // 聚焦窗口} },{ type: 'separator' }, // 分隔线{ label: '退出应用', click: () => {// 退出前清理托盘和窗口tray.destroy();mainWindow.destroy();app.quit();} }]);tray.setContextMenu(trayMenu); // 绑定菜单到托盘// 创建主窗口mainWindow = new BrowserWindow({width: 800,height: 600,title: 'electron桌面版', // 窗口标题// 可选:添加窗口图标(替换为你的图标路径)// icon: path.join(__dirname, 'window_icon.png'),webPreferences: {nodeIntegration: false, // 禁用node集成(安全最佳实践)contextIsolation: true, // 启用上下文隔离(安全最佳实践)sandbox: false, // 明确关闭沙箱,确保预加载可使用 Node 内置模块// 指定预加载脚本:用于在渲染层安全地注入平台信息到页面preload: path.join(__dirname, 'preload.js')}});// 适配macOS窗口按钮mainWindow.setWindowButtonVisibility(true);// 加载本地页面显示平台信息mainWindow.loadFile(path.join(__dirname, 'index.html'));// 打开开发者工具便于观察预加载与页面日志mainWindow.webContents.openDevTools({ mode: 'detach' });// 窗口关闭时最小化到托盘(而非直接退出)mainWindow.on('close', (e) => {// 仅在用户主动退出时才真正关闭(避免托盘菜单"退出"被拦截)if (app.quitting) {mainWindow = null;} else {e.preventDefault(); // 阻止默认关闭行为mainWindow.hide(); // 隐藏窗口到托盘}});// 点击托盘图标切换窗口显示/隐藏tray.on('click', () => {if (mainWindow.isVisible()) {mainWindow.hide();} else {mainWindow.show();mainWindow.focus();}});// 可选:窗口隐藏时在托盘显示提示mainWindow.on('hide', () => {tray.displayBalloon({title: '应用已最小化',content: '点击托盘图标恢复窗口'});});
}// 应用就绪后创建窗口
app.whenReady().then(createWindow);// 处理不同平台的窗口关闭逻辑
app.on('window-all-closed', () => {// macOS:应用通常在所有窗口关闭后仍保持运行if (process.platform !== 'darwin') {app.quit();}
});// macOS:点击dock图标时重新创建窗口
app.on('activate', () => {if (BrowserWindow.getAllWindows().length === 0) {createWindow();} else {mainWindow.show();}
});// 标记应用是否正在退出(用于区分关闭窗口和退出应用)
app.on('before-quit', () => {app.quitting = true;
});// 主进程:提供平台信息的兜底获取(预加载不可用 Node 内置模块时)
ipcMain.handle('get-platform-info', () => {const method1 = (() => {const platform = process.platform;const arch = process.arch;const map = { darwin: 'macOS', win32: 'Windows', linux: 'Linux', freebsd: 'FreeBSD', openbsd: 'OpenBSD', sunos: 'SunOS', aix: 'AIX' };const name = map[platform] || platform;return `${name} (${arch})`;})();const method2 = (() => {const platform = os.platform();const arch = os.arch();const release = os.release();const map = { darwin: 'macOS', win32: 'Windows', linux: 'Linux', freebsd: 'FreeBSD', openbsd: 'OpenBSD', sunos: 'SunOS', aix: 'AIX' };const name = map[platform] || platform;return `${name} ${release} (${arch})`;})();const method3 = (() => `${os.type()} ${os.release()} (${os.arch()}) @ ${os.hostname()}`)();return {method1,method2,method3,method4: '',method5: '',raw: {processPlatform: process.platform,osPlatform: os.platform()}};
});

preload.js

// 预加载脚本:在渲染层安全地暴露必要 API,并注入平台信息到页面
const { contextBridge, ipcRenderer } = require('electron');
// 尝试加载 Node 内置 os 模块;在沙箱或受限环境下可能不可用
let os;
try {// 优先使用 node:os 前缀(新版本 Node 的推荐形式)// eslint-disable-next-line node/no-missing-requireos = require('node:os');
} catch (_) {try {// 退回传统形式// eslint-disable-next-line node/no-missing-requireos = require('os');} catch (e) {// 最终兜底:提供只读的占位实现,避免预加载脚本崩溃console.warn('[preload] os module unavailable, falling back to stubs:', e);os = {platform: () => 'Unavailable',arch: () => process.arch || 'Unavailable',release: () => 'Unavailable',type: () => 'Unavailable',hostname: () => 'Unavailable'};}
}// —— 五种系统检测方法 ——
// 方法一:process.platform(映射为友好名称并带架构)
function getOSMethod1() {const platform = process.platform;const arch = process.arch;const osMap = {darwin: 'macOS',win32: 'Windows',linux: 'Linux',freebsd: 'FreeBSD',openbsd: 'OpenBSD',sunos: 'SunOS',aix: 'AIX'};const osName = osMap[platform] || platform;return `${osName} (${arch})`;
}// 方法二:os.platform()(附版本与架构)
function getOSMethod2() {const platform = os.platform();const arch = os.arch();const release = os.release();const osMap = {darwin: 'macOS',win32: 'Windows',linux: 'Linux',freebsd: 'FreeBSD',openbsd: 'OpenBSD',sunos: 'SunOS',aix: 'AIX'};const osName = osMap[platform] || platform;return `${osName} ${release} (${arch})`;
}// 方法三:os.type()(更接近系统原始名称)
function getOSMethod3() {const type = os.type();const arch = os.arch();const release = os.release();const hostname = os.hostname();return `${type} ${release} (${arch}) @ ${hostname}`;
}// 方法四:navigator.platform(Web 标准 API)
function getOSMethod4() {try {const platform = navigator.platform || '';let osName = 'Unknown';if (platform.includes('Mac')) osName = 'macOS (via Navigator)';else if (platform.includes('Win')) osName = 'Windows (via Navigator)';else if (platform.includes('Linux')) osName = 'Linux (via Navigator)';else if (platform.includes('iPhone') || platform.includes('iPad')) osName = 'iOS (via Navigator)';return `${osName} - ${platform}`;} catch (e) {return 'Unknown';}
}// 方法五:navigator.userAgent(传统 UA 解析)
function getOSMethod5() {try {const ua = navigator.userAgent || '';// macOSif (ua.includes('Mac OS X')) {const match = ua.match(/Mac OS X (\d+[._]\d+[._]\d+)/);if (match) {const version = match[1].replace(/_/g, '.');return `macOS ${version}`;}return 'macOS (version unknown)';}// Windowsif (ua.includes('Windows NT')) {const match = ua.match(/Windows NT (\d+\.\d+)/);if (match) {const versionMap = {'10.0': 'Windows 10/11','6.3': 'Windows 8.1','6.2': 'Windows 8','6.1': 'Windows 7','6.0': 'Windows Vista','5.1': 'Windows XP'};return versionMap[match[1]] || `Windows NT ${match[1]}`;}return 'Windows (version unknown)';}// Linux / Androidif (/Android/i.test(ua)) return 'Android';if (/Linux/i.test(ua)) return 'Linux';// iOSif (/iPhone|iPad|iPod/i.test(ua)) return 'iOS';return 'Unknown';} catch (e) {return 'Unknown';}
}// 暴露安全的 API 给渲染进程
// 重构:以函数形式提供平台信息,避免时序问题导致页面读取为“未知”
contextBridge.exposeInMainWorld('electronAPI', {// 获取早报数据(保留示例)fetchZaobao: () => ipcRenderer.invoke('fetch-zaobao'),// 获取版本信息versions: {chrome: process.versions.chrome,node: process.versions.node,electron: process.versions.electron},// 同步函数返回平台信息对象(含五种检测方式与原始字段)getPlatformInfo: () => ({method1: getOSMethod1(),method2: getOSMethod2(),method3: getOSMethod3(),method4: getOSMethod4(),method5: getOSMethod5(),// 兼容旧字段(如果页面仍使用 p-way1/2/3)way1: os.type(),way2: process.platform,way3: (typeof navigator !== 'undefined' ? navigator.userAgent : ''),raw: {processPlatform: process.platform,processArch: process.arch,osPlatform: os.platform(),osArch: os.arch(),osRelease: os.release(),hostname: os.hostname(),navigatorPlatform: (typeof navigator !== 'undefined' ? navigator.platform : ''),userAgent: (typeof navigator !== 'undefined' ? navigator.userAgent : '')}}),// 异步兜底:通过主进程获取平台信息(避免预加载受限时失败)fetchPlatformInfo: () => ipcRenderer.invoke('get-platform-info')
});// 将平台信息以浮层形式展示在页面左下角
// 说明:由于主窗口当前加载的是远程页面(Electron 文档站),我们通过 preload 在隔离上下文中
// 操作 DOM 注入一个安全的提示框,不修改站点原有脚本,兼容 CSP。
function injectPlatformBanner() {const info = {processPlatform: process.platform,osPlatform: os.platform(),osType: os.type()};const banner = document.createElement('div');banner.id = 'platform-info-banner';banner.setAttribute('role', 'status');banner.setAttribute('aria-live', 'polite');banner.style.position = 'fixed';banner.style.left = '12px';banner.style.bottom = '12px';banner.style.zIndex = '2147483647';banner.style.background = 'rgba(0,0,0,0.65)';banner.style.color = '#fff';banner.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif';banner.style.fontSize = '12px';banner.style.lineHeight = '1.5';banner.style.padding = '10px 12px';banner.style.borderRadius = '8px';banner.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';banner.style.backdropFilter = 'blur(4px)';banner.innerHTML = ['<strong>系统平台信息</strong>',`运行平台(process.platform):<code>${info.processPlatform}</code>`,`操作系统平台(os.platform()):<code>${info.osPlatform}</code>`,`操作系统类型(os.type()):<code>${info.osType}</code>`].join('<br/>');// 防止重复注入if (!document.getElementById('platform-info-banner')) {document.body.appendChild(banner);}
}// 等待文档就绪再注入,避免页面未加载完成无法插入
window.addEventListener('DOMContentLoaded', () => {try {// 优先填充本地页面占位元素(若存在),否则注入远程页面浮层const placeholders = ['p-m1','p-m2','p-m3','p-m4','p-m5','p-process','p-os-platform','p-way1','p-way2','p-way3'];const hasPlaceholders = placeholders.some(id => document.getElementById(id));const info = {method1: getOSMethod1(),method2: getOSMethod2(),method3: getOSMethod3(),method4: getOSMethod4(),method5: getOSMethod5(),raw: {processPlatform: process.platform,osPlatform: os.platform()}};const setText = (id, value) => {const el = document.getElementById(id);if (el) el.textContent = value ?? '未知';};console.log('[preload] DOMContentLoaded. hasPlaceholders=', hasPlaceholders, 'info=', info);if (hasPlaceholders) {// 新版页面占位setText('p-m1', info.method1);setText('p-m2', info.method2);setText('p-m3', info.method3);setText('p-m4', info.method4);setText('p-m5', info.method5);// 兼容旧版占位setText('p-way1', info.method1);setText('p-way2', info.method2);setText('p-way3', info.method3);setText('p-process', info.raw.processPlatform);setText('p-os-platform', info.raw.osPlatform);} else {injectPlatformBanner();}} catch (e) {console.error('[preload] 注入平台信息失败:', e);}
});

package.json

{"name": "my-electron-app","version": "1.1.0","main": "main.js","scripts": {"start": "electron .","test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "","license": "ISC","description": "","devDependencies": {"electron": "^39.1.1"}
}

完毕。所以你学会了吗?

http://www.dtcms.com/a/592906.html

相关文章:

  • Apriel-1.5-15b-Thinker 中间训练新突破
  • 景安网站商城网站建设费用
  • 《MySQL数据库进阶(九):数据库备份与恢复(二)》
  • 进程 端口排查速查手册
  • 《中医基础理论》- 2.哲学基础之藏象学说
  • 【Java 开发日记】我们来说一下 Mybatis 的缓存机制
  • 做直播导航网站好西安网站搭建的公司
  • 简要概括自建网站的优缺点网站的标题怎么做吸引人
  • ⸢ 拾肆-Ⅰ⸥⤳ 实战检验应用实践(上):制定规范 开展演练
  • web网页开发,在线物流管理系统,基于Idea,html,css,jQuery,jsp,java,SSM,mysql
  • 企业级混合存储架构:MySQL + MinIO 混合存储实践
  • Android 底部导航栏 (BottomNavigationView) 制作教程
  • 当“老龄化”撞上“数字化”:金融业的“续命解药”还是“增长新图”?银行保险AI人工智能数字化营销销售培训老师培训讲师
  • ArrayList与LinkedList的比较
  • 魏公村网站建设建设部网站电话
  • MySQL索引篇 -- 从数据页的角度看B+树
  • 使用 Newtonsoft.Json(Json.NET)库将对象导出为格式化的 JSON 文件
  • 林州市网站建设给个网站好人有好报
  • 使用前端框架vue做一个小游戏
  • 【操作系统原理】进程优先级与命令行参数、环境变量详解
  • 【深度学习新浪潮】扩散模型中,VAE潜空间正则化如何为生成带来帮助?
  • 从零学习Node.js框架Koa 【四】Koa 与数据库(MySQL)连接,实现CRUD操作
  • Zotero在代理模式下无法同步问题
  • LeetCode(python)——438.找到字符串中所有字母异位词
  • 解决添加asp.net网站报错请添加 MIME 映射
  • 浙江省工程建设管理协会网站常州小程序开发公司
  • ASP vs ASP.NET vs ASP.NET Core:三代微软 Web 技术核心区别解析
  • 【项目设计】基于正倒排索引的Boost搜索引擎
  • 建网站需要几程序员关键词网站优化平台
  • 深圳网站建设方案书做sns网站需要什么