For AI agents: the complete documentation index is available at /next/zh/llms.txt, the full documentation bundle is available at /next/zh/llms-full.txt, and this page is available as Markdown at /next/zh/lynxtron/learn/Migrating-From-Electron.md.
  • 简体中文
  • 从 Electron 迁移到 Lynxtron

    如果你已经有一个 Electron 应用,可以把迁移到 Lynxtron 理解为:尽量保留主进程和桌面端能力,把 Chromium Renderer 中的 Web UI 替换为 Lynx UI。

    Lynxtron 的主进程 API 基本沿用 Electron,LynxWindow、Menu、Tray、Notification、dialog、shell、screen 等桌面端 API 的使用方式接近 Electron。真正需要重新设计的是 UI 层:Lynxtron 不加载 HTML 页面,也没有 DOM 渲染层;窗口内容由 Lynx bundle 驱动,使用 Lynx 元素、样式和运行时 DSL 渲染。

    迁移前先划分边界

    迁移时建议先按代码所在层级拆分,而不是直接逐文件替换:

    Electron 应用中的部分迁移到 Lynxtron 的处理方式
    主进程生命周期、菜单、托盘、通知、对话框、shell、screen 等通常可以保留大部分代码,改用 Lynxtron 导出的 app、Menu、Tray、Notification、dialog、shell、screen 等同名或相近 API。
    BrowserWindow 创建和窗口选项替换为 LynxWindow。width、height、frame、transparent、titleBarStyle 等窗口选项基本一致。
    loadFile('index.html') / loadURL() 加载 Web 页面改为加载 Lynx bundle,例如 win.loadFile(bundlePath)。
    Renderer 中的 HTML、DOM、Web CSS、Web 组件库需要重写为 Lynx UI。可以使用 ReactLynx、VueLynx 等 Lynx 运行时 DSL,最终渲染为 <view>、<text>、<image> 等 Lynx 元素。
    ipcRenderer / ipcMain按通信方向迁移为 LynxWindow.sendGlobalEvent()、NativeModules.bridge.call / NativeModules.bridge.send,并在主进程侧监听 -lynx-invoke / -lynx-message 事件。
    webPreferences.preload / contextBridge改为 lynxPreference.preload + contextBridge.exposeInLynxBTS() + NativeModules.nodejs.exposed,通过 preload scripts 暴露 Node.js 能力。
    Electron ABI 下的 Node.js 原生模块需要使用 @lynx-js/lynxtron-rebuild 重新构建,见 Node.js 原生模块。
    electron-builder 打包配置改用 @lynx-js/lynxtron-builder 作为打包入口,通常可以继续使用 electron-builder.yml,但需要确认入口目录、资源列表和 Lynx bundle 产物已经指向 Lynxtron 应用。
    <webview>、BrowserView、WebContentsView 等嵌入式 Web 内容需要按场景迁移。若迁移后仍需在 Lynx UI 中嵌入 Web 内容,可以参考浏览器教程;若迁移后需要接入系统控件或复杂原生视图,可以参考 Lynx 原生能力库。

    API 对照速查

    迁移目标ElectronLynxtron
    创建窗口BrowserWindowLynxWindow
    窗口构造选项new BrowserWindow({ width, height, frame, transparent })new LynxWindow({ width, height, frame, transparent })
    加载 UIwin.loadFile('index.html')win.loadFile(bundlePath)
    配置 Preload ScriptswebPreferences.preloadlynxPreference.preload
    暴露 Node.js 能力contextBridge.exposeInMainWorld()contextBridge.exposeInLynxBTS()
    UI 访问暴露对象window.<api>NativeModules.nodejs.exposed
    主进程向 UI 推送事件webContents.send()LynxWindow.sendGlobalEvent()
    UI 调用主进程并等待返回ipcRenderer.invoke() + ipcMain.handle()NativeModules.bridge.call → -lynx-invoke 事件
    UI 向主进程发送单向消息ipcRenderer.send() + ipcMain.on()NativeModules.bridge.send → -lynx-message 事件
    原生模块重建@electron/rebuild@lynx-js/lynxtron-rebuild
    打包应用electron-builder@lynx-js/lynxtron-builder

    迁移主进程窗口创建

    Electron 中常见的窗口创建代码:

    import { app, BrowserWindow } from 'electron';
    import path from 'path';
    
    app.whenReady().then(() => {
      const win = new BrowserWindow({
        width: 960,
        height: 640,
        frame: false,
        webPreferences: {
          preload: path.join(__dirname, 'preload.js'),
        },
      });
    
      win.loadFile('index.html');
    });

    迁移到 Lynxtron 后,主进程仍然运行在 Node.js 中,但窗口类型和 UI 加载目标要替换:

    import { app, LynxWindow } from '@lynx-js/lynxtron';
    import path from 'path';
    
    app.whenReady().then(() => {
      const win = new LynxWindow({
        width: 960,
        height: 640,
        frame: false,
        lynxPreference: {
          preload: path.join(__dirname, 'preload.js'),
        },
      });
    
      const bundlePath = path.join(__dirname, 'main.lynx.bundle');
      win.loadFile(bundlePath);
    });

    示例假定构建后的主进程文件与 main.lynx.bundle 位于同一目录,请按实际构建产物布局调整路径。

    窗口形态选项可以优先按原 Electron 配置迁移,例如 width、height、frame、transparent、titleBarStyle、trafficLightPosition。无边框、透明窗口和自定义标题栏的 Lynx UI 写法见构建自定义窗口。

    重写 Renderer UI

    Lynxtron 不把 Electron Renderer 里的 HTML 页面直接运行起来。迁移 UI 时,需要把 DOM 结构、Web CSS 和浏览器事件模型改写为 Lynx UI。

    Web / Electron RendererLynxtron UI
    <div> / <span><view> / <text>
    <img><image>
    Web CSSLynx 样式
    -webkit-app-region-x-app-region
    自定义标题栏 DOM<title-bar-view> 或使用 -x-app-region: drag 的 Lynx 元素

    事件也需要从 React DOM 事件改成 Lynx 元素事件。例如,一个 Electron React 按钮:

    export function Toolbar() {
      return <button onClick={openFile}>Open</button>;
    }

    迁移到 ReactLynx 后:

    export function Toolbar() {
      return (
        <view className="button" bindtap={openFile}>
          <text>Open</text>
        </view>
      );
    }

    如果你的 Electron 应用大量依赖 DOM API、浏览器布局、Canvas、WebGL 或 Web 组件库,建议把 UI 迁移拆成独立阶段:先保留主进程服务和通信协议,再逐个窗口用 Lynx UI 重写。

    渐进式迁移:先把现有 Web UI 放进 <webview>

    如果一次性重写全部 Renderer UI 成本太高,可以先用 Lynx UI 搭出窗口外壳,再把暂时未迁移的 Web UI 放进 <webview>。这样可以先完成主进程、窗口创建、打包、preload scripts 和 IPC 的迁移,同时保留部分现有 Web 页面继续运行。

    export function LegacyPanel() {
      return (
        <view className="legacy-panel">
          <webview
            className="legacy-webview"
            src="http://127.0.0.1:3000/legacy.html"
            use-osr={true}
            enable-debug={true}
          />
        </view>
      );
    }

    这种方式适合作为过渡方案:

    • 先用 Lynx 实现窗口框架、标题栏、导航、全局状态和新的页面入口。
    • 暂时把复杂度高、依赖 DOM 或 Web 组件库的旧页面放入 <webview>。
    • 每迁移完一个页面,就用 Lynx 元素替换对应的 <webview> 内容。
    • 新迁移的主进程能力尽量收口到 Lynxtron 的 bridge 或 preload scripts,避免继续扩大旧 Web UI 对 Electron Renderer API 的依赖。

    需要注意的是,<webview> 中的内容仍然是 Web 内容,不会自动变成 Lynx UI。它适合降低迁移风险和拆分迁移节奏,但最终仍建议把核心桌面 UI 逐步改写为 Lynx 元素。

    迁移 Preload Scripts 和 Node.js 能力暴露

    Electron 中常见做法是在 preload script 中用 contextBridge.exposeInMainWorld() 暴露能力:

    // preload.ts
    import { contextBridge, ipcRenderer } from 'electron';
    
    contextBridge.exposeInMainWorld('desktop', {
      readConfig: () => ipcRenderer.invoke('read-config'),
    });

    Renderer 侧通过 window.desktop 调用:

    const config = await window.desktop.readConfig();

    在 Lynxtron 中,preload script 仍然可以使用 Node.js 能力,但暴露目标变为 Lynx BTS 的 NativeModules.nodejs.exposed:

    // preload.ts
    import { contextBridge } from '@lynx-js/lynxtron/context-bridge';
    import fs from 'fs';
    
    contextBridge.exposeInLynxBTS({
      desktop: {
        readConfig: () => {
          return fs.promises.readFile('/path/to/config.json', 'utf8');
        },
      },
    });

    Lynx UI 侧通过 NativeModules.nodejs.exposed 调用:

    const { desktop } = NativeModules.nodejs.exposed;
    const config = await desktop.readConfig();

    Lynxtron 的 preload scripts 和 Lynx BTS 都在同一进程内的隔离 JS context 中。exposeInLynxBTS() 暴露的是 BTS 可直接调用的 JS 对象,可以包含函数、闭包和返回 Promise 的异步 API。详细说明见 Node.js 与 Lynx 通信:使用 Preload Scripts。

    迁移 IPC

    如果原 Electron Renderer 通过 ipcRenderer.invoke() 调用主进程:

    // renderer.ts
    const result = await ipcRenderer.invoke('get-system-info', {
      includeCpuDetail: true,
    });

    Lynx UI 侧使用 NativeModules.bridge.call(method, payload, callback)。该方法返回 void,需要通过回调接收结果,不能直接 await 调用:

    // App.tsx
    NativeModules.bridge.call(
      'get-system-info',
      { includeCpuDetail: true },
      (result) => {
        renderSystemInfo(result);
      },
    );

    Node 主进程通过监听 -lynx-invoke 事件处理 bridge.call 调用,并通过 event.sendReply 返回:

    // main.ts
    // 复用上文 app.whenReady() 内的 win,在 win.loadFile() 前注册此监听器。
    
    win.on('-lynx-invoke', (event, methodName, params) => {
      if (methodName === 'get-system-info') {
        event.sendReply(getSystemInfo(params));
      }
    });

    如果原 Electron 主进程用 webContents.send() 向 Renderer 推送事件:

    win.webContents.send('system-info-update', data);

    迁移到 Lynxtron 后使用 LynxWindow.sendGlobalEvent():

    win.sendGlobalEvent('system-info-update', data);

    Lynx UI 侧通过 GlobalEventEmitter 接收。完整示例见 Node 主进程与 Lynx 双向消息通信。

    处理原生模块、构建和打包

    如果 Electron 项目使用了 Node.js 原生模块,迁移后需要按 Lynxtron 运行时重新构建:

    npx @lynx-js/lynxtron-rebuild

    应用打包建议使用 @lynx-js/lynxtron-builder。它基于 electron-builder,可以继续读取 electron-builder.yml,但会把 Electron runtime 替换为当前 Lynxtron 依赖对应的 Lynxtron runtime。建议先按快速上手创建工程,再迁入业务代码。以下脚本对应当前脚手架的 lynx.config.ts 和 rsbuild.config.ts,分别构建 Lynx UI 和桌面主进程;已有工程应保留与其构建配置匹配的 build 命令,将打包入口替换为 lynxtron-builder:

    package.json
    {
      "scripts": {
        "build": "rspeedy build --environment lynx && rsbuild build --environment desktop",
        "pack": "npm run build && lynxtron-builder --publish never"
      }
    }

    迁移时建议检查以下内容:

    • 原生模块是否依赖 Electron ABI,需要用 @lynx-js/lynxtron-rebuild 重新构建。
    • 原 Renderer 中直接访问 Node.js 的代码是否已经移入主进程或 preload scripts。
    • electron-builder.yml 中的 directories.app、files、extraResources 等配置是否已经指向 Lynxtron 主进程产物、Lynx bundle 和必要资源。
    • Lynx bundle 是否被正确构建并包含在打包产物中。
    • 无边框窗口、透明窗口、拖拽区域和系统按钮是否已用 Lynx UI 重写。

    推荐迁移顺序

    1. 创建一个新的 Lynxtron 工程,跑通最小窗口和打包流程。
    2. 迁移 Electron 主进程中的应用生命周期、菜单、托盘、对话框等桌面端逻辑。
    3. 把 BrowserWindow 替换为 LynxWindow,加载一个最小 Lynx bundle。
    4. 迁移 preload scripts,把 UI 需要的 Node.js 能力暴露到 NativeModules.nodejs.exposed。
    5. 按窗口重写 Renderer UI,把 HTML/DOM/CSS 改成 Lynx 元素和样式;如果需要渐进式迁移,可以先用 <webview> 承载暂时未迁移的 Web 页面。
    6. 迁移 IPC,统一使用 NativeModules.bridge.call / NativeModules.bridge.send 和 LynxWindow.sendGlobalEvent(),并在主进程侧监听 -lynx-invoke / -lynx-message 事件。
    7. 重建原生模块,使用 @lynx-js/lynxtron-builder 检查打包产物和平台相关能力。

    继续阅读

    除非另有说明,本项目采用知识共享署名 4.0 国际许可协议进行许可,代码示例采用 Apache License 2.0 许可协议进行许可。