Electron構(gòu)建跨平臺應(yīng)用

文章發(fā)表于 http://blog.poetries.top/2019/01/06/electron-summary

一掸茅、前言

  • NW.jsElectron 都可以用前端的知識來開發(fā)桌面應(yīng)用。NW.jsElectron起初是同一 個(gè)作者開發(fā)匆光。后來種種原因分為兩個(gè)產(chǎn)品。一個(gè)命名為 NW.js(英特爾公司提供技術(shù)支持)酿联、 另一命名為 Electron(Github 公司提供技術(shù)支持)终息。
  • NW.jsElectron 可以用 Nodejs 中幾乎所有的模塊。NW.jsElectron不僅可以把 html 寫的 web 頁面打包成跨平臺可以安裝到電腦上面的軟件贞让,也可以通過 javascript 訪問操作 系統(tǒng)原生的 UIApi(控制窗口周崭、添加菜單項(xiàng)目、托盤應(yīng)用菜單喳张、讀寫文件休傍、訪問剪貼板)。

githubatom 編輯器蹲姐、微軟的 vscode 編輯器磨取,包括阿里內(nèi)部的一些 軟件也是用 electron 開發(fā)的

1. Electron 是由誰開發(fā)的?

Electron 是由 Github 開發(fā)

2. Electron 是什么?

Electron 是一個(gè)用 HTMLCSSJavaScript 來構(gòu)建跨平臺桌面應(yīng)用程序的一個(gè)開源庫

3. Electron 把 HTML柴墩,CSS 和 JavaScript 組合的程序構(gòu)建為跨平臺桌面應(yīng)用程序的原理 是什么?

原理為 Electron 通過將 ChromiumNode.js 合并到同一個(gè)運(yùn)行時(shí)環(huán)境中忙厌,并將其打包為 MacWindowsLinux 系統(tǒng)下的應(yīng)用來實(shí)現(xiàn)這一目的江咳。

4. Electron 何時(shí)出現(xiàn)的逢净,為什么會出現(xiàn)?

Electron2013 年作為構(gòu)建 Atom 的框架而被開發(fā)出來。這兩個(gè)項(xiàng)目在 2014 春季開源歼指。 (Atom:為 Github 上可編程的文本編輯器)

一些歷史:

  • 20134Atom Shell 項(xiàng)目啟動 爹土。
  • 20145Atom Shell 被開源 。
  • 20154Atom Shell 被重命名為 Electron
  • 20165Electron 發(fā)布了 v1.0.0 版本

5. Electron 當(dāng)前流行程度?

目前 Electron 已成為開源開發(fā)者踩身、初創(chuàng)企業(yè)和老牌公司常用的開發(fā)工具胀茵。

6. Electron 當(dāng)前由那些人在維護(hù)支持?

Electron 當(dāng)前由 Github 上的一支團(tuán)隊(duì)和一群活躍的貢獻(xiàn)者維護(hù)。有些貢獻(xiàn)者是獨(dú)立開發(fā)者挟阻,有些則在用 Electron 構(gòu)建應(yīng)用的大型公司里工作琼娘。

7. Electron 新版本多久發(fā)布一次?

Electron 的版本發(fā)布相當(dāng)頻繁峭弟。每當(dāng) ChromiumNode.js 有重要的 bug 修復(fù)脱拼,新 API 或是版本更新時(shí) Electron 會發(fā)布新版本瞒瘸。

  • 一般 Chromium 發(fā)行新的穩(wěn)定版后的一到兩周之內(nèi),ElectronChromium 的版本會對其進(jìn)行更新熄浓,具體時(shí)間根據(jù)升級所需的工作量而定情臭。
    一般 Node.js 發(fā)行新的穩(wěn)定版一個(gè)月后,ElectronNode.js 的版本會對其進(jìn)行更新赌蔑,具 體時(shí)間根據(jù)升級所需的工作量而定俯在。

8. Electron 的核心理念是什么?

Electron 的核心理念是:保持 Electron 的體積小和可持續(xù)性開發(fā)。
如:為了保持 Electron 的小巧 (文件體積) 和可持續(xù)性開發(fā) (以防依賴庫和 API 的泛濫) 惯雳, Electron 限制了所使用的核心項(xiàng)目的數(shù)量朝巫。
比如 Electron 只用了 Chromium 的渲染庫而不是其全部組件。這使得升級 Chromium 更加容易石景,但也意味著 Electron 缺少了 Google Chrome 里的一些瀏覽器相關(guān)的特性劈猿。 添加到 Electron 的新功能應(yīng)該主要是原生 API。 如果可以的話潮孽,一個(gè)功能應(yīng)該盡可能的成 為一個(gè) Node.js 模塊揪荣。

9. Electron 當(dāng)前的最新版本為多少?

Electron 當(dāng)前的最新版本為 4.0.1 (當(dāng)前時(shí)間為 201916 號)

二、環(huán)境搭建

1. 安裝 electron

npm install -g electron

2. 克隆一個(gè)倉庫往史、快速啟動一個(gè)項(xiàng)目

# 克隆示例項(xiàng)目的倉庫
git clone https://github.com/electron/electron-quick-start

# 進(jìn)入這個(gè)倉庫
cd electron-quick-start

# 安裝依賴并運(yùn)行
npm install && npm start

3. 手動搭建一個(gè) electron 項(xiàng)目

  1. 新建一個(gè)項(xiàng)目目錄 例如: electrondemo01
  2. electrondemo01 目錄下面新建三個(gè)文件: index.html仗颈、main.jspackage.json
  3. index.html 里面用 css 進(jìn)行布局(以前怎么寫現(xiàn)在還是怎么寫)
  4. main.js 中寫如下代碼
var electron =require('electron'); //electron 對象的引用
const app=electron.app; //BrowserWindow 類的引用
const BrowserWindow=electron.BrowserWindow;

let mainWindow=null; //監(jiān)聽?wèi)?yīng)用準(zhǔn)備完成的事件 app.on('ready',function(){

//監(jiān)聽?wèi)?yīng)用準(zhǔn)備完成的事件
app.on('ready',function(){
    //創(chuàng)建窗口
    mainWindow=new BrowserWindow({width: 800, height: 600}); mainWindow.loadFile('index.html');
    
    mainWindow.on('closed', function () {
        mainWindow = null; })
    })
})

//監(jiān)聽所有窗口關(guān)閉的事件 
app.on('window-all-closed', function () {
    // On OS X it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q 
    if (process.platform !== 'darwin') {
        app.quit(); 
    }
})
  1. 運(yùn)行
electron . #注意:命令后面有個(gè)點(diǎn)

4. electron-forge 搭建一個(gè) electron 項(xiàng)目

electron-forge 相當(dāng)于 electron 的一個(gè)腳手架椎例,可以讓我們更方便的創(chuàng)建挨决、運(yùn)行、打包 electron 項(xiàng)目

npm install -g electron-forge 

electron-forge init my-new-app 

cd my-new-app

npm start

三订歪、Electron 運(yùn)行流程

3.1 Electron 運(yùn)行的流程

image.png

3.2 Electron 主進(jìn)程和渲染進(jìn)程

  • Electron 運(yùn)行 package.jsonmain 腳本的進(jìn)程被稱為主進(jìn)程脖祈。
  • 在主進(jìn)程中運(yùn)行的腳本通過創(chuàng)建 web 頁面來展示用戶界面。 一個(gè) Electron 應(yīng)用總是有且只有一個(gè)主進(jìn)程刷晋。
  • 由于 Electron 使用了 Chromium(谷歌瀏覽器)來展示 web 頁面盖高,所以 Chromium 的 多進(jìn)程架構(gòu)也被使用到。 每個(gè) Electron 中的 web 頁面運(yùn)行在它自己的渲染進(jìn)程中眼虱。
  • 主進(jìn)程使用 BrowserWindow 實(shí)例創(chuàng)建頁面喻奥。每個(gè) BrowserWindow 實(shí)例都在自己的渲 染進(jìn)程里運(yùn)行頁面。 當(dāng)一個(gè) BrowserWindow實(shí)例被銷毀后捏悬,相應(yīng)的渲染進(jìn)程也會被終止
