电竞比分网-中国电竞赛事及体育赛事平台

分享

使用Angular與TypeScript構(gòu)建Electron應用

 看見就非常 2020-01-31

回顧請前往第一節(jié)
本文所有代碼都可以在github找到。你可以通過commit歷史來查看這些代碼是如何一步一步構(gòu)建的。如果有任何問題,也可以在github的issue上提出。

接前文,現(xiàn)在我們搭建好了一系列的環(huán)境,創(chuàng)建了一些初始的代碼,是時候開始工作了。
在這篇文章中我們主要負責創(chuàng)建登錄界面與主界面,涉及篇幅關(guān)系我們不再使用遠程服務端來交互,而是創(chuàng)建一些模擬的登錄請求,當然,與服務端的交互方法可以在此系列文章后面幾篇找到。OK,這里我們希望前端能夠像QQ或微信一樣,先展示一個登錄界面,在登錄成功后帶領(lǐng)我們打開一個長時間停留的主界面,我們先理清需要做那幾件事:

  1. 在Angular中創(chuàng)建路由,包括登錄界面與主界面。

  2. 創(chuàng)建browser相關(guān)代碼,給登錄與跳轉(zhuǎn)提供通信反饋。

  3. 在登錄成功后我們關(guān)閉登錄界面跳轉(zhuǎn)至主界面。

Angular創(chuàng)建前端頁面

由于我們安裝了angular-cli,所以每次創(chuàng)建各類文件時都可以通過cli的方式來解決,這很方便,也降低了Angular的學習成本,如果對此不明白,可以看這里的文檔

1, 創(chuàng)建組件與路由

首先在src/app的路徑下創(chuàng)建2個組件: login與main。好吧,你需要輸入ng g component login來創(chuàng)建這個組件,但在之后我們就不再討論這些細節(jié),我只會告訴該怎么做一件事。

其次我們在src/app的路徑下創(chuàng)建一個路由app.routing.ts,我們希望它可以做好兩件事,根據(jù)URL進行頁面的導航,在沒有權(quán)限時對相應的導航進行保護。具體代碼可以參照Angular的官方文檔,但我猜你們懶得看,代碼如下:

import {NgModule} from '@angular/core'import {Routes, RouterModule} from '@angular/router'import {LoginComponent} from './login/login.component'import {MainComponent} from './main/main.component'export const appRoutes: Routes = [ {path: '', component: LoginComponent}, {path: 'login', component: LoginComponent}, {path: 'main', component: MainComponent}]@NgModule({ imports: [RouterModule.forRoot(appRoutes)], exports: [RouterModule]})export class AppRoutingModule {}

ok,這很簡單,和我們熟悉的Angular1.x或react-route也沒有太大區(qū)別。但是要讓路由運行起來還要做兩件事,第一是將路由在app.module.ts中注冊,在module上掛載文件,Angular在編譯時才會將文件引入進來,第二是在app.component.html中增加路由插座。

#### 2, 創(chuàng)建樣式與邏輯

現(xiàn)在,我們?yōu)榍岸隧撁嫣砑右恍邮脚c邏輯,這此的commit記錄在這里,現(xiàn)在我們需要為登錄界面添加邏輯與路由保護。

登錄頁面樣式

登錄可以提交用戶名與密碼用作驗證,這時候可以借助Angular的模板語法來快速的完成它們:

   <div class='input-box'>           <input type='text' #username>   </div>   <div class='input-box'>           <input type='text' #password>   </div>   <button (click)='login(username.value, password.value)'>登錄</button>

我們希望所有嚴格的邏輯或涉及數(shù)據(jù)庫的問題都放在主進程解決,那么確認登錄需要與electron主進程進行交互,以便于主進程來切換窗口。當然,在實際業(yè)務中你可以選擇把服務器的交互放在Angular中來做,也可以在electron發(fā)起一個request?,F(xiàn)在我們按下面幾步來操作:

  1. 在login組件文件夾下創(chuàng)建login.service.ts,別忘了將服務添加到組件的providers依賴項中!

  2. src/index.html文件中添加var electron = require('electron'),別忘了script標簽。

  3. src/app下添加shared文件夾,用來存放一些共用的組件與邏輯。在這里創(chuàng)建一個名為ipc-renderer的服務,并將它注冊到app.component.ts中。具體代碼如下:

