阳光正好Electron今天出发

阳光正好Electron今天出发

本文阅读须要花费一点时间,若是时间紧迫者能够阅读开头以及结尾总结。thanks
来咿呀快活呀html


首先问一点问题

  1. 什么是Electron
  2. Electron是用来干什么的,解决些什么
  3. 写个todo看看得怎么玩?
  4. 怎么实现桌面程序,概念意义,底层的技术大概是什么?

第一步 如何安装

首先咱们的选择能够在save在开发依赖上
npm install electron --save-dev
也能够全局安装electron
npm install electron -g

这里有一段文档翻译有意思的话

Electron 可让你使用纯 JavaScript 调用丰富的原生(操做系统) APIs 来创造桌面应用。 你能够把它看做一个专一于桌面应用的 Node. js 的变体,而不是 Web 服务器。java

这不意味着 Electron 是绑定了 (GUI) 库的 JavaScript。 相反,Electron 使用 web 页面做为它的 GUI,因此你能把它看做成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。

awesomenode

第二步 了解基本概念

Electron 分主进程与渲染进程

主进程运行package.json 的main脚本被成为主进程。运行在主进程中的脚本将以建立 web 页面的方式显示一个 GUI。
(这让我想起了。之前作python 跟java时候gui主线程的感受。。。)python

Electron的web页面运行在一个成为渲染进程的进程中。 而且在页面与操做系统中得到一些低级别的交互git

主进程 渲染进程 区别

主进程使用BrowserWindow实例建立页面github

每一个 BrowserWindow实例都在本身的渲染进程里运行页面。 web

当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。chrome

渲染进程相互独立,主进程管理。npm

渲染进程只须要关心它们本身的页面。json

网页内是不能进行GUI操做的。

须要对应的渲染进程与主进程之间通讯。

通讯方法

一、使用HTML5 API 例如storage API、localstorage、sessionstorage或IndexedDB

二、IPC通讯( ipcRenderer 和 ipcMain)

三、RPC通讯(remote模块)

下面介绍实现一个todolist

首先编码开始。

它管控文件与package.json相似。

主线程由main定义的入口文件建立

如下是不完整代码。大概的。

须要完整代码能够查看github地址。

todolist

项目目录大概为
todo
-- addWindow.html
-- main.js
-- package.json
-- window.html

入口文件是咱们的main.js,它是一个应用中的中心调度。

// 能够向使用nodejs时同样引入模块
// 这个也就是所谓的主进程,看起来就像是nodejs脚本
const electron = require('electron');
const url = require('url');
const path = require('path');


const {app,BrowserWindow,Menu,ipcMain} = electron;

//设置环境
process.env.NODE_ENV = 'production';

let mainWindow;
let addWindow;
//listen for app to be ready

app.on('ready',function(){
    //1. 建立一个新的窗口
    mainWindow = new BrowserWindow({});
    //加载 html进窗口
    mainWindow.loadURL(url.format({
        pathname: path.join(__dirname,'window.html'),
        protocol: 'file:', //协议
        slashes:true
    }));
    //file:/dirname/mainWindow.html

    //6. 大窗口关闭整个程序关闭
    mainWindow.on('close',function(){
        app.quit();
    })

    //2 .设置菜单
    const mainMenu = Menu.buildFromTemplate(mainMenuTemplate);
    Menu.setApplicationMenu(mainMenu);

})


//handle create add window 

function createAddWindow(){
    //5. 新建窗口
    addWindow = new BrowserWindow({
        width: 300,
        height: 200,
        title: 'Add TODO list item'
    });
    //load html into window
    addWindow.loadURL(url.format({
        pathname: path.join(__dirname,'addWindow.html'),
        protocol: 'file:', //协议
        slashes:true
    }));
    // 垃圾回收
    addWindow.on('close',function(){
        addWindow = null;
    })
}

//7. 捕获子窗口ipc item:add
// IPC通讯
ipcMain.on('item:add',function(e,item){
    console.log('item');
    mainWindow.webContents.send('item:add',item);
    addWindow.close();
});