image.png
image.png
  • 進(jìn)程:進(jìn)程是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動撞蚕,是 系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)邮破。
  • 線程:在一個(gè)程序里的一個(gè)執(zhí)行路線就叫做線程(thread)诈豌。更準(zhǔn)確的定義是: 線程是“一個(gè)進(jìn)程內(nèi)部的控制序列”仆救。
  • 線程和進(jìn)程:一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程

3.3 Electron 渲染進(jìn)程中通過 Nodejs 讀取本地文件

在普通的瀏覽器中抒和,web頁面通常在一個(gè)沙盒環(huán)境中運(yùn)行矫渔,不被允許去接觸原生的資源。 然而 Electron 的用戶在 Node.jsAPI支持下可以在頁面中和操作系統(tǒng)進(jìn)行一些底層交 互摧莽。
Nodejs 在主進(jìn)程和渲染進(jìn)程中都可以使用庙洼。渲染進(jìn)程因?yàn)榘踩拗疲荒苤苯硬僮魃?GUI镊辕。雖然如此油够,因?yàn)榧闪?Nodejs,渲染進(jìn)程也有了操作系統(tǒng)底層 API的能力征懈,Nodejs 中常用的 Path石咬、fsCrypto 等模塊在 Electron 可以直接使用卖哎,方便我們處理鏈接鬼悠、路徑、 文件 MD5 等亏娜,同時(shí) npm 還有成千上萬的模塊供我們選擇焕窝。

var fs = require('fs');
var content = document.getElementById('content'); 
var button = document.getElementById('button');

button.addEventListener('click',function(e){
    fs.readFile('package.json','utf8',function(err,data){ 
        content.textContent = data;
        console.log(data);
    }); 
});

3.4 Electron 開啟調(diào)試模式

 mainWindow.webContents.openDevTools();
image.png

四、Electron 模塊介紹

Electron 模塊介紹维贺、remote 模塊它掂、通 過 BrowserWindow 打開新窗口

4.1 Electron 主進(jìn)程和渲染進(jìn)程中的模塊

image.png

4.2 Electron remote 模塊

remote 模塊提供了一種在渲染進(jìn)程(網(wǎng)頁)和主進(jìn)程之間進(jìn)行進(jìn)程間通訊(IPC)的簡便途徑

Electron 中, 與 GUI 相關(guān)的模塊(如 dialog, menu 等)只存在于主進(jìn)程,而不在渲染進(jìn)程中 溯泣。為了能從渲染進(jìn)程中使用它們虐秋,需要用ipc模塊來給主進(jìn)程發(fā)送進(jìn)程間消息。使用 remote 模塊垃沦,可以調(diào)用主進(jìn)程對象的方法客给,而無需顯式地發(fā)送進(jìn)程間消息,這類似于 JavaRMI

4.3 通過BrowserWindow 打開新窗口

Electron 渲染進(jìn)程中通過 remote 模塊調(diào)用主進(jìn)程中的 BrowserWindow 打開新窗口

https://electronjs.org/docs/api/browser-window

// 主進(jìn)程代碼


const electron = require('electron'); 

// 控制應(yīng)用生命周期的模塊 
const {app} = electron;

// 創(chuàng)建本地瀏覽器窗口的模塊 
const {BrowserWindow} = electron;

// 指向窗口對象的一個(gè)全局引用栏尚,如果沒有這個(gè)引用起愈,那么當(dāng)該 javascript 對象被垃圾回收 的
// 時(shí)候該窗口將會自動關(guān)閉
let win;

function createWindow() {
    // 創(chuàng)建一個(gè)新的瀏覽器窗口
    win = new BrowserWindow({width: 1104, height: 620});//570+50
    
    // 并且裝載應(yīng)用的 index.html 頁面
    win.loadURL(`file://${__dirname}/html/index.html`);
    
    // 打開開發(fā)工具頁面
    win.webContents.openDevTools();
    
    //當(dāng)窗口關(guān)閉時(shí)調(diào)用的方法
    win.on('closed', () => {
        // 解除窗口對象的引用,通常而言如果應(yīng)用支持多個(gè)窗口的話译仗,你會在一個(gè)數(shù)組里 // 存放窗口對象抬虽,在窗口關(guān)閉的時(shí)候應(yīng)當(dāng)刪除相應(yīng)的元素。
        win = null;
    });
}

// 當(dāng) Electron 完成初始化并且已經(jīng)創(chuàng)建了瀏覽器窗口纵菌,則該方法將會被調(diào)用阐污。
// 有些 API 只能在該事件發(fā)生后才能被使用
app.on('ready', createWindow);

// 當(dāng)所有的窗口被關(guān)閉后退出應(yīng)用 
app.on('window-all-closed', () => {
    // 對于 OS X 系統(tǒng),應(yīng)用和相應(yīng)的菜單欄會一直激活直到用戶通過 Cmd + Q 顯式退出 
    if (process.platform !== 'darwin') {
        app.quit(); 
    }
});


app.on('activate', () => {
    // 對于 OS X 系統(tǒng)咱圆,當(dāng) dock 圖標(biāo)被點(diǎn)擊后會重新創(chuàng)建一個(gè) app 窗口笛辟,并且不會有其他
    // 窗口打開
    if (win === null) {
        createWindow(); 
    }
});

// 在這個(gè)文件后面你可以直接包含你應(yīng)用特定的由主進(jìn)程運(yùn)行的代碼功氨。 
// 也可以把這些代碼放在另一個(gè)文件中然后在這里導(dǎo)入
// 渲染進(jìn)程代碼 /src/render/index.js
// 打開新窗口屬性用法有點(diǎn)類似vscode打開新的窗口

const btn = document.querySelector('#btn');
const path = require('path');
const BrowerWindow = require('electron').remote.BrowserWindow;

btn.onclick = () => {
    win = new BrowerWindow({ 
        width: 300,
        height: 200, 
        frame: false, // false隱藏關(guān)閉按鈕、菜單選項(xiàng) true顯示
        fullscreen:true, // 全屏展示
        transparent: true 
    }) 

    win.loadURL(path.join('file:',__dirname,'news.html'));

    win.on('close',()=>{win = null});
}
image.png

五手幢、自定義頂部菜單/右鍵菜單

image.png

5.1 主進(jìn)程中調(diào)用Menu模塊-自定義軟件頂部菜單

https://electronjs.org/docs/api/menu-item

ElectronMenu 模塊可以用來創(chuàng)建原生菜單捷凄,它可用作應(yīng)用菜單和 context 菜單

這個(gè)模塊是一個(gè)主進(jìn)程的模塊,并且可以通過 remote 模塊給渲染進(jìn)程調(diào)用

// main/menu.js
const { Menu }  = require('electron')

// 文檔 https://electronjs.org/docs/api/menu-item
// 菜單項(xiàng)目
let menus = [
    {
        label: '文件',
        submenu: [
            {
                label: '新建文件',
                accelerator: 'ctrl+n', // 綁定快捷鍵
                click: function () { // 綁定事件
                    console.log('新建文件')
                }
            },
            {
                label: '新建窗口',
                click: function () {
                    console.log('新建窗口')
                }
            }
        ]
    },
    {
        label: '編輯',
        submenu: [
            {
                label: '復(fù)制',
                role: 'copy' // 調(diào)用內(nèi)置角色實(shí)現(xiàn)對應(yīng)功能
            },
            {
                label: '剪切',
                role: 'cut'  // 調(diào)用內(nèi)置角色實(shí)現(xiàn)對應(yīng)功能
            }
        ]
    },
    {
        label: '視圖',
        submenu: [
            {
                label: '瀏覽'
            },
            {
                label: '搜索'
            }
        ]
    }
]