import {Injectable} from '@angular/core declare let electron:any; @Injectable() export class IpcRendererService { constructor (){} private ipcRenderer = electron.ipcRenderer on (message: string, done){ return this.ipcRenderer.on(message, done); } send (message: string, ...args){ this.ipcRenderer.send(message, args); } api (action: string, ...args) { this.ipcRenderer.send('api', action, ...args); return new Promise((resolve, reject) => { this.ipcRenderer.once(`${action}reply`, (e, reply, status) =>{ if (!reply){ return reject(status) } return resolve(reply) }) }) } dialog (action: string, ...args) { this.ipcRenderer.send('dialog', action, ...args); } sendSync (message: string, ...args){ return this.ipcRenderer.sendSync(message, arguments); } }

這里我們通過ipcRenderer與electron交互,ipc-renderer就是Angular中用來通信的公共服務,這個服務模塊理論上共享的,而且我們也只希望它被實例化一次,所以將它注入在app.component.ts中。這樣每次子組件需要服務時不必在providers中標明它,而是直接在constructor中注入即可。這很重要,特別是你想要在一個類中保存一些即時的數(shù)據(jù)信息,希望只存在一個實例用來共享時很有用。
可以看出來,api這個方法是我們增加的一個有意思的方法,這里我們可以作出一些參數(shù)上的約定,便于監(jiān)聽事件時做出更好的反饋。

#### 3, 監(jiān)聽與反饋

這時,api的第一個參數(shù)被約定為action,用于描述這個API事件的用途,每一個API事件都會發(fā)起一次apiName+reply的事件用于回復。在Angular的公共服務中,我們不妨先把它轉(zhuǎn)化為我們熟悉的Promise,再返回給每一個具體的組件服務,當然你也可以直接把它用作做fromEvent的Observable,但在這里,我們希望它看起來像是一個http服務,便于大家更好的理解它們工作的方式。
實際上,你可以選擇一些成熟electron數(shù)據(jù)通信庫或框架來解決這些復雜的問題,但在第一次請不要這樣做,這就像上手使用Rails一樣,雖然做的很快,但對你并沒有多少益處。

這里有一些復雜,如果你希望對照當時的代碼來學習,可以看這一次的commit。

ok,大家也可以想象的到,現(xiàn)在要做的是在electron中新建一個事件接收器,處理一些邏輯并且將它們返回,在根文件夾下新建browser/ipc/index.js并且填充基礎(chǔ)的代碼:

const {ipcMain} = require('electron')const api = require('./api')ipcMain.on('api', (event, actionName, ...args) =>{    const reply = (replayObj, status = 'success') =>{        event.sender.send(`${actionName}reply`, replayObj, status);    }    if (api[actionName]){        api[actionName](Object.assign({reply: reply}, event), ...args)    }})

假設(shè)現(xiàn)在有一個browser/ipc/api文件作為處理器,以上代碼做的事情即是確定一個Action,并且監(jiān)聽事件,為event合并一個名為reply的方法,用于返回數(shù)據(jù)。根據(jù)此,我們再創(chuàng)建這個虛擬的browser/ipc/api文件:

module.exports = { login: (e, user) =>{ // todo something e.reply({msg: 'ok'}) }}

怎么樣?現(xiàn)在看起來一切都完成了!每次當我們在loginService中調(diào)用this.ipcRendererService.api時,相應的數(shù)據(jù)就會被傳達至對應的事件(看起來它更像一個路由)上,我們在nodejs環(huán)境中做一些操作,比如儲存session,更新數(shù)據(jù)庫,抓取新聞,向遠程服務器發(fā)送一條信息等等。

最關(guān)鍵的是我們也能用輕而易舉的方式來得到想要的數(shù)據(jù),回復數(shù)據(jù)也足夠簡單,e.reply({msg: 'ok'})就像是express中的res.xxx({});一樣,整個項目也變得層次分明。等到有一天我們需要下載、上傳、顯示系統(tǒng)原生提示框、讀取一個文件等等之類的功能時,只需要將Action名替換一下,在api文件夾下新增幾段邏輯即可。

這一小節(jié)文章有些瑣碎和復雜,登錄成功與跳轉(zhuǎn)等等邏輯不妨放在下一節(jié)中再講。大家可以嘗試閱讀github的源碼,考慮它有哪些問題是值得優(yōu)化的。在后面幾節(jié)中,我們再來討論如何優(yōu)化這些邏輯。

注:原文首次載于維特博客。需要演示項目請看這里

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多