使用Electron打造跨平台程序需要关注的技术点

2023年 8月 29日 56.5k 0

背景

上篇文章已经介绍了使用electron forge+vite+vue3来实现一个桌面应用程序的框架。本文重点介绍完善一个这样的框架的几个通用的需求点及实现方式。

需求

  • 实现客户端在线升级
  • 实现与本地操作系统的交互
  • 实现配置信息持久化
  • 国际化配置
  • 实现跨域访问

实现客户端在线升级

update.js

const {app,dialog,autoUpdater} = require('electron');
const log = require("electron-log")
autoUpdater.logger = log
autoUpdater.logger.transports.file.level = "info"

const server = 'https://update.electronjs.org'
const url = `${server}/dongluyang/intel-desktop-app/${process.platform}-${process.arch}/${app.getVersion()}`

autoUpdater.setFeedURL(
  { 
    url:url
  } 
)


autoUpdater.on('checking-for-update', () => {
    log.info("获取版本信息")
})

autoUpdater.on('update-not-available', () => {
    log.info("没有可更新版本")
})

autoUpdater.on('update-available', ()  => {
    log.info("发现新版本")
})

autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) => {
    dialog.showMessageBox({
        type: 'info',
        title: '软件更新',
        message: "发现新版本"+releaseName+", 确定安装?",
        detail: process.platform === 'win32' ? releaseNotes : releaseName,
        buttons: ['确定', '取消']
      }).then(returnValue => {
        if (returnValue.response === 0)  autoUpdater.quitAndInstall()
      })
  })

autoUpdater.on('error', (message) => {
    log.error('There was a problem updating the application')
    log.error(message)
})
  
export default autoUpdater

main.js

import autoUpdater from './update'

const createWindow = () => {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });
  // and load the index.html of the app.
  if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
    mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
  } else {
    mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
  }
  // Open the DevTools.
  mainWindow.webContents.openDevTools();
  mainWindow.once('ready-to-show', () => {
     autoUpdater.autoDownload = false
     autoUpdater.checkForUpdates()
 });

};

优缺点比较

方案

优点

缺点

本方案

实现简单,和electron-forge集成容易。electron-forge有丰富的插件生态

  • 只有update-downloaded事件,没有进度更新的事件。
  • 无法选择安装路径
  • 安装过程丑了一点,是一个动态gif
  • electron-builder + electron-updater的autoUpdater

  • 有download-progress事件,可以实现下载进度显示。
  • 不可以选择安装路径
  • 1.实现稍微比上述方案复杂。

    2.官方推荐electron-forge打包,与主流技术分叉。

    Electron Forge可以被认为是Electron Builder的替代品,后者在应用程序构建和发布方面实现了相同的用例。

    这两个项目在理念上的关键区别在于,Electron Forge专注于将现有的官方工具组合成一个单一的构建管道,而Builder则为大多数构建任务重写自己的内部逻辑。

    使用Forge有两个主要优势:

    Forge一旦在Electron中得到支持,就会接收用于应用程序构建的新功能(例如ASAR完整性或通用macOS构建)。这些功能是在考虑到官方Electron工具的情况下构建的,因此Forge在发布后立即收到它们。

    Forge的多包体系结构使其更易于理解和扩展。由于Forge由许多职责明确的较小包组成,因此更容易遵循代码流。此外,它的可扩展API设计意味着您可以编写自己的构建逻辑,而不必为高级用例提供配置选项。

    运行界面

    日志查看

    运行期间,有错误,可以及时查看内容,日志地址是:

    windows: C:Users%USERPROFILE%AppDataRoaming你的工程logs。

    mac: ~/Library/Application Support/你的工程 或者 ~/Library/Logs/你的工程。

    实现与本地操作系统的交互

    preload.js

    在这个问题中可以暴露一些接口,这些接口可以在前端页面调用,例如下面的就可以在前端vue页面调用window.versions.node调用node方法。

    const { contextBridge, ipcRenderer } = require('electron')
    
    contextBridge.exposeInMainWorld('versions', { 
      node: () => process.versions.node, 
      chrome: () => process.versions.chrome, 
      electron: () => process.versions.electron, 
      ping: () => ipcRenderer.send('ping') ,
      pong: () => ipcRenderer.invoke('pong') 
    })

    main.js

    通过ipcMain来处理。

    async function handlePing (event, keyword) {
      const webContents = event.sender
      const win = BrowserWindow.fromWebContents(webContents)
      win.setTitle(keyword)
    }
    
    
    // This method will be called when Electron has finished
    // initialization and is ready to create browser windows.
    // Some APIs can only be used after this event occurs.
    app.on('ready', ()=>{
      ipcMain.on('ping', handlePing)
      createWindow()
    });

    运行效果

    总结

    方向

    解释

    对应元语

    单向

    ipcRender向ipcMain发送消息

    ipcRender.send与ipcMain.on

    双向

    ipcRender向ipcMain发送消息,并等待结果

    ipcRender.invoke与ipcMain.handle

    国际化配置

    src/renderer/App.vue。

    配置默认中文显示。

    
     
        
     
    
    
    
    import { computed,ref } from 'vue';
    import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
    import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
    import useLocale from './hooks/locale';
    
    const { currentLocale } = useLocale();
    
    const locale = computed(() => {
      switch (currentLocale.value) {
        case 'zh-CN':
          return zhCN;
        case 'en-US':
          return enUS;
        default:
          return zhCN;
      }
    });
    const percentage = ref(0);
    const showProgressBar = ref(false);
    

    src/render/hooks/locale.js。

    import { computed } from 'vue';
    import { useI18n } from 'vue-i18n';
    import { Message } from '@arco-design/web-vue';
    
    export default function useLocale() {
      const i18 = useI18n();
      const currentLocale = computed(() => {
        return i18.locale.value;
      });
      const changeLocale = (value) => {
        if (i18.locale.value === value) {
          return;
        }
        i18.locale.value = value;
        localStorage.setItem('arco-locale', value);
        Message.success(i18.t('navbar.action.locale'));
      };
      return {
        currentLocale,
        changeLocale,
      };
    }

    src/render/main.js。

    import { createI18n } from 'vue-i18n';
    
    const i18n = createI18n({
      legacy: false, // 如果你使用 Composition API(推荐),请将legacy设置为false
      locale: 'zh', // 默认语言环境
      messages: {
        en: {
          hello: 'Hello',
          welcome: 'Welcome to our app!',
        },
        zh: {
          hello: '你好',
          welcome: '欢迎来到我们的应用!',
        },
      },
    });
    
    
    createApp(App).use(i18n).use(router).use(ArcoVue,{}).use(ArcoVueIcon).mount('#app');

    显示代码:

    {{ $t('welcome') }}

    剩余的两个功能,下一篇再完善。预告下,后面我把这个项目的模块进行分解,然后子模块拆分成技术点,然后通过chatgpt来实现,看看它的效果如何。敬请期待!是否能实现大部分功能,我们拭目以待。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论