let m = Menu.buildFromTemplate(menus)
Menu.setApplicationMenu(m)
// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/menu.js')
};

image.png

我們給菜單綁定事件,在命令行控制臺可以看到

image.png

5.2 渲染進(jìn)程中調(diào)用Menu模塊

不推薦使用這種方式,建議在主進(jìn)程中使用

1. remote

通過remote調(diào)用主進(jìn)程的方法

// 菜單引入的方式發(fā)生變化
const { Menu }  = require('electron').remote

// 其他代碼和上面菜單一樣
// ...

2. 加入index.html

<script src="render/menu.js"></script>

5.3 渲染進(jìn)程中自定義右鍵菜單

1. 定義菜單

// render/menu.js

// 在渲染進(jìn)程中通過remote模塊調(diào)用主進(jìn)程中的模塊
const { Menu }  = require('electron').remote
const { remote } = require('electron')

// 文檔 https://electronjs.org/docs/api/menu-item
// 菜單項(xiàng)目
let menus = [
    {
        label: '文件',
        submenu: [
            {
                label: '新建文件',
                accelerator: 'ctrl+n', // 綁定快捷鍵
                click: function () { // 綁定事件
                    console.log('新建文件')
                }
            },
            {
                label: '新建窗口',
                click: function () {
                    console.log('新建窗口')
                }
            }
        ]
    },
    {
        label: '編輯',
        submenu: [
            {
                label: '復(fù)制',
                role: 'copy' // 調(diào)用內(nèi)置角色實(shí)現(xiàn)對應(yīng)功能
            },
            {
                label: '剪切',
                role: 'cut'  // 調(diào)用內(nèi)置角色實(shí)現(xiàn)對應(yīng)功能
            }
        ]
    },
    {
        label: '視圖',
        submenu: [
            {
                label: '瀏覽'
            },
            {
                label: '搜索'
            }
        ]
    }
]

let m = Menu.buildFromTemplate(menus)
// Menu.setApplicationMenu(m)

// 綁定右鍵菜單
window.addEventListener('contextmenu', (e)=>{
   e.preventDefault()
   m.popup({
    window: remote.getCurrentWindow()
   })
}, false)
image.png

2. 引入

<!--index.html-->
<script src="render/menu.js"></script>

六屈藐、進(jìn)程通信

image.png

6.1 主進(jìn)程與渲染進(jìn)程之間的通信

有時(shí)候我們想在渲染進(jìn)程中通過一個(gè)事件去執(zhí)行主進(jìn)程里面的方法及皂。或者在渲染進(jìn)程中通知 主進(jìn)程處理事件,主進(jìn)程處理完成后廣播一個(gè)事件讓渲染進(jìn)程去處理一些事情。這個(gè)時(shí)候就 用到了主進(jìn)程和渲染進(jìn)程之間的相互通信

Electron 主進(jìn)程,和渲染進(jìn)程的通信主要用到兩個(gè)模塊:ipcMainipcRenderer

  • ipcMain:當(dāng)在主進(jìn)程中使用時(shí)院刁,它處理從渲染器進(jìn)程(網(wǎng)頁)發(fā)送出來的異步和同步信息,當(dāng)然也有可能從主進(jìn)程向渲染進(jìn)程發(fā)送消息。
  • ipcRenderer: 使用它提供的一些方法從渲染進(jìn)程 (web 頁面) 發(fā)送同步或異步的消息到主進(jìn)程粪狼。 也可以接收主進(jìn)程回復(fù)的消息

6.1.1 渲染進(jìn)程給主進(jìn)程發(fā)送異步消息

間接實(shí)現(xiàn)渲染進(jìn)程執(zhí)行主進(jìn)程里面的方法

1. 引入ipcRender

<!--src/index.html-->
<button id="send">在 渲染進(jìn)程中執(zhí)行主進(jìn)程里的方法(異步)</button>
<script src="render/ipcRender.js"></script>

2. 引入ipcMain

// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/ipcMain.js')
};

3. 渲染進(jìn)程發(fā)送消息

// src/render/ipcRender.js
//渲染進(jìn)程

let send = document.querySelector('#send');
const { ipcRenderer } = require('electron');

send.onclick = function () {
    // 傳遞消息給主進(jìn)程
    // 異步
    ipcRenderer.send('sendMsg', {name:'poetries', age: 23})
}

2. 主進(jìn)程接收消息

// src/main/ipcMain.js

//主進(jìn)程

const { ipcMain }  = require('electron')

// 主進(jìn)程處理渲染進(jìn)程廣播數(shù)據(jù)
ipcMain.on('sendMsg', (event, data)=> {
    console.log('data\n ', data)
    console.log('event\n ', event)
})
image.png

6.1.2 渲染進(jìn)程發(fā)送消息退腥,主進(jìn)程接收消息并反饋

渲染進(jìn)程給主進(jìn)程發(fā)送異步消息,主進(jìn)程接收到異步消息以后通知渲染進(jìn)程

1. 引入ipcRender

<!--src/index.html-->
<button id="sendFeedback">在 渲染進(jìn)程中執(zhí)行主進(jìn)程里的方法鸳玩,并反饋給主進(jìn)程(異步)</button>
<script src="render/ipcRender.js"></script>

2. 引入ipcMain

// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/ipcMain.js')
};

3. 渲染進(jìn)程發(fā)送消息

// src/render/ipcRender.js

//渲染進(jìn)程
let sendFeedback = document.querySelector('#sendFeedback');

const { ipcRenderer } = require('electron');

// 向主進(jìn)程發(fā)送消息
sendFeedback.onclick = function () {
    // 觸發(fā)主進(jìn)程里面的方法
    ipcRenderer.send('sendFeedback', {name:'poetries', age: 23})
}

4. 主進(jìn)程收到消息處理并廣播反饋通知渲染進(jìn)程

// src/main/ipcMain.js

//主進(jìn)程
const { ipcMain }  = require('electron')


// 主進(jìn)程處理渲染進(jìn)程廣播數(shù)據(jù)阅虫,并反饋給渲染進(jìn)程
ipcMain.on('sendFeedback', (event, data)=> {
    // console.log('data\n ', data)
    // console.log('event\n ', event)
    
    // 主進(jìn)程給渲染進(jìn)程廣播數(shù)據(jù)
    event.sender.send('sendFeedbackToRender', '來自主進(jìn)程的反饋')
})

5. 渲染進(jìn)程處理主進(jìn)程廣播的數(shù)據(jù)

// src/render/ipcRender.js
// 向主進(jìn)程發(fā)送消息后,接收主進(jìn)程廣播的事件
ipcRenderer.on('sendFeedbackToRender', (e, data)=>{
    console.log('event\n ', e)
    console.log('data\n ', data)
})
image.png

6.1.3 渲染進(jìn)程給主進(jìn)程發(fā)送同步消息

1. 引入ipcRender

<!--src/index.html-->
 <button id="sendSync">渲染進(jìn)程和主進(jìn)程同步通信</button>
<script src="render/ipcRender.js"></script>

2. 引入ipcMain

// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/ipcMain.js')
};

3. 渲染進(jìn)程給主進(jìn)程同步通信

// src/render/ipcMain.js
let sendSync = document.querySelector('#sendSync');

