Simple Life

和这个世界交手这许多年   你是否光彩依旧,兴致盎然...

您现在的位置是:首页 爱折腾 爱折腾详情

基于Electron构建你专属的桌面应用

发布时间:2017-3-12 作者:Felix 浏览(1589)

        在移动互联网大行其道的今天,尤其像最近微信新出小应用,又是赚足了眼球。但是桌面应用在今天仍然拥有一席之地,与web应用比起来,桌面应用可以很好的和操作系统交互,可以快速响应,在dock或者开始菜单随时启动,可以alt-tab 或者 cmd-tab进行切换等等。

       下面就来介绍我们今天的主角Electron——一个能让你用web的方式搭建一个跨平台桌面应用的开源项目。它使用nodejs调用丰富的原生API来创造桌面应用,你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。也是因为Chromium大法好,你不太需要考虑兼容的问题,目前Electron支持的系统有Linux、macOS、Windows。

       Electron的前身是Atom shell,它的诞生离不开Atom的贡献,当然Atom Editor也是用它实现的,除了Atom还有好许多软件是基于Eletron的,下图

        

electron1.png


      当然不仅仅是这些,目前github上Eletron的star已经超过4万,关注度颇高。

基本概念

       下面说一下一些经常在Electron 开发中使用的术语。

        ASAR:代表 Atom Shell Archive Format。一个asar压缩包就是一个简单的 tar 文件-就像将那些有联系的文件格式化至一个单独的文件中。Electron 能够任意读取其中的文件并且不需要解压缩整个文件。

       main process: 主进程,通常是指 main.js 文件,是每个 Electron 应用的入口文件。它控制着整个应用的生命周期,从打开到关闭。它也管理着原生元素比如菜单,菜单栏,Dock 栏,托盘等。主进程负责创建 APP 的每个渲染进程。而且整个 Node API 都集成在里面。

       renderer process: 渲染进程是你的应用内的一个浏览器窗口。与主进程不同的是,它能够同时存在多个而且运行在不一样的进程。而且它们也能够被隐藏。通常在浏览器内,网页通常运行在一个沙盒的环境挡住并且不能够使用原生的资源。然而 Electron 的用户在 Node.js 的 API 支持下可以在页面中和操作系统进行一些低级别的交互。

       IPC:代表 Inter-Process Communication。Electron使用 IPC来在主进程和渲染进程之间传递JSON信息。

基本假设

       基本的环境搭建这里就不赘述了,所以这里假设你有基本的HTML/CSS/JavaScript知识,有基本的数据库只是,了解sqlite,并且已经安装了npm、node.js和sqlite3,本例是在macOS下开发。好了,现在拿起你趁手的编辑器操练起来吧。 

沐浴在桌面应用的圣光下

       先来看下最终的实现效果:

        

electron2.png

        1. 首先我们创建一个TestProject的项目和一些必要的文件,最终结构如下: 

TestProject/
        ├── str/
        ├── css/
            ├──student.css  自定义的css样式,如有需要。
            ├──photon.min.css  photon的css样式
        ├── js/
            ├──jquery.min.js  
            ├──sql.js  通过js操作sqlite的js库
            ├──student.js  自定义的js文件
        ├── font/
            ├── photon-entypo.ttf  
            ├── photon-entypo.woff 
        ├──student.html
        ├──add_student.html
        ├──favicon.ico 应用图标
        ├── tchli.db 存储本地的sqlite数据
        ├── main.js
        └── package.json

        这里需要注意下,photon是一个前端库,让你更快的开发Electron apps,项目地址:https://github.com/connors/photon 顺便安利下Awesome Electron,开发Electron的利器 https://github.com/sindresorhus/awesome-electron

         2. 在package.json中加入如下代码,列出依赖库,和项目信息。然后在TestProject目录下执行npm install安装依赖库, 这里推荐淘宝的npm源。

执行:npm install --registry=https://registry.npm.taobao.org

