您的位置:88bifa必发唯一官网 > 必发88首页 > XCel 项目总括:Electron 与 Vue 的性质优化必发88首

XCel 项目总括:Electron 与 Vue 的性质优化必发88首

发布时间:2019-10-08 02:57编辑:必发88首页浏览(169)

    汇成一句话

    Electron 应用就像 Node 应用,它也依赖一个 package.json 文件。该文件定义了哪个文件作为主进程,并因此让 Electron 知道从何启动应用。然后主进程能创建渲染进程,并能使用 IPC 让两者间进行消息传递。

    必发88首页 1

    至此,Electron 的基础部分介绍完毕。该部分是基于笔者之前翻译的一篇文章《Essential Electron》,译文可点击 这里。


    两个进程(重点)

    Electron 有两种进程:『主进程』和『渲染进程』。部分模块只能在两者之一上运行,而有些则无限制。主进程更多地充当幕后角色,而渲染进程则是应用程序的各个窗口。

    注:可通过任务管理器(PC)/活动监视器(Mac)查看进程的相关信息。

    • 模块:Electron 的 API 是根据它们的用途进行分组。例如:dialog 模块拥有所有原生 dialog 的 API,如打开文件、保存文件和警告等弹窗。

    斜分割线

    如图:必发88首页 2

    分割线可以通过 ::after/::before 伪类元素实现一条直线,然后通过 transform:rotate(); 旋转特定角度实现。但这种实现的一个问题是:由于宽度是不定的,因此需要通过 JavaScript 运算才能得到准确的对角分割线。

    因此,这里可以通过 CSS 线性渐变 linear-gradient(to top right, transparent, transparent calc(50% - .5px), #d3d6db calc(50% - .5px), #d3d6db calc(50% .5px), transparent calc(50% .5px)) 实现。无论宽高如何变,依然妥妥地自适应。

    多进程!!!

    前面说道,JavaScript 天生单线程,即使再快,对于数据量较大时,也会出现拒绝响应的问题。因此需要 Web Worker 或类似的方案去解决。

    在这里我不选择 Web worker 的原因有如下几点:

    1. 有其它更好的替代方案:一个主进程能创建多个渲染进程,通过 IPC 即可进行数据交互;
    2. Electron 不支持 Web Worker!(当然,可能会在新版本支持,最新信息请关注官方)

    Electron 作者在 2014.11.7 在《state of web worker support?》 issue 中回复了以下这一段:

    Node integration doesn’t work in web workers, and there is no plan to do. Workers in Chromium are implemented by starting a new thread, and Node is not thread safe. Back in past we had tried to add node integration to web workers in Atom, but it crashed too easily so we gave up on it.

    因此,我们最终采用了创建一个新的渲染进程 background process 进行处理数据。由 Electron 章节可知,每个 Electron 渲染进程是独立的,因此它们不会互相影响。但这也带来了一个问题:它们不能相互通讯?

    错!下面有 3 种方式进行通讯:

    1. Storage API:对某个标签页的 localStorage/sessionStorage 对象进行增删改时,其他标签页能通过 window.storage 事件监听到。
    2. IndexedDB:IndexedDB 是一个为了能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API。
    3. 通过主进程作为中转站:设主界面的渲染进程是 A,background process 是 B,那么 A 先将 Excel 数据传递到主进程,然后主进程再转发到 B。B 处理完后再原路返回,具体如下图。当然,也可以将数据存储在主进程中,然后在多个渲染进程中使用 remote 模块来访问它。

    该工具采用了第三种方式的第一种情况:
    必发88首页 3

    1、主页面渲染进程 A 的代码如下:

    JavaScript

    //① ipcRenderer.send('filter-start', { filterTagList: this.filterTagList, filterWay: this.filterWay, curActiveSheetName: this.activeSheet.name }) // ⑥ 在某处接收 filter-response 事件 ipcRenderer.on("filter-response", (arg) => { // 得到处理数据 })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //①
    ipcRenderer.send('filter-start', {
        filterTagList: this.filterTagList,
        filterWay: this.filterWay,
        curActiveSheetName: this.activeSheet.name
    })
     
    // ⑥ 在某处接收 filter-response 事件
    ipcRenderer.on("filter-response", (arg) => {
        // 得到处理数据
    })
     

    2、作为中转站的主进程的代码如下:

    JavaScript

    //② ipcMain.on("filter-start", (event, arg) => { // webContents 用于渲染和控制 web page backgroundWindow.webContents.send("filter-start", arg) }) // ⑤ 用于接收返回事件 ipcMain.on("filter-response", (event, arg) => { mainWindow.webContents.send("filter-response", arg) })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //②
    ipcMain.on("filter-start", (event, arg) => {
        // webContents 用于渲染和控制 web page
        backgroundWindow.webContents.send("filter-start", arg)
    })
     
    // ⑤ 用于接收返回事件
    ipcMain.on("filter-response", (event, arg) => {
        mainWindow.webContents.send("filter-response", arg)
    })
     

    3、处理繁重数据的 background process 渲染进程 B 的代码如下:

    JavaScript

    // ③ ipcRenderer.on('filter-start', (event, arg) => { // 进行运算 ... // ④ 运算完毕后,再通过 IPC 原路返回。主进程和渲染进程 A 也要建立相应的监听事件 ipcRenderer.send('filter-response', { filRow: tempFilRow }) })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // ③
    ipcRenderer.on('filter-start', (event, arg) => {
        // 进行运算
        ...
     
        // ④ 运算完毕后,再通过 IPC 原路返回。主进程和渲染进程 A 也要建立相应的监听事件
        ipcRenderer.send('filter-response', {
            filRow: tempFilRow
        })
    })
     

    至此,我们将『读取文件』、『过滤数据』和『导出文件』三大耗时的数据操作均转移到了 background process 中处理。

    这里,我们只创建了一个 background process,如果想要做得更极致,我们可以新建『CPU 线程数- 1 』 个的 background process 同时对数据进行处理,然后在主进程对处理后数据进行拼接,最后再将拼接后的数据返回到主页面的渲染进程。这样就可以充分榨干 CPU 了。当然,在此笔者不会进行这个优化。

    不要为了优化而优化,否则得不偿失。 —— 某网友

    支持常见的编辑功能,如粘贴和复制

    Electron 应用在 MacOS 中默认不支持『复制』『粘贴』等常见编辑功能,因此需要为 MacOS 显式地设置复制粘贴等编辑功能的菜单栏,并为此设置相应的快捷键。

    JavaScript

    // darwin 就是 MacOS if (process.platform === 'darwin') { var template = [{ label: 'FromScratch', submenu: [{ label: 'Quit', accelerator: 'CmdOrCtrl Q', click: function() { app.quit(); } }] }, { label: 'Edit', submenu: [{ label: 'Undo', accelerator: 'CmdOrCtrl Z', selector: 'undo:' }, { label: 'Redo', accelerator: 'Shift CmdOrCtrl Z', selector: 'redo:' }, { type: 'separator' }, { label: 'Cut', accelerator: 'CmdOrCtrl X', selector: 'cut:' }, { label: 'Copy', accelerator: 'CmdOrCtrl C', selector: 'copy:' }, { label: 'Paste', accelerator: 'CmdOrCtrl V', selector: 'paste:' }, { label: 'Select All', accelerator: 'CmdOrCtrl A', selector: 'selectAll:' }] }]; var osxMenu = menu.buildFromTemplate(template); menu.setApplicationMenu(osxMenu); }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // darwin 就是 MacOS
    if (process.platform === 'darwin') {
        var template = [{
          label: 'FromScratch',
          submenu: [{
            label: 'Quit',
            accelerator: 'CmdOrCtrl Q',
            click: function() { app.quit(); }
          }]
        }, {
          label: 'Edit',
          submenu: [{
            label: 'Undo',
            accelerator: 'CmdOrCtrl Z',
            selector: 'undo:'
          }, {
            label: 'Redo',
            accelerator: 'Shift CmdOrCtrl Z',
            selector: 'redo:'
          }, {
            type: 'separator'
          }, {
            label: 'Cut',
            accelerator: 'CmdOrCtrl X',
            selector: 'cut:'
          }, {
            label: 'Copy',
            accelerator: 'CmdOrCtrl C',
            selector: 'copy:'
          }, {
            label: 'Paste',
            accelerator: 'CmdOrCtrl V',
            selector: 'paste:'
          }, {
            label: 'Select All',
            accelerator: 'CmdOrCtrl A',
            selector: 'selectAll:'
          }]
        }];
        var osxMenu = menu.buildFromTemplate(template);
        menu.setApplicationMenu(osxMenu);
    }
     

    实现思路

    1. 通过 js-xlsx 将 Excel 文件解析为 JSON 数据
    2. 根据筛选条件对 JSON 数据进行筛选过滤
    3. 将过滤后的 JSON 数据转换成 js-xlsx 指定的数据结构
    4. 利用 js-xlsx 对转换后的数据生成 Excel 文件

    纸上得来终觉浅,绝知此事要躬行

    Vue 全家桶

    该工具使用了 Vue、Vuex、Vuex-router。在工具基本定型阶段,由 1.x 升级到了 2.x。

    记得关闭 Vuex 的严格模式

    另外,由于自己学艺不精和粗心大意,忘记在生产环境关闭 Vuex 的『严格模式』。

    Vuex 的严格模式要在生产环境中关闭,否则会对 state 树进行一个深观察 (deep watch),产生不必要的性能损耗。也许在数据量少时,不会注意到这个问题。

    还原当时的场景:导入 Excel 数据后,再进行交互(涉及 Vuex 的读写操作),需要等几秒才会响应,而直接通过纯 DOM 监听的事件则无此问题。由此,判断出是 Vuex 问题。

    JavaScript

    const store = new Vuex.Store({ // ... strict: process.env.NODE_ENV !== 'production' })

    1
    2
    3
    4
    5
    const store = new Vuex.Store({
      // ...
      strict: process.env.NODE_ENV !== 'production'
    })
     

    开发体验如何?

    基于 Electron 的开发就像在开发网页,而且能够无缝地 使用 Node。或者说:在构建一个 Node 应用的同时,通过 HTML 和 CSS 构建界面。另外,你只需为一个浏览器(最新的 Chrome)进行设计(即无需考虑兼容性等)。

    • 使用 Node:这还不是全部!除了完整的 Node API,你还可以使用托管在 npm 上超过 350,000 个的模块。
    • 一个浏览器:并非所有浏览器都提供一致的样式,Web 设计师和开发者经常因此而不得不花费更多的精力,让网站在不同浏览器上表现一致。
    • 最新的 Chrome:可使用超过 90% 的 ES2015 特性和其它很酷的特性(如 CSS 变量)。

    自动更新

    如果 Electron 应用没有提供自动更新功能,那么就意味着用户想体验新开发的功能或用上修复 Bug 后的新版本,只能靠用户自己主动地去官网下载,这无疑是糟糕的体验。Electron 提供的 autoUpdater 模块可实现自动更新功能,该模块提供了第三方框架 Squirrel 的接口,但 Electron 目前只内置了 Squirrel.Mac,且它与 Squirrel.Windows(需要额外引入)的处理方式也不一致(在客户端与服务器端两方面)。因此如果对该模块不熟悉,处理起来会相对比较繁琐。具体可以参考笔者的另一篇译文《Electron 自动更新的完整教程(Windows 和 OSX)》。

    目前 Electron 的 autoUpdater 模块不支持 Linux 系统。

    另外,XCel 目前并没有采用 autoUpdater 模块实现自动更新功能,而是利用 Electron 的 DownloadItem 模块实现,而服务器端则采用了 Nuts。

    主进程

    主进程,通常是一个命名为 main.js 的文件,该文件是每个 Electron 应用的入口。它控制了应用的生命周期(从打开到关闭)。它既能调用原生元素,也能创建新的(多个)渲染进程。另外,Node API 是内置其中的。

    • 调用原生元素:打开 diglog 和其它操作系统的交互均是资源密集型操作(注:出于安全考虑,渲染进程是不能直接访问本地资源的),因此都需要在主进程完成。

    必发88首页 4

    XCel 项目总结:Electron 与 Vue 的性能优化

    2017/03/01 · 基础技术 · Javascript, 算法

    本文作者: 伯乐在线 - 刘健超-J.c 。未经作者许可,禁止转载!
    欢迎加入伯乐在线 专栏作者。

    XCEL 是由京东用户体验设计部凹凸实验室推出的一个 Excel 数据清洗工具,其通过可视化的方式让用户轻松地对 Excel 数据进行筛选。

    XCEL 基于 Electron 和 Vue 2.x,它不仅跨平台(windows 7 、Mac 和 Linux),而且充分利用 Electron 多进程任务处理等功能,使其性能优异。

    落地页: ✨✨✨
    项目地址: ✨✨✨

    如何在渲染进程调用原生弹框?

    在渲染进程中调用原本专属于主进程中的 API (如弹框)的方式有两种:

    1. IPC 通讯模块:先在主进程通过 ipcMain 进行监听,然后在渲染进程通过 ipcRenderer 进行触发;
    2. remote 模块:该模块为渲染进程和主进程之间提供了快捷的通讯方式。

    对于第二种方式,在渲染进程中,运行以下代码即可:

    JavaScript

    const remote = require('electron').remote remote.dialog.showMessageBox({ type: 'question', buttons: ['不告诉你', '没有梦想'], defaultId: 0, title: 'XCel', message: '你的梦想是什么?' }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const remote = require('electron').remote
     
    remote.dialog.showMessageBox({
      type: 'question',
      buttons: ['不告诉你', '没有梦想'],
      defaultId: 0,
      title: 'XCel',
      message: '你的梦想是什么?'
    }
     

    为 DOM 的 File 对象增加了 path 属性

    Electron 为 File 对象额外增了 path 属性,该属性可得到文件在文件系统上的真实路径。因此,你可以利用 Node 为所欲为

    本文由88bifa必发唯一官网发布于必发88首页,转载请注明出处:XCel 项目总括:Electron 与 Vue 的性质优化必发88首

    关键词: 必发88首页