// 渲染進(jìn)程和主進(jìn)程同步通信
sendSync.onclick = function () {
    // 同步廣播數(shù)據(jù)
   let msg =  ipcRenderer.sendSync('sendsync', {name:'poetries', age: 23})
    
   // 同步返回主進(jìn)程反饋的數(shù)據(jù)
   console.log('msg\n ', msg)
}

4. 主進(jìn)程接收數(shù)據(jù)處理

// src/main/ipcMain.js

// 渲染進(jìn)程和主進(jìn)程同步通信 接收同步廣播
ipcMain.on('sendsync', (event, data)=> {
    // console.log('data\n ', data)
    // console.log('event\n ', event)
    // 主進(jìn)程給渲染進(jìn)程廣播數(shù)據(jù)
    event.returnValue ='渲染進(jìn)程和主進(jìn)程同步通信 接收同步廣播不跟,來自主進(jìn)程的反饋.';
})
image.png

6.1.4 渲染進(jìn)程廣播通知主進(jìn)程打開窗口

一般都是在渲染進(jìn)程中執(zhí)行廣播操作颓帝,去通知主進(jìn)程完成任務(wù)

1. 引入openWindow

<!--src/index.html-->
 <button id="sendSync">渲染進(jìn)程和主進(jìn)程同步通信</button>
<script src="render/openWindow.js"></script>

2. 引入ipcMain2

// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/ipcMain2.js')
};

3. 渲染進(jìn)程通知主進(jìn)程打開窗口

// src/render/openWindow.js

/* eslint-disable */
let openWindow = document.querySelector('#openWindow');

var { ipcRenderer } = require('electron');

// 渲染進(jìn)程和渲染進(jìn)程直接的通信========
openWindow.onclick = function () {
    // 通過廣播的形式 通知主進(jìn)程執(zhí)行操作
    ipcRenderer.send('openwindow', {name:'poetries', age: 23})
}

4. 主進(jìn)程收到通知執(zhí)行操作

// src/main/ipcMain2.js

/* eslint-disable */
let { ipcMain,BrowserWindow } = require('electron')
const path = require('path')

let win;

// 接收到廣播
ipcMain.on('openwindow', (e, data)=> {
    // 調(diào)用window打開新窗口
    win = new BrowserWindow({
        width: 400,
        height: 300,
    });
    win.loadURL(path.join('file:',__dirname, '../news.html'));
    win.webContents.openDevTools()
    win.on('closed', () => {
        win = null;
      });
})
image.png

6.2 渲染進(jìn)程與渲染進(jìn)程之間的通信

也就是兩個(gè)窗口直接的通信

6.2.1 localstorage傳值

Electron 渲染進(jìn)程通過 localstorage 給另一個(gè)渲染進(jìn)程傳值

1. 引入openWindow

<!--src/index.html-->
 <button id="sendSync">渲染進(jìn)程和主進(jìn)程同步通信</button>
<script src="render/openWindow.js"></script>

2. 引入ipcMain2

// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/ipcMain2.js')
};

3. 渲染進(jìn)程通知主進(jìn)程打開窗口

// src/render/openWindow.js

/* eslint-disable */
let openWindow = document.querySelector('#openWindow');

var { ipcRenderer } = require('electron');

// 渲染進(jìn)程和渲染進(jìn)程直接的通信========
openWindow.onclick = function () {
    // 通過廣播的形式 通知主進(jìn)程執(zhí)行操作
    ipcRenderer.send('openwindow', {name:'poetries', age: 23})
    
    // ======= localstorage傳值 =====
     localStorage.setItem('username', 'poetries')
}

4. 新建news頁面

<!--src/news.html-->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    news page
  </body>
  <script src="render/news.js"></script>
</html>
// src/render/news.js

let username = localStorage.getItem('username')
console.log(username)

5. 主進(jìn)程收到通知執(zhí)行操作

// src/main/ipcMain2.js

/* eslint-disable */
let { ipcMain,BrowserWindow } = require('electron')
const path = require('path')

let win;

// 接收到廣播
ipcMain.on('openwindow', (e, data)=> {
    // 調(diào)用window打開新窗口
    win = new BrowserWindow({
        width: 400,
        height: 300,
    });
    win.loadURL(path.join('file:',__dirname, '../news.html'));
    win.webContents.openDevTools()
    win.on('closed', () => {
        win = null;
      });
})

6.2.2 BrowserWindow和webContents方式實(shí)現(xiàn)

通過 BrowserWindowwebContents 模塊實(shí)現(xiàn)渲染進(jìn)程和渲染進(jìn)程的通信

webContents 是一個(gè)事件發(fā)出者.它負(fù)責(zé)渲染并控制網(wǎng)頁,也是 BrowserWindow 對象的屬性

需要了解的幾個(gè)知識點(diǎn)

  1. 獲取當(dāng)前窗口的 id
const winId = BrowserWindow.getFocusedWindow().id;
  1. 監(jiān)聽當(dāng)前窗口加載完成的事件
win.webContents.on('did-finish-load',(event) => {
    
})
  1. 同一窗口之間廣播數(shù)據(jù)
win.webContents.on('did-finish-load',(event) => {
    win.webContents.send('msg',winId,'我是 index.html 的數(shù)據(jù)');
})
  1. 通過 id 查找窗口
let win = BrowserWindow.fromId(winId);

下面是具體演示

1. 引入openWindow

<!--src/index.html-->
 <button id="sendSync">渲染進(jìn)程和主進(jìn)程同步通信</button>
<script src="render/openWindow.js"></script>

2. 引入ipcMain2

// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/ipcMain2.js')
};

3. 渲染進(jìn)程通知主進(jìn)程打開窗口

// src/render/openWindow.js

/* eslint-disable */
let openWindow = document.querySelector('#openWindow');

var { ipcRenderer } = require('electron');

// 渲染進(jìn)程和渲染進(jìn)程直接的通信========
openWindow.onclick = function () {
    // 通過廣播的形式 通知主進(jìn)程執(zhí)行操作
    ipcRenderer.send('openwindow', {name:'poetries', age: 23})
}

4. 主進(jìn)程收到通知執(zhí)行操作

// src/main/ipcMain2.js

let { ipcMain,BrowserWindow } = require('electron')
const path = require('path')

let win;

// 接收到廣播
ipcMain.on('openwindow', (e, userInfo)=> {
    // 調(diào)用window打開新窗口
    win = new BrowserWindow({
        width: 400,
        height: 300,
    });
    win.loadURL(path.join('file:',__dirname, '../news.html'));

    // 新開窗口調(diào)試模式
    win.webContents.openDevTools()

    // 把渲染進(jìn)程傳遞過來的數(shù)據(jù)再次傳遞給渲染進(jìn)程news
    // 等待窗口加載完
    win.webContents.on('did-finish-load', ()=>[
        win.webContents.send('toNews', userInfo)
    ])
    

    win.on('closed', () => {
        win = null;
      });
})

5. news接收主進(jìn)程傳遞的數(shù)據(jù)

數(shù)據(jù)經(jīng)過渲染進(jìn)程->主進(jìn)程->news渲染進(jìn)程

<!--news頁面-->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    news page
  </body>
  <script src="render/news.js"></script>
</html>
// src/render/news.js

var { ipcRenderer } = require('electron');

// let username = localStorage.getItem('username')
// console.log(username)

// 監(jiān)聽主進(jìn)程傳遞過來的數(shù)據(jù) 
ipcRenderer.on('toNews',(e, userInfo)=>{
    console.log(userInfo)
})
image.png
image.png

那么窝革,這里有一個(gè)問題购城,news進(jìn)程接收到了廣播后如何給出反饋呢?