{
  "name": "TestProject",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "keywords": [
    "Electron",
    "demo"
  ],
  "author": "Felix",
  "devDependencies": {
    "electron-prebuilt": "1.4.13",
    "sql.js": "0.4.0",
    "electron-packager": "^8.5.0"
  }
}

        执行成功,这时候你会看到TestProject路径下多了node_modules文件夹

        3. 然后main.js中加入如下代码,就是Electron中的main process:

const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
// 引入node.js模块
const path = require('path')
const url = require('url')
let mainWindow
function createWindow() {
    // 创建浏览器窗口
    mainWindow = new BrowserWindow({width: 1000, height: 600, icon: path.join(__dirname, 'src/favicon.ico')})
    // 加载指定页面
    mainWindow.loadURL(url.format({
        pathname: path.join(__dirname, 'src/student.html'),
        protocol: 'file:',
        slashes: true
    }));
    mainWindow.webContents.openDevTools() // 开启调试窗口,发布应用时关闭
    mainWindow.on('closed', function () {
        mainWindow = null
    })
}
app.on('ready', createWindow);
// 设置dock图标
app.dock.setIcon(path.join(__dirname, 'src/favicon.ico'))
// 所有窗口关闭时,退出应用
app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') {
        app.quit()
    }
})
app.on('activate', function () {
    if (mainWindow === null) {
        createWindow()
    }
})

        4. 完成了main process 接着我们创建render process来渲染我们的页面。就是Step1中src下的student.html和add_student.html文件。

student.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Student Management</title>
    <link rel="stylesheet" href="css/photon.min.css">
    <link rel="stylesheet" href="css/student.css"/>
</head>
<body>
<div class="window">
    <div class="window-content">
        <div class="pane-group">
            <div class="pane pane-sm sidebar">
                <nav class="nav-group">
                    <h5 class="nav-group-title">Student Management</h5>
                    <a href="student.html"><span class="nav-group-item active">
                          <span class="icon icon-user"></span>学生管理</span></a>
                </nav>
            </div>
            <div class="pane">
                <header class="toolbar toolbar-header">
                    <div class="toolbar-actions">
                        <div class="btn-group">
                            <a href="add_student.html">
                                <button class="btn btn-default">
                                    <span class="icon icon-user-add"></span>
                                </button>
                            </a>
                        </div>
                    </div>
                </header>
                <table class="table-striped">
                    <thead>
                    <tr>
                        <th>Name</th>
                        <th>Course</th>
                        <th>Remark</th>
                    </tr>
                    </thead>
                    <tbody class="table-content">
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
<script src="js/sql.js"></script>
<script src="js/jquery.min.js"></script>
<script src="js/student.js"></script>
<script>
    getStudentList()
</script>
</body>
</html>

        add_student.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Student Management</title>
    <link rel="stylesheet" href="css/photon.min.css">
    <link rel="stylesheet" href="css/student.css"/>
</head>
<body>
<div class="window">
    <div class="window-content">
        <div class="pane-group">
            <div class="pane pane-sm sidebar">
                <nav class="nav-group">
                    <h5 class="nav-group-title">TchLi</h5>
                    <a href="student.html"><span class="nav-group-item active">
                        <span class="icon icon-user"></span>学生管理</span></a>
                </nav>
            </div>
            <div class="pane">
                <div class="student-add">
                    <div class="form-group">
                        <label>学生姓名</label>
                        <input type="text" class="form-control student-name" placeholder="Student name">
                    </div>
                    <div class="form-group">
                        <label>课程名称</label>
                        <input type="text" class="form-control student-course" placeholder="Course">
                    </div>
                    <div class="form-group">
                        <label>备注</label>
                        <textarea class="form-control student-remark" rows="3" placeholder="Remark"></textarea>
                    </div>
                    <div class="form-actions">
                        <a href="student.html">