const mainMenuTemplate = [{
    label: 'File',
    submenu: [//3. 设置子菜单
        {
            label: 'Add Item',
            click(){
                //4. 新建窗口
                createAddWindow();
            }

        },
        {
            label: 'Clear Items',
            click(){
                mainWindow.webContents.send('item:clear');
            }
        },
        {
            label: 'Quit',
            accelerator: process.platform =='darwin'? 'Commond+Q':'Ctrl+Q',//快捷键作os版本区分
            click(){
                app.quit();
            }
        }
    ]
}];


// if mac , add empty object to menu
if(process.platform =='darwin'){
    mainMenuTemplate.unshift({});
}


// 区分开发环境
if(process.env.NODE_ENV !== 'production'){
    // add devtools
    mainMenuTemplate.push({
        label:'Developer tools',
        submenu:[
            {
                role:'reload'
            },
            {
                label: 'Toggle DevTools',
                accelerator: process.platform =='darwin'? 'Commond+I':'Ctrl+I',//快捷键作os版本区分,
                click(item,focusedWindow){
                    //区分点击窗口
                    focusedWindow.toggleDevTools();
                }
            }
        ]
    });
}

主进程设置好ipc通讯口。
监控渲染进程对应事件

addWindow.html

<div class="container">
            <form>
                    <div>
                        <label>
                            Enter Item
                        </label>
                        <input type="text" id="item" autofocus>
                    </div>
                    <button class="btn waves-effect waves-light" type="submit">add Item</button>
                    <!-- 将item发送到mainWindow -->
                </form>
    </div>
    
    
        const electron = require('electron');
        const {ipcRenderer} = electron;
        
        const form = document.querySelector('form');
        form.addEventListener('submit',function(e){
            e.preventDefault();
            // console.log(123);
            const item = document.querySelector('#item');
            ipcRenderer.send('item:add',item.value);
            //使用ipc进行通讯
            
        })

主进程渲染页面

<nav>
        <div class="nav-wrapper">
            <a class="brand-logo center">
                TODO LIST
            </a>
        </div>
    </nav>
    <ul id="mainContainer">

    </ul>


    <script>
        const electron = require('electron');
        const {ipcRenderer} = electron;
        const ul = document.querySelector('#mainContainer');
        ipcRenderer.on('item:add',function(e,item){
            ul.className = 'collection';
            const li = document.createElement('li');
            li.className = 'collection-item';
            const itemText = document.createTextNode(item);
            li.appendChild(itemText);
            // const li = '<li class="collection-item">'+item+'</li>';
            ul.appendChild(li);  
            // ul.innerHTML = ul.innerHTML+li;     
        })
        //clear items
        ipcRenderer.on('item:clear',function(e){
            ul.innerHTML = '';
            // if(ul.clildren.length == 0){
                ul.className = '';
            // }
        })
        // Remove item by doubleclick
        ul.addEventListener('dblclick',function(e){
            e.target.remove();
            // console.log(ul.clildren.length);
            if(ul.children.length == 0){
                ul.className = '';
            }
        })
    </script>

demo总结

大概能够了解

  • 可使用node模块
  • 渲染界面可使用web页面渲染
  • 了解进程间的通讯

调试

渲染进程的调试

渲染进行的调试能够经过webContent的openDevTool api打开,调试web页面

const { BrowserWindow } = require('electron')
  
let win = new BrowserWindow()
win.webContents.openDevTools()

主进程调试

命令行开关
--inspect=[port]
监听 V8 引擎中有关 port 的调试器协议信息

--inspect-brk=[port]
和--inspector 同样,可是会在JavaScript 脚本的第一行暂停运行。
外部调试器
  • 经过访问 chrome://inspect 来链接 Chrome 并在那里选择须要检查的Electron 应用程序。
  • 使用 VSCode 进行主进程调试

(感受这里有点想传统的debug nodejs in browser)


chromium

由于是使用chromium结合,因此咱们同时也可使用HTML5新特性在咱们渲染进程里
例如离线通知,离线缓存等。


总结

  • Electron 是一个能够用 JavaScript、HTML 和 CSS 构建桌面应用程序的库
  • 能够多端兼容(一套代码)
  • Chromium、Node.js 、调用操做系统本地功能的 API
  • 无缝使用Node

一些连接


在这些地方能够找到我

题外话

本文写成较早,有些不妥之处还望谅解(2017年)

若有疑惑欢迎讨论。

相关文章
相关标签/搜索