image.png

1. 在主進(jìn)程中獲取窗口ID傳遞

// src/main/ipcMain2.js


let { ipcMain,BrowserWindow } = require('electron')
const path = require('path')

let win;

// 接收到廣播
ipcMain.on('openwindow', (e, userInfo)=> {
      // 獲取當(dāng)前窗口ID 放在第一行保險(xiǎn)  因?yàn)楹竺嬉泊蜷_了新窗口使得獲取的ID有問題
    let winId = BrowserWindow.getFocusedWindow().id

    // 調(diào)用window打開新窗口
    win = new BrowserWindow({
        width: 400,
        height: 300,
    });
    win.loadURL(path.join('file:',__dirname, '../news.html'));

    // 新開窗口調(diào)試模式
    win.webContents.openDevTools()

  

    // 把渲染進(jìn)程傳遞過來的數(shù)據(jù)再次傳遞給渲染進(jìn)程news
    // 等待窗口加載完
    win.webContents.on('did-finish-load', ()=>[
        win.webContents.send('toNews', userInfo, winId)
    ])
    

    win.on('closed', () => {
        win = null;
      });
})

2. 在news進(jìn)程中廣播數(shù)據(jù)

// src/render/news.js

var { ipcRenderer } = require('electron');

// 注意這里 在渲染進(jìn)程中需要從remote中獲取BrowserWindow
const BrowerWindow = require('electron').remote.BrowserWindow;

// let username = localStorage.getItem('username')
// console.log(username)

// 監(jiān)聽主進(jìn)程傳遞過來的數(shù)據(jù) 
ipcRenderer.on('toNews',(e, userInfo, winId)=>{
    // windID 第一個(gè)窗口ID
    // 獲取對應(yīng)ID的窗口
    let firstWin = BrowerWindow.fromId(winId)
    firstWin.webContents.send('toIndex', '來自news進(jìn)程反饋的信息')
    console.log(userInfo)
})

3. 在另一個(gè)渲染進(jìn)程中處理廣播

/* eslint-disable */
let openWindow = document.querySelector('#openWindow');

var { ipcRenderer } = require('electron');

// 渲染進(jìn)程和渲染進(jìn)程直接的通信========
openWindow.onclick = function () {
    // 傳遞消息給主進(jìn)程
    ipcRenderer.send('openwindow', {name:'poetries', age: 23})

    // 傳遞給打開的窗口 渲染進(jìn)程和渲染進(jìn)程直接的通信
    localStorage.setItem('username', 'poetries')
    
}

// 接收news渲染進(jìn)程傳遞回來的消息
ipcRenderer.on('toIndex', (e, data)=>{
    console.log('===', data)
})
image.png

七虐译、Electron Shell 模塊

image.png

7.1 Shell 模塊使用

文檔 https://electronjs.org/docs/api/shell

Electron Shell 模塊在用戶默認(rèn)瀏覽器 中打開 URL 以及 Electron DOM webview 標(biāo)簽瘪板。Shell既屬于主進(jìn)程模塊又是渲染進(jìn)程模塊

shell 模塊提供了集成其他桌面客戶端的關(guān)聯(lián)功能

1. 引入

<!--index.html-->
<button id="shellDom">通過shell打開外部鏈接</button>
<script src="render/shell.js"></script>

2. shell.js

// src/render/shell.js

const { shell } = require('electron')
let shellDom = document.querySelector('#shellDom');

shellDom.onclick = function (e) {
   shell.openExternal('https://github.com/poetries')
}

7.2 Electron DOM <webview> 標(biāo)簽

Webviewiframe 有點(diǎn)相似,但是與 iframe 不同, webview 和你的應(yīng)用運(yùn)行的是不同的進(jìn)程漆诽。它不擁有渲染進(jìn)程的權(quán)限侮攀,并且應(yīng)用和嵌入內(nèi)容之間的交互全部都是異步的。因?yàn)檫@能 保證應(yīng)用的安全性不受嵌入內(nèi)容的影響厢拭。

<!--src/index.html中引入-->
<webview id="webview" src="http://blog.poetries.top" style="position:fixed; width:100%; height:100%">
</webview>

7.3 shell模塊<webview>結(jié)合Menu模塊使用案例

1. 新建src/render/webview.js

/* eslint-disable */
var { ipcRenderer } = require('electron');
let myWebview = document.querySelector('#myWebview')

ipcRenderer.on('openwebview', (e, url)=>{
    myWebview.src = url
})

2. 引入src/index.html

<webview id="myWebview" src="http://blog.poetries.top" style="position:fixed; width:100%; height:100%">
</webview>
    
<script src="render/webview.js"></script>

3. 新建src/main/menu.js

/* eslint-disable */
const { shell, Menu, BrowserWindow } = require('electron');

// 當(dāng)前窗口渲染網(wǎng)頁
function openWebView(url) {
    // 獲取當(dāng)前窗口Id
    let win = BrowserWindow.getFocusedWindow()

    // 廣播通知渲染進(jìn)程打開webview
    win.webContents.send('openwebview', url)
}

// 在窗口外打開網(wǎng)頁
function openWeb(url) {
    shell.openExternal(url)
}

let template = [
    {
        label: '幫助',
        submenu: [
            {
                label: '關(guān)于我們',
                click: function () {
                    openWeb('http://blog.poetries.top')
                }
            },
            {
                type: 'separator'
            },
            {
                label: '聯(lián)系我們',
                click: function () {
                    openWeb('https://github.com/poetries')
                }
            }
        ]
    },
   {
        label: '加載網(wǎng)頁',
        submenu: [
            {
                label: '博客',
                click: function () {
                    openWebView('http://blog.poetries.top')
                }
            },
            {
                type: 'separator' // 分隔符
            },
            {
                label: 'GitHub',
                click: function () {
                    openWebView('https://github.com/poetries')
                }
            },
            {
                type: 'separator' // 分隔符
            },
            {
                label: '簡書',
                click: function () {
                    openWebView('http://www.reibang.com/users/94077fcddfc0/timeline')
                }
            }
        ]
   },
   {
    label: '視頻網(wǎng)站',
    submenu: [
        {
            label: '優(yōu)酷',
            click: function () {
                openWebView('https://www.youku.com')
            }
        },
        {
            type: 'separator' // 分隔符
        },
        {
            label: '愛奇藝',
            click: function () {
                openWebView('https://www.iqiyi.com/')
            }
        },
        {
            type: 'separator' // 分隔符
        },
        {
            label: '騰訊視頻',
            click: function () {
                openWebView('https://v.qq.com/')
            }
        }
    ]
    }
]

let m = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(m)

4. 引入menu

// 在主進(jìn)程src/index.js中引入
const createWindow = () => {

  // 創(chuàng)建菜單  
  // 引入菜單模塊
  require('./main/menu.js')
};
image.png

八兰英、Electron dialog 彈出框

image.png

文檔 https://electronjs.org/docs/api/dialog

dialog屬于主進(jìn)程中的模塊

dialog 模塊提供了 api 來展示原生的系統(tǒng)對話框,例如打開文件框供鸠,alert 框畦贸, 所以 web 應(yīng)用可以給用戶帶來跟系統(tǒng)應(yīng)用相同的體驗(yàn)

1. 在src/index.html中引入

<button id="showError">showError</button><br />
<button id="showMsg">showMsg</button><br />
<button id="showOpenDialog">showOpenDialog</button><br />
<button id="saveDialog">saveDialog</button><br />

<script src="render/dialog.js"></script>

2. 新建render/dialog.js

// render/dialog.js

let showError = document.querySelector('#showError');
let showMsg = document.querySelector('#showMsg');
let showOpenDialog = document.querySelector('#showOpenDialog');
let saveDialog = document.querySelector('#saveDialog');