<button class="btn btn-form btn-default">Cancel</button></a>
                        <button class="btn btn-form btn-primary" id="add-student-submit">OK</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script src="js/sql.js"></script>
<script src="js/jquery.min.js"></script>
<script src="js/student.js"></script>
</body>
</html>

        5. 本例是教大家开发一个本地应用,那就需要一个本地存储。这里我选择了sqlite,它的特点就是无需安装、无需配置、跨平台,在这里还是非常合适的。这里使用了sql.js,使用nodejs去操作数据库。

项目地址: https://github.com/kripken/sql.js/

首先我们必须初始化数据库,创建表student,TestProject下执行

       sqlite3 tchli.db

CREATE TABLE student (id integer primary key autoincrement, name char(30), course char(80), remark text);

创建完成,确认下:

.tables

输出数据库tchli.db下的表student

student.js内容如下:

var fs = require('fs');
const path = require('path')
var filebuffer = fs.readFileSync(path.join(__dirname, '../../tchli.db'));
var getStudentList = function () {
    var db = new SQL.Database(filebuffer);
    var res = db.exec("select name, course, remark from student;");
    var result = res[0] ? res[0].values :  {}
    var tableContent = '';
    for(var i=0; i < result.length; i++) {
        tableContent += "<tr><td>" + result[i][0] + "</td><td>" + result[i][1] + "</td><td>" + result[i][2] + "</td></tr>"
    }
    $(".table-content").html(tableContent);
    db.close()
};
$('#add-student-submit').click(function () {
    var student_name = $('.student-name').val();
    var student_course = $('.student-course').val();
    var student_remark = $('.student-remark').val();
    var db = new SQL.Database(filebuffer);
    db.run("insert into student (name, course, remark)values(?, ?, ?)", [student_name, student_course, student_remark])
    var data = db.export();
    var buffer = new Buffer(data);
    fs.writeFileSync(path.join(__dirname, '../../tchli.db'), buffer);
    db.close()
    window.location.href="student.html";
})

        这个是添加学生和读取学生列表的操作。

        6. 现在肯定会有人说你巴拉巴拉了半天,什么也没看到啊。如果你一步步跟着来的话,现在我们在TestProject下执行

   elctron .

            出意外的话会弹出如下窗口

electron4.png

                           测试成功!!!点击顶部添加按钮,我们可以添加学生。

electron5.png

electron6.png


        7. 万事具备,只欠东风,这个东风就是打包发布了。现在我们的程序已经能够运行,借助打包工具只要稍微调整,我们可以发布到不同平台,macOS,Windows或者Linux,这就是跨平台的优势。

这里我们使用 electron-packager 打包,在Step2中已安装

      注释掉main.js中的devtools,在TestProject的上层目录执行:

electron-packager TestProject MyProject --platform=darwin --arch=x64 --icon="TestProject/tchli.icns"

这里tchli.icns是你自定义的应用图标,网上可以在线转格式,不设置会显示electron默认图标。

执行完毕,会在当前路径生成新文件夹

electron7.png

双击MyProject.app,噔噔噔~成功!现在你可以把它移到Mac系统应用程序路径下啦。

附上源码: https://github.com/felixglow/ElectronProject

接下来做什么

        Electron目前文档真的很少,也有不少坑,我也是一个个趟过来,有问题只能去atom社区或是stackoverflow上找找,这也是折腾的乐趣吧。本示例只是简单介绍了如何做一个桌面应用功能,篇幅有限,还有诸如IPC通信,Clipboard,Tray, 桌面集成等等没有加载实例中,可以阅读官方API文档,多实践,让你的应用更强大,更完美。进一步完善应用后可以提交到Mac App Store或是Windows应用商店。





上一篇: 使用Fabric加快你的项目部署

下一篇: 没有下一篇

基于 Django 搭建

服务器采用的 阿里云

域名来自 万网

苏ICP备16015443号

©2015-2016 felixglow.com.

GitHub

Design by Felix