如何在JavaScript中构建Electron桌面应用:多线程、SQLite、原生组件和其他公共的痛点

声明:本博文的全部图片和内容,均来自于原博客

原博文链接: https://medium.freecodecamp.org/how-to-build-an-electron-desktop-app-in-javascript-multithreading-sqlite-native-modules-and-1679d5ec0ac

 

       作为一个开发桌面端应用的框架,Electron有很多与桌面端相关的东西。该框架能够提供全部的Node API和完整的桌面开发生态。它能够运行在所有的主流浏览器上。因为Electron是基于web结构的,你可以使用最新的CSS样式来布局你的UI。

       有很多文章都介绍了如何开始使用Electron,但是很少有人介绍怎么使用SQLite,怎么使用多线程。我们将介绍如何使用Electron来创建处理大量数据,还能够处理多个任务的应用。

 

我们会重点介绍以下一些内容:

(1)Electron是如何工作的(简要介绍),还有就是Electron的框架结构是如何影响我们要做的事情的;

(2)多线程;

(3)使用本地数据库,比如SQLite,或者在Electron应用中嵌入任意的文件;

(4)原生组件;

(5)一些可能已经明白,但是需要注意的地方;

(6)使用一些原生的组件来打包应用。

 

简要介绍Electron是如何工作的:

       再次强调Electron框架结构背后的原则,是非常有必要的。一个Electron应用包含以下至少两个步骤。主线程是你应用的入口,主线程会为你渲染进程(renderer process)来完成所有的工作。主线程只有一个实例。

       渲染进程(renderer processes)使用Chromium内核来渲染你的应用,应用的渲染方式与浏览器中标签页的渲染方式是一致的。渲染进程都是通过浏览器窗口中的loadURL方法,也就是说,你需要提供一个HTML文件,来开始运行你的应用。

Electron结构的说明:

       Electron的简洁性是它的一个最大的特点。你的主线程完成所有的配置工作,然后传入一个HTML文件或者URL地址来开启整个渲染流程。这个HTML文件可以与浏览器上打开的任何一个HTML文件一样,所以你可以把自己写得很多HTML文件直接拿来用。

       不过Electron的单一主线程不是会跟应用的多线程矛盾吗?Electron的文档指出,每个UI的渲染都对应一个渲染进程。

       我们需要非常注意的是,在Electron中做大量运算会降低你应用程序的渲染速度,甚至会导致应用暂停。所以,我们要将大量的运算移除主线程。最好在主线程中,只留下一些必要的操作。因为我们不能在前端的渲染中加入大量运算,所以我们必须想其他的法子。

多线程:

Electron中三种实现多线程的不同方法:

1、使用web workers;

2、分出新的进程来执行任务;

3、将渲染器按照workers进程来执行。

Web Workers:

         因为Electron是基于Chromium做的,所以任何在浏览器中的渲染进程,Electron同样可以做。这也就是说,你可以使用web workers来分配独立的线程来处理需要大量计算的任务。这样采用多线程的方式,与浏览器中采用多线程的优势一样。

       然而,有个需要特别注意的是,你不能在Electron中使用原生组件。从技术上讲,可以,但是使用原生组件会导致你的应用崩溃。这是一个非常棘手的问题,因为在浏览器中的很多多线程都使用原生的组件(node-sqlite3也是一样的)。

分出新的进程来执行任务:

       Electron使用Node做作为运行环境,这也就意味着你可以使用已经建立好的组件例如集群。这样会有新的进程被分配用来执行任务,也就可以将大量计算的进程,移出主线程。

       主要的问题是,不像渲染进程,子进程不能使用Electron库中的方法。这样的话,你就必须有使用IPC(Inter-process Channel)与主线程通信,渲染进程可以通过远程组件(remote module)来直接告诉主进程,只做主要的任务。

       另外一个问题是,如果你正在使用ES组件或JavaScript的TC39特性,你需要确保你的那些脚本能够使用,并且最后,你需要将这些脚本全部打包到应用中。当然啦,这个问题,只要是Node应用要分出新的进程,都会遇到,这些也会增加你创建进程来解决问题的复杂性。在用一些新的JavaScript特性时,你需要权衡一下,你能节省的时间和为了一些不必要引入的特性擦屁股所耗费的时间。

使用渲染进程作为工作线程:

       渲染进程一般来讲,是用来渲染你应用的UI组件的。不过,渲染进程能够干的事儿,不止是这一件。你也可以将它作为背后工作的进程来使用,不过你需要使用被传入BrowserWindow的configuring the show flag。

       将渲染进程作为后台工作进程有很多好处。不像web workers,你可以自由的使用原生的组件,也不像分支进程,你仍然能够使用electron的库来通知主进程做什么事情,比如打开一个窗口或者创建一个系统通知。

       使用Electron的一个巨大挑战是IPC。简单地说,你需要一个非常强大的模板和一堆复杂的监听器来进行艰难的调试。还有就是你做单元测试的时候,会遇到同样的问题。通过使用渲染进程作为主进程,你可以完全绕过上述挑战。跟你服务器端做的事情一样,你需要监听本地的端口,接收请求,这样你就需要使用一些工具,比如GraphQL + React Apollo。你也可以使用websockets来实现实时通信。另外一个好处是,你不需要使用ipcRenderer,也能够保证你的Electron和web应用是一致的(你可以在桌面应用和web应用中共享你的代码)。

       对于一些更加高级的应用场景,上述的方法也能够通过集群的方式来做到全世界最好。唯一的缺点是,你需要提供一个HTML文件为你的worker renderer进程提供一个入口。

如何使用SQLite(或者一些其他你想写入的文件):

  

       有几个状态管理的方法,这些方法无需原生的组件。例如,在一个渲染环境中使用Redux处理你的状态信息。

       然而,如果你想处理大量数据,上面的方法就远远不能满足了。那么接下来,就让我们看看,我们怎么样在Electron应用中使用SQLite。

       为了部署你的Electron应用,你首先要对Electron应用进行打包。有一系列的工具来对Electron应用进行打包——最流行的一款是Electron Builder。Electron使用ASAR归档格式来将应用打包成一个,非压缩的文件。ASAR归档文件是只读的——也就是说,你不能往里面写任何数据。这也就意味着,你不能在ASAR包中包含数据库或者一些其他代码文件(在Electron builder中,被成为被遗留的“文件”)。

       有一种代替策略,就是将数据库放在你的Electron应用的Resources文件夹下。整个打包后的Electron应用的文件结构和数据库的存放位置如图:

       打包好的ASAR归档包被叫做app.asar,存放在./Contents/Resources中。你可以存放你的数据库,在同一文件夹下,你还可以放入一些其他想要包含在应用中的文件。这个文件夹可以通过Electron Builder 的“extraResources”配置来创建。

       另一种方法是,直接在另一个文件夹下面单独创建数据库,不过你要保证,当用户想要删除应用的时候,数据库也要被删掉。

打包你的原生组件:

       主要的Node组件就是一些用JavaScript写成的脚本。原生组件则是用C++写成的,直接绑定在Node上的。这些原生组件,提供的接口与其他C/C++写成的库一样,这些库通常配置为安装后编译。

        作为底层的组件,这些组件需要在不同的操作系统下进行编译。也就是说,在windows机器上编译好的原生组件,不能在Linux机器上运行。这也是Electron中会存在的问题,因为我们需要在最后将这些原生组件打包为.dmg(OSX),.exe(Windows)或者.deb(Linux)执行文件。

       Electron应用需要将这些原生的组件在他们的目标系统上打包。因为你想要在CI/CD pipeline中自动完成整个过程,你需要在打包原生应用之前,将你的依赖包编译好。为了完成这件事情,你可以使用Electron团队的一个工具,叫做electron-rebuild。

       如果你正在开发一个非商业用途的开源项目,你可以使用TravisCI(Linux,OSX)和Appveyor(Windows)来自动编译,测试和部署你的应用。

       如果你有集成的测试,你需要安装特定的依赖包来完成无穷无尽的测试。在TravisCI(Linux和OSX)下的配置文件可以在这里找到,Appveyor(Windows)下的配置文件可以在这里找到(这些示例配置文件,是基于electron-react-boilerplate项目来的)。

一些其他的坑:

       当你的Electron应用打包之后,一些原有的路径常量工作方式可能跟你预想的不太一样。像一些变量__dirname,__filename和一些方法,例如process.cwd工作方式不一样了(可以看这个问题,问题,问题)。使用app.getAppPath来代替。

总结:

       在Electron中有许多多线程的应用,Web Workers虽然方便,但是缺少使用原生组件的能力。像node一样创建分支进程,缺少使用Electron库的能力,公共的任务使用IPC时,事情就变得复杂起来、使用渲染进程作为工作进程,能够给予应用像Node Server是使用IPC的一种替代方式,同时,能够保证用户具备从Electron渲染库中获取组件和方法的能力。

       由于Electron包文件是只读的ASAR归档包,任何我们需要写入的文件(例如SQLite数据库)都不能包含到归档包中。不过,你可以通过防止Resource文件夹的方式来包含你的这些文件。

       最后就是,路径可能会发生改变,你需要找一些替代方法。

你可能感兴趣的:(JavaScript,Electron)