var {remote} = require('electron')

showError.onclick = function () {
    remote.dialog.showErrorBox('警告', '操作有誤')
}
showMsg.onclick = function () {
    remote.dialog.showMessageBox({
        type: 'info',
        title: '提示信息',
        message: '內(nèi)容',
        buttons: ['確定', '取消']
    },function(index){
        console.log(index)
    })
}
showOpenDialog.onclick = function () {
    remote.dialog.showOpenDialog({
        // 打開文件夾
        properties: ['openDirectory', 'openFile']

        // 打開文件
        // properties: ['openFile']
    }, function (data) {
        console.log(data)
    })
}
saveDialog.onclick = function () {
    remote.dialog.showSaveDialog({
        title: 'Save File',
        defaultPath: '/Users/poetry/Downloads/',
        // filters 指定一個(gè)文件類型數(shù)組,用于規(guī)定用戶可見或可選的特定類型范圍
        filters: [
            { name: 'Images', extensions: ['jpg', 'png', 'gif'] },
            { name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
            { name: 'Custom File Type', extensions: ['as'] },
            { name: 'All Files', extensions: ['*'] }
        ]
    }, function (path) {
        // 不是真的保存 楞捂,具體還需nodejs處理
        console.log(path)
    })
}

showError

remote.dialog.showErrorBox('警告', '操作有誤')
image.png

showMessageBox

remote.dialog.showMessageBox({
    type: 'info',
    title: '提示信息',
    message: '內(nèi)容',
    buttons: ['確定', '取消']
},function(index){
    console.log(index)
})
image.png

showOpenDialog

remote.dialog.showOpenDialog({
    // 打開文件夾
    properties: ['openDirectory', 'openFile']

    // 打開文件
    // properties: ['openFile']
}, function (data) {
    console.log(data)
})
image.png

showSaveDialog

remote.dialog.showSaveDialog({
    title: 'Save File',
    defaultPath: '/Users/poetry/Downloads/',
    // filters 指定一個(gè)文件類型數(shù)組薄坏,用于規(guī)定用戶可見或可選的特定類型范圍
    filters: [
        { name: 'Images', extensions: ['jpg', 'png', 'gif'] },
        { name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
        { name: 'Custom File Type', extensions: ['as'] },
        { name: 'All Files', extensions: ['*'] }
    ]
}, function (path) {
    // 不是真的保存 趋厉,具體還需nodejs處理
    console.log(path)
})
image.png

九、實(shí)現(xiàn)一個(gè)類似EditPlus的簡易記事本代碼編輯器

代碼 https://github.com/poetries/electron-demo/tree/master/notepad

十胶坠、系統(tǒng)托盤君账、托盤右鍵菜單、托盤圖標(biāo)閃爍

image.png

文檔 https://electronjs.org/docs/api/tray

系統(tǒng)托盤涵但,托盤右鍵菜單杈绸、托盤圖標(biāo)閃爍 點(diǎn)擊右上角關(guān)閉按鈕隱藏到托盤(仿殺毒軟件)

1. 引入文件

// src/index.js
const createWindow = () => {
    require('./main/tray.js')
};

2. Electron 創(chuàng)建任務(wù)欄圖標(biāo)以及任務(wù)欄圖標(biāo)右鍵菜單

// src/main/tray.js
var {
    Menu, Tray, app, BrowserWindow
} = require('electron');

const path = require('path');

var appIcon = new Tray(path.join(__dirname, '../static/lover.png'));

const menu = Menu.buildFromTemplate([
    {
        label: '設(shè)置',
        click: function() {} //打開相應(yīng)頁面 
    },
    {
        label: '幫助',
        click: function() {}
    },
    {
        label: '關(guān)于',
        click: function() {}
    },
    {
        label: '退出',
        click: function() { 
            // BrowserWindow.getFocusedWindow().webContents().send('close-main-window');
            app.quit();
    }
}])
// 鼠標(biāo)放上去提示信息
appIcon.setToolTip('hello poetries');
appIcon.setContextMenu(menu);
mac系統(tǒng)托盤

3. 監(jiān)聽任務(wù)欄圖標(biāo)的單擊帖蔓、雙擊事件

// 實(shí)現(xiàn)點(diǎn)擊關(guān)閉按鈕矮瘟,讓應(yīng)用保存在托盤里面,雙擊托盤打開
let win = BrowserWindow.getFocusedWindow()

win.on('close', (e)=>{
    e.preventDefault()
    win.hide()
})

iconTray.on('double-click', (e)=>{
    win.show()
})

4. Electron 點(diǎn)擊右上角關(guān)閉按鈕隱藏任務(wù)欄圖標(biāo)

const win = BrowserWindow.getFocusedWindow();

win.on('close', (e) =>{

    console.log(win.isFocused());
    
    if (!win.isFocused()) {
        win = null;
    } else {
        e.preventDefault();/*阻止應(yīng)用退出*/
        win.hide();/*隱藏當(dāng)前窗口*/
    }
})

5. Electron 實(shí)現(xiàn)任務(wù)欄閃爍圖標(biāo)

var appIcon = new Tray(path.join(__dirname, '../static/lover.png'));

timer = setInterval(function() {
    count++;
    if (count % 2 == 0) {
        appIcon.setImage(path.join(__dirname, '../static/empty.ico'))
    } else {
        appIcon.setImage(path.join(__dirname, '../static/lover.png'))
    }
},
500);

十一塑娇、消息通知澈侠、監(jiān)聽網(wǎng)絡(luò)變 化、網(wǎng)絡(luò)變化彈出通知框

11.1 消息通知

1. Electron 實(shí)現(xiàn)消息通知

Electron 里面的消息通知是基于 h5 的通知 api 實(shí)現(xiàn)的

文檔 https://developer.mozilla.org/zh-CN/docs/Web/API/notification

1. 新建notification.js

// h5api實(shí)現(xiàn)通知
const path = require('path')

let options = {
    title: 'electron 通知API',
    body: 'hello poetries',
    icon: path.join('../static/img/favicon2.ico') // 通知圖標(biāo)
}


document.querySelector('#showNotification').onclick = function () {
    let myNotification  = new window.Notification(options.title, options)
    
    // 消息可點(diǎn)擊
    myNotification.onclick = function () {
        console.log('click notification')
    }
}

2. 引入

<!--src/index.html-->

<button id="showNotification">彈出消息通知</button>
<script src="render/notification.js"></script>

mac上的消息通知

mac上的消息通知

11.2 監(jiān)聽網(wǎng)絡(luò)變化

1. 基本使用

 // 監(jiān)聽網(wǎng)絡(luò)變化
// 端開網(wǎng)絡(luò) 再次連接測試
 window.addEventListener('online', function(){
    console.log('online')
 }); 
 
 window.addEventListener('offline', function(){
    console.log('offline')
 });

2. 監(jiān)聽網(wǎng)絡(luò)變化實(shí)現(xiàn)消息通知

// 端開網(wǎng)絡(luò) 再次連接測試
// 監(jiān)聽網(wǎng)絡(luò)變化實(shí)現(xiàn)消息通知
window.addEventListener('online', function(){
   console.log('online')
}); 
window.addEventListener('offline', function(){
   // 斷開網(wǎng)絡(luò)觸發(fā)事件
   var options = {
       title: 'QQ郵箱',
       body: '網(wǎng)絡(luò)異常埋酬,請檢查你的網(wǎng)絡(luò)',
       icon: path.join('../static/img/favicon2.ico') // 通知圖標(biāo)
   }
   var myNotification  = new window.Notification(options.title, options)
   myNotification.onclick = function () {
       console.log('click notification')
   }
});
image.png

十二哨啃、注冊全局快捷鍵/剪切板事件/nativeImage 模塊

Electron 注冊全局快捷鍵 (globalShortcut) 以及 clipboard 剪 切板事件以及 nativeImage 模塊(實(shí)現(xiàn)類似播放器點(diǎn)擊機(jī)器碼自動復(fù)制功 能)

12.1 注冊全局快捷鍵

image.png

1. 新建src/main/shortCut.js

const {globalShortcut, app} = require('electron')

app.on('ready', ()=>{
    // 注冊全局快捷鍵
    globalShortcut.register('command+e', ()=>{
        console.log(1)
    })

    // 檢測快捷鍵是否注冊成功 true是注冊成功
    let isRegister = globalShortcut.isRegistered('command+e')
    console.log(isRegister)
})

// 退出的時(shí)候取消全局快捷鍵
app.on('will-quit', ()=>{
    globalShortcut.unregister('command+e')
})

2. 引入src/index.js

// 注意在外部引入即可 不用放到app中
require('./main/shortCut.js')

12.2 剪切板clipboard、nativeImage 模塊

image.png

1. html

<!--src/index.html-->
<div>
  <h2>雙擊下面信息復(fù)制</h2>
  <p id='msg'>123456789</p>
  <button id="plat">粘貼</button><br />
  <input id="text" type="text"/>
</div>.
<div>
  <h2>復(fù)制圖片到界面</h2>
  <button id="copyImg">復(fù)制圖片</button><br />
</div>
<script src="render/clipboard.js"></script>

2. 新建src/render/clipboard.js

// clipboard可以在主進(jìn)程或渲染進(jìn)程使用
const { clipboard, nativeImage }  = require('electron')

//復(fù)制
// 運(yùn)行ctrl+v可看到復(fù)制的內(nèi)容
// clipboard.writeText('poetries')

// clipboard.readText() //獲取復(fù)制的內(nèi)容 粘貼

// 雙擊復(fù)制消息
let msg = document.querySelector('#msg')
let plat = document.querySelector('#plat')
let text = document.querySelector('#text')

msg.ondblclick  = function () {
    clipboard.writeText(msg.innerHTML)
    alert(msg.innerHTML)
}
plat.onclick = function () {
    text.value = clipboard.readText()
}

// 復(fù)制圖片顯示到界面
let copyImg = document.querySelector('#copyImg')
copyImg.onclick = function () {
    // 結(jié)合nativeImage模塊
    let image = nativeImage.createFromPath('../static/img/lover.png') 

    // 復(fù)制圖片
    clipboard.writeImage(image)

    // 粘貼圖片
    let imgSrc = clipboard.readImage().toDataURL() // base64圖片

    // 顯示到頁面上
    let imgDom = new Image()
    imgDom.src = imgSrc 
    document.body.appendChild(imgDom)
}

十三写妥、結(jié)合electron-vue

13.1 electron-vue 的使用

1. electron-vue 的一些資源

https://github.com/SimulatedGREG/electron-vue

Electron-vue 文檔 https://simulatedgreg.gitbooks.io/electron-vue/content/cn

2. electron-vue 環(huán)境搭建拳球、創(chuàng)建項(xiàng)目

npm install -g vue-cli

vue init simulatedgreg/electron-vue my-project

cd my-project

yarn # or npm install

yarn run dev # or npm run dev

3. electron-vue 目錄結(jié)構(gòu)分析

image.png

13.2 electron-vue 中使用 sass/ElementUi

1. electron-vue UI 框架 ElementUi 的使用

http://element-cn.eleme.io/#/zh-CN

2. electron-vue 中使用 sass

# 安裝 sass-loader:

npm install --save-dev sass-loader node-sass
<!--vue 文件中修改 style 為如下代碼:-->

<style lang="scss"> 
    body {
        /* SCSS */ 
    }
</style>

13.3 electron-vue 中隱藏頂部菜單隱藏

electron-vue 中隱藏頂部菜單隱藏頂部最大化、最小化珍特、關(guān)閉按鈕 自定最大化祝峻、最小化 、關(guān)閉按鈕

1. electron-vue 中隱藏頂部菜單

// src/main/index.js
mainWindow.setMenu(null)

2. electron-vue 中隱藏關(guān)閉 最大化 最小化按鈕

// src/main/index.js
mainWindow = new BrowserWindow({
    height: 620,
    useContentSize: true,
    width: 1280,
    frame: false /*去掉頂部導(dǎo)航 去掉關(guān)閉按鈕 最大化最小化按鈕*/
})

3 .electron-vue 自定義關(guān)閉/最大化最小化按鈕

// 注意在mac下不需要監(jiān)聽窗口最大最小化扎筒、以為系統(tǒng)默認(rèn)支持莱找,這個(gè)只是針對windows平臺

ipc.on('window-min',function() {
    mainWindow.minimize();
})

//登錄窗口最大化 
ipc.on('window-max',function(){
    if (mainWindow.isMaximized()) {
        mainWindow.restore();
    } else {
        mainWindow.maximize();
    }
}) 

ipc.on('window-close',function() {
    mainWindow.close();
})

4. electron-vue 自定義導(dǎo)航可拖拽

  • 可拖拽的 css: -webkit-app-region: drag;
  • 不可拖拽的 css: -webkit-app-region: no-drag;

13.4 使用electron-vue開發(fā)輿情監(jiān)控系統(tǒng)

13.4.1 配置開發(fā)環(huán)境

1. 項(xiàng)目搭建

npm install -g vue-cli

vue init simulatedgreg/electron-vue my-project

cd my-project

yarn # or npm install

yarn run dev # or npm run dev

2. 安裝一些依賴

# 安裝 sass-loader:
npm install --save-dev sass-loader node-sass

# 安裝elementUI、js-md5
npm i element-ui  js-md5 -S
  • .electron-vue/webpack.renderer.config.js中配置sass-loader就可以編寫``sass`了
<!--vue 文件中修改 style 為如下代碼:-->

<style lang="scss"> 
    body {
        /* SCSS */ 
    }
</style>

13.4.2 主進(jìn)程配置

1. src/main/index.js

function createWindow () {
  // 去掉頂部菜單
  mainWindow.setMenu(null)
  
  // 菜單項(xiàng)
  require('./model/menu.js');
  
  // 系統(tǒng)托盤相關(guān)
  require('./model/tray.js');

2. src/main/menu.js菜單配置

const { Menu,ipcMain,BrowserWindow} = require('electron');


//右鍵菜單
const contextMenuTemplate=[
    {
        label: '復(fù)制', role: 'copy' },
    {
        label: '黏貼', role: 'paste' },        
    { type: 'separator' }, //分隔線
    {
        label: '其他功能',     
        click: () => {
        console.log('click')
         }
    }
];

const contextMenu=Menu.buildFromTemplate(contextMenuTemplate);


ipcMain.on('contextmenu',function(){

    contextMenu.popup(BrowserWindow.getFocusedWindow());

})

3. src/main/tray.js系統(tǒng)托盤配置

托盤點(diǎn)擊監(jiān)聽事件只有在windows下才生效嗜桌,mac系統(tǒng)默認(rèn)支持

(function () {
    const path=require('path');
    const {app,Menu,BrowserWindow,Tray, shell} = require('electron');

    //創(chuàng)建系統(tǒng)托盤
    const tray = new Tray(path.resolve(__static, 'favicon.png'))

    //給托盤增加右鍵菜單
    const template= [
        {
            label: '設(shè)置',
            click: function () {
                shell.openExternal('http://blog.poetries.top')
            }
        },
        {
            label: '幫助',
            click: function () {
                shell.openExternal('http://blog.poetries.top/2019/01/06/electron-summary')
            }
        },
        {
            label: '關(guān)于',
            click: function () {
                shell.openExternal('https://github.com/poetries/yuqing-monitor-electron')
            }
        },
        {
            label: '退出',
            click: function () {
                // BrowserWindow.getFocusedWindow().webContents().send('close-main-window');
                app.quit();
            
            }
        }
    ];

    const menu = Menu.buildFromTemplate(template);
    tray.setContextMenu(menu);


    tray.setToolTip('輿情監(jiān)控系統(tǒng)');


    //監(jiān)聽關(guān)閉事件隱藏到系統(tǒng)托盤
    // 這里需要注意:在window中才生效奥溺,mac下系統(tǒng)默認(rèn)支持
    // var win = BrowserWindow.getFocusedWindow();
    // win.on('close',(e)=>{
    //         if(!win.isFocused()){
    //             win=null;
    //         }else{
    //             e.preventDefault();  /*阻止應(yīng)用退出*/

    //             win.hide(); /*隱藏當(dāng)前窗口*/

    //         }       
    // })

    // //監(jiān)聽托盤的雙擊事件
    // tray.on('double-click',()=>{               
    //     win.show();
    // })
})()

4. src/main/shortCut.js快捷鍵配置

src/main/index.js中引入(require('src/main/shortCut.js'))即可,不需要放到app監(jiān)控中

var {globalShortcut, app} = require('electron')

app.on('ready', ()=>{
    // 注冊全局快捷鍵
    globalShortcut.register('command+e', ()=>{
        console.log(1)
    })

    // 檢測快捷鍵是否注冊成功 true是注冊成功
    let isRegister = globalShortcut.isRegistered('command+e')
    console.log(isRegister)
})

// 退出的時(shí)候取消全局快捷鍵
app.on('will-quit', ()=>{
    globalShortcut.unregister('command+e')
})

13.4.3 渲染進(jìn)程配置

1. src/render/main.js配置

import Vue from 'vue'
import axios from 'axios'

import App from './App'
import router from './router'
import store from './store'

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import VueHighcharts from 'vue-highcharts';
import VueSocketIO from 'vue-socket.io'

Vue.use(ElementUI);
Vue.use(VueHighcharts);

//引入socket.io配置連接
Vue.use(new VueSocketIO({
  debug: true,
  connection: 'http://118.123.14.36:3000',
  vuex: {
      store,
      actionPrefix: 'SOCKET_',
      mutationPrefix: 'SOCKET_'
  }
}))

if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
Vue.http = Vue.prototype.$http = axios
Vue.config.productionTip = false


/* eslint-disable no-new */
new Vue({
  components: { App },
  router,
  store,
  template: '<App/>'
}).$mount('#app')

2. 路由配置src/renderer/router/index.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/home',
      name: 'home',
      component: require('@/components/Home').default
    },
    {
      path: '/report',
      name: 'report',
      component: require('@/components/Report').default
    },
    {
      path: '/negativereport',
      name: 'negativereport',
      component: require('@/components/NegativeReport').default
    },
    {
      path: '/positivereport',
      name: 'positivereport',
      component: require('@/components/PositiveReport').default
    },
    {
      path: '/keyword',
      name: 'keyword',
      component: require('@/components/KeyWord').default
    },
    {
      path: '/alarm',
      name: 'alarm',
      component: require('@/components/Alarm').default
    },
    {
      path: '/msg',
      name: 'msg',
      component: require('@/components/Msg').default
    },
    {
      path: '*',
      redirect: '/home'
    }
  ]
})

其他頁面更多詳情Github

3. 在渲染進(jìn)程中使用主進(jìn)程方式

// electron掛載到了vue實(shí)例上 $electron
this.$electron.shell

13.4.4 多平臺打包

需要注意的是打包mac版本在mac系統(tǒng)上打包骨宠,打包window則在windows上打包浮定,可以避免很多問題

# 在不同平臺上執(zhí)行即可打包應(yīng)用
npm run build

13.4.4.1 打包介紹

electron-vue打包文檔

1. electron 中構(gòu)建應(yīng)用最常用的模塊

  • electron-packager
  • electron-builder

electron-packagerelectron-builder在自己單獨(dú)創(chuàng)建的應(yīng)用用也可以完成打包功 能。但是由于配置太復(fù)雜所以我們不建議單獨(dú)配置

2. electron-forge

https://github.com/electron-userland/electron-forge

electron-forge package 
electron-forge make

3. electron-vue中的打包方式

# https://simulatedgreg.gitbooks.io/electron-vue/content/cn/using-electron-packager. html
# 之需要執(zhí)行一條命令
npm run build

13.4.4.2 修改應(yīng)用信息

1. 修改package.json

image.png

2. 修改src/index.ejs標(biāo)題信息

3. 修改build/icons圖標(biāo)

13.4.4.3 打包遇到的問題

1. 創(chuàng)建應(yīng)用托盤的時(shí)候可能會遇到錯誤

  • 把托盤圖片放在根目錄static里面层亿,然后注意下面寫法桦卒。
var tray = new Tray(path.join(__static,'favicon.ico'))
  • 如果托盤路徑?jīng)]有問題,還是包托盤相關(guān)錯誤的話棕所,把托盤對應(yīng)的圖片換成.png 格式重試

2. 模塊問題可能會遇到的錯誤

image.png
image.png

解決辦法

  • 刪掉 node_modules 然后重新用 npm install 安裝依賴
  • yarn 來安裝模塊
  • 用手機(jī)創(chuàng)建一個(gè)熱點(diǎn)電腦連上熱點(diǎn)重試

最后執(zhí)行yarn run build即可

項(xiàng)目打包結(jié)果

項(xiàng)目截圖

輿情監(jiān)控系統(tǒng)頁面

登錄頁
首頁
全部輿情
輿情關(guān)鍵詞
增加關(guān)鍵詞
輿情報(bào)警設(shè)置

系統(tǒng)系統(tǒng)托盤闸盔、electron 消息通知 (類似騰訊新聞)

系統(tǒng)托盤
消息通知

項(xiàng)目源碼 https://github.com/poetries/yuqing-monitor-electron

十四、更多參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迎吵,一起剝皮案震驚了整個(gè)濱河市躲撰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌击费,老刑警劉巖拢蛋,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蔫巩,居然都是意外死亡谆棱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門圆仔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垃瞧,“玉大人,你說我怎么就攤上這事坪郭「龃樱” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵歪沃,是天一觀的道長嗦锐。 經(jīng)常有香客問我,道長沪曙,這世上最難降的妖魔是什么奕污? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮液走,結(jié)果婚禮上碳默,老公的妹妹穿的比我還像新娘。我一直安慰自己育灸,他們只是感情好腻窒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著磅崭,像睡著了一般儿子。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上砸喻,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天柔逼,我揣著相機(jī)與錄音,去河邊找鬼割岛。 笑死愉适,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的癣漆。 我是一名探鬼主播维咸,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了癌蓖?” 一聲冷哼從身側(cè)響起瞬哼,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎租副,沒想到半個(gè)月后坐慰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡用僧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年结胀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片责循。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡糟港,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沼死,到底是詐尸還是另有隱情着逐,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布意蛀,位于F島的核電站,受9級特大地震影響健芭,放射性物質(zhì)發(fā)生泄漏县钥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一慈迈、第九天 我趴在偏房一處隱蔽的房頂上張望若贮。 院中可真熱鬧,春花似錦痒留、人聲如沸谴麦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匾效。三九已至,卻和暖如春恤磷,著一層夾襖步出監(jiān)牢的瞬間面哼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工扫步, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留魔策,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓河胎,卻偏偏與公主長得像闯袒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353