今年の初めに、Githubは有名なオープンソースエディターのコアであるAtom-Shellをリリースしました 原子 、名前をに変更しました 電子 特別な日のために。
Electronは、Node.jsベースのデスクトップアプリケーションのカテゴリの他の競合他社とは異なり、Node.jsのパワーを組み合わせることで、このすでに確立された市場に独自のひねりをもたらします( io.js 最近のリリースまで) Chromiumエンジン サーバー側とクライアント側の両方のJavaScriptの長所を提供します。
増え続けるNPMモジュールのリポジトリだけでなく、Bowerレジストリ全体を利用して、パフォーマンスの高いデータ駆動型のクロスプラットフォームデスクトップアプリケーションを構築し、クライアント側のすべてのニーズを満たすことができる世界を想像してみてください。
入る 電子 。
このチュートリアルでは、Electron、Angular.js、およびを使用して簡単なパスワードキーチェーンアプリケーションを構築します。 Loki.js 、おなじみの構文を備えた軽量でメモリ内のデータベース MongoDB開発者 。
このアプリケーションの完全なソースコードが利用可能です ここに 。
このチュートリアルでは、次のことを前提としています。
まず最初に、アプリをローカルでテストするためにElectronバイナリを取得する必要があります。グローバルにインストールしてCLIとして使用することも、アプリケーションのパスにローカルにインストールすることもできます。グローバルにインストールすることをお勧めします。そうすれば、開発するすべてのアプリで何度もインストールする必要がなくなります。
Gulpを使用して配布用にアプリケーションをパッケージ化する方法については後で学習します。このプロセスにはElectronバイナリのコピーが含まれるため、アプリケーションのパスに手動でインストールすることはほとんど意味がありません。
Electron CLIをインストールするには、ターミナルで次のコマンドを入力します。
$ npm install -g electron-prebuilt
インストールをテストするには、electron -h
と入力しますElectronCLIのバージョンが表示されます。
この記事が書かれた時点では、Electronのバージョンは0.31.2
でした。
次の基本的なフォルダ構造を想定しましょう。
my-app |- cache/ |- dist/ |- src/ |-- app.js | gulpfile.js
… どこ: - キャッシュ/ アプリのビルド時にElectronバイナリをダウンロードするために使用されます。 - 距離/ 生成された配布ファイルが含まれます。 - src / ソースコードが含まれます。 - src / app.js アプリケーションのエントリポイントになります。
次に、src/
に移動しますターミナルにフォルダを作成し、package.json
を作成しますおよびbower.json
私たちのアプリのファイル:
$ npm init $ bower init
このチュートリアルの後半で、必要なパッケージをインストールします。
電子は、2つのタイプのプロセスを区別します。
コードをわかりやすくするために、レンダラープロセスごとに個別のファイルを使用する必要があります。アプリのメインプロセスを定義するには、
src/app.js
を開きます。app
を含めますアプリを起動するモジュール、およびbrowser-window
アプリのさまざまなウィンドウ(両方ともElectronコアの一部)を作成するモジュール。
var app = require('app'), BrowserWindow = require('browser-window');
アプリが実際に起動されると、ready
が起動します。バインドできるイベント。この時点で、アプリのメインウィンドウをインスタンス化できます。
var mainWindow = null; app.on('ready', function() { mainWindow = new BrowserWindow({ width: 1024, height: 768 }); mainWindow.loadUrl('file://' + __dirname + '/windows/main/main.html'); mainWindow.openDevTools(); });
キーポイント:
BrowserWindow
の新しいインスタンスを作成して、新しいウィンドウを作成します。オブジェクト。loadUrl()
がありますメソッド。現在のウィンドウに実際のHTMLファイルのコンテンツをロードできます。 HTMLファイルは次のいずれかになります。 地元 または リモート 。openDevTools()
がありますメソッド。デバッグ目的で、現在のウィンドウでChrome開発ツールのインスタンスを開くことができます。次に、コードを少し整理する必要があります。 windows/
を作成することをお勧めしますsrc/
のフォルダフォルダー、および各ウィンドウのサブフォルダーを作成できる場所。
my-app |- src/ |-- windows/ |--- main/ |---- main.controller.js |---- main.html |---- main.view.js
…ここでmain.controller.js
アプリケーションの「サーバー側」ロジックとmain.view.js
が含まれますアプリケーションの「クライアント側」ロジックが含まれます。
main.html
ファイルは単純にHTML5Webページであるため、次のように簡単に開始できます。
Password Keychain
この時点で、アプリを実行する準備ができているはずです。テストするには、ターミナルのsrc
のルートに次のように入力するだけです。フォルダ:
$ electron .
start
を定義することで、このプロセスを自動化できます。 package.sonファイルのスクリプト。
パスワードキーチェーンアプリケーションを構築するには、次のものが必要です。-パスワードを追加、生成、および保存する方法。 -パスワードをコピーおよび削除する便利な方法。
新しいパスワードを挿入するには、単純なフォームで十分です。 Electronの複数のウィンドウ間の通信を示すために、アプリケーションに2番目のウィンドウを追加することから始めます。これにより、「挿入」フォームが表示されます。このウィンドウは何度も開いたり閉じたりするので、必要なときに簡単に呼び出せるように、ロジックをメソッドでラップする必要があります。
function createInsertWindow() { insertWindow = new BrowserWindow({ width: 640, height: 480, show: false }); insertWindow.loadUrl('file://' + __dirname + '/windows/insert/insert.html'); insertWindow.on('closed',function() { insertWindow = null; }); }
キーポイント:
アイデアは、エンドユーザーが「メイン」ウィンドウのボタンをクリックしたときに「挿入」ウィンドウをトリガーできるようにすることです。これを行うには、メインウィンドウからメインプロセスにメッセージを送信して、挿入ウィンドウを開くように指示する必要があります。これは、ElectronのIPCモジュールを使用して実現できます。 IPCモジュールには実際には2つのバリエーションがあります。
Electronの通信チャネルはほとんど単方向ですが、レンダラープロセスでメインプロセスのIPCモジュールにアクセスするには、 リモート モジュール。また、メインプロセスは、イベントの発生元であるレンダラープロセスにメッセージを送り返すことができます。 Event.sender.send() 方法。
IPCモジュールを使用するには、メインプロセススクリプトの他のNPMモジュールと同じようにIPCモジュールが必要です。
var ipc = require('ipc');
…そしてon()
でイベントにバインドします方法:
ipc.on('toggle-insert-view', function() { if(!insertWindow) { createInsertWindow(); } return (!insertWindow.isClosed() && insertWindow.isVisible()) ? insertWindow.hide() : insertWindow.show(); });
キーポイント:
closed
にあるかどうかに関係なく、ブール値を返します。状態。次に、実際にレンダラープロセスからそのイベントを発生させる必要があります。 main.view.js
という名前の新しいスクリプトファイルを作成し、通常のスクリプトの場合と同じようにHTMLページに追加します。
script
HTML経由でスクリプトファイルをロードする
window.
tagは、このファイルをクライアント側のコンテキストでロードします。これは、たとえば、グローバル変数がrequire()
を介して利用できることを意味します。サーバー側のコンテキストでスクリプトをロードするには、require('./main.controller.js');
を使用できます。 HTMLページで直接メソッド:var ipc = require('ipc'); angular .module('Utils', []) .directive('toggleInsertView', function() { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); ipc.send('toggle-insert-view'); }); }; });
。
スクリプトがロードされていても クライアント側 コンテキストでは、メインプロセスの場合と同じ方法でレンダラープロセスのIPCモジュールにアクセスし、次のようにイベントを送信できます。
add
イベントを同期的に送信する必要がある場合に備えて、sendSync()メソッドも使用できます。
これで、「挿入」ウィンドウを開くためにあとは、一致するAngularディレクティブを含むHTMLボタンを作成するだけです。
angular .module('MainWindow', ['Utils']) .controller('MainCtrl', function() { var vm = this; });
そして、そのディレクティブをメインウィンドウのAngularコントローラーの依存関係として追加します。
uuid
物事を単純にするために、NPMを使用することができますvar uuid = require('uuid'); angular .module('Utils', []) ... .factory('Generator', function() { return { create: function() { return uuid.v4(); } }; })
このチュートリアルの目的でパスワードとして機能する一意のIDを生成するモジュール。他のNPMモジュールと同じようにインストールし、「Utils」スクリプトでそれを要求してから、一意のIDを返す単純なファクトリを作成できます。
generate
これで、挿入ビューにボタンを作成し、ボタンのクリックイベントをリッスンしてcreate()メソッドを呼び出すディレクティブをアタッチするだけです。
// in Utils.js angular .module('Utils', []) ... .directive('generatePassword', ['Generator', function(Generator) { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); if(!scope.vm.formData) scope.vm.formData = {}; scope.vm.formData.password = Generator.create(); scope.$apply(); }); }; }])
{ 'id': String 'description': String, 'username': String, 'password': String }
この時点で、パスワードを保存します。パスワードエントリのデータ構造は非常に単純です。
Description... Username... Password... generate cancel save
したがって、本当に必要なのは、バックアップのためにオプションでファイルに同期できる、ある種のインメモリデータベースです。この目的のために、Loki.jsは理想的な候補のようです。これは、このアプリケーションの目的に必要なことを正確に実行し、その上に 動的ビュー この機能により、MongoDBのAggregationモジュールと同様のことが可能になります。
動的ビューは、MongodDBの集約モジュールが提供するすべての機能を提供するわけではありません。を参照してください ドキュメンテーション 詳細については。
簡単なHTMLフォームを作成することから始めましょう。
var loki = require('lokijs'), path = require('path'); angular .module('Utils', []) ... .service('Storage', ['$q', function($q) { this.db = new loki(path.resolve(__dirname, '../..', 'app.db')); this.collection = null; this.loaded = false; this.init = function() { var d = $q.defer(); this.reload() .then(function() { this.collection = this.db.getCollection('keychain'); d.resolve(this); }.bind(this)) .catch(function(e) { // create collection this.db.addCollection('keychain'); // save and create file this.db.saveDatabase(); this.collection = this.db.getCollection('keychain'); d.resolve(this); }.bind(this)); return d.promise; }; this.addDoc = function(data) { var d = $q.defer(); if(this.isLoaded() && this.getCollection()) { this.getCollection().insert(data); this.db.saveDatabase(); d.resolve(this.getCollection()); } else { d.reject(new Error('DB NOT READY')); } return d.promise; }; }) .directive('savePassword', ['Storage', function(Storage) { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); if(scope.vm.formData) { Storage .addDoc(scope.vm.formData) .then(function() { // reset form & close insert window scope.vm.formData = {}; ipc.send('toggle-insert-view'); }); } }); }; }])
それでは、フォームのコンテンツの投稿と保存を処理するJavaScriptロジックを追加しましょう。
getCollection()
キーポイント:
insert()
を使用して、データベース内の特定のコレクションを取得できます。方法。saveDatabase()
を含むいくつかのメソッドを公開しますメソッド。コレクションに新しいドキュメントを追加できます。getCollection()
を公開します。方法。これで、新しいパスワードを生成して保存できる簡単なフォームができました。メインビューに戻って、これらのエントリを一覧表示しましょう。
ここでいくつかのことが起こる必要があります:
this.getCollection = function() { this.collection = this.db.getCollection('keychain'); return this.collection; }; this.getDocs = function() { return (this.getCollection()) ? this.getCollection().data : null; };
を呼び出すと、ドキュメントのリストを取得できます。 Lokiオブジェクトのメソッド。このメソッドは、というプロパティを持つオブジェクトを返します データ 、これは単にそのコレクション内のすべてのドキュメントの配列です。
angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); }); });
次に、AngularコントローラーでgetDocs()を呼び出し、データベースを初期化した後、データベースに保存されているすべてのパスワードを取得できます。
{{item.description}} {item.username } • copy remove
少しAngularテンプレートを作成し、パスワードリストを用意しました。
main.view.js
追加された優れた機能は、新しいパスワードを挿入した後にパスワードのリストを更新することです。このために、ElectronのIPCモジュールを使用できます。前述のように、メインプロセスのIPCモジュールは、リモートモジュールを使用して、レンダラープロセスで呼び出し、リスナープロセスに変えることができます。 var remote = require('remote'), remoteIpc = remote.require('ipc'); angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); remoteIpc.on('update-main-view', function() { Storage .reload() .then(function() { vm.keychain = db.getDocs(); }); }); }); }]);
で実装する方法の例を次に示します。
require()
キーポイント:
on()
を介してリモートモジュールを使用する必要がありますメインプロセスからリモートIPCモジュールを要求する方法。Storage .addDoc(scope.vm.formData) .then(function() { // refresh list in main view ipc.send('update-main-view'); // reset form & close insert window scope.vm.formData = {}; ipc.send('toggle-insert-view'); });
を介してレンダラープロセスをイベントリスナーとして設定できます。メソッド、およびコールバック関数をこれらのイベントにバインドします。挿入ビューは、新しいドキュメントが保存されるたびにこのイベントのディスパッチを担当します。
var clipboard = require('clipboard'); angular .module('Utils', []) ... .directive('copyPassword', [function() { return function(scope, el, attrs) { el.bind('click', function(e) { e.preventDefault(); var text = (scope.vm.keychain[attrs.copyPassword]) ? scope.vm.keychain[attrs.copyPassword].password : ''; // atom's clipboard module clipboard.clear(); clipboard.writeText(text); }); }; }]);
通常、パスワードをプレーンテキストで表示することはお勧めできません。代わりに、エンドユーザーが特定のエントリのパスワードを直接コピーできるようにする便利なボタンを非表示にして提供します。
請負業者のレートを計算する方法
ここでも、Electronは私たちに クリップボード テキストコンテンツだけでなく、画像やHTMLコードもコピーして貼り付ける簡単な方法を備えたモジュール:
writeText()
生成されるパスワードは単純な文字列になるため、copy-password
を使用できます。パスワードをシステムのクリップボードにコピーする方法。次に、メインビューのHTMLを更新し、 copy
を使用してコピーボタンを追加できます。パスワードの配列のインデックスを提供するディレクティブ:
remove()
エンドユーザーは、パスワードが陳腐化した場合に備えて、パスワードを削除できるようにすることもできます。これを行うには、this.removeDoc = function(doc) { return function() { var d = $q.defer(); if(this.isLoaded() && this.getCollection()) { // remove the doc from the collection & persist changes this.getCollection().remove(doc); this.db.saveDatabase(); // inform the insert view that the db content has changed ipc.send('reload-insert-view'); d.resolve(true); } else { d.reject(new Error('DB NOT READY')); } return d.promise; }.bind(this); };
を呼び出すだけです。キーチェーンコレクションのメソッド。ドキュメント全体を「remove()」メソッドに提供する必要があります。
var remote = require('remote'), Menu = remote.require('menu');
Loki.jsのドキュメントには、IDでドキュメントを削除することもできると記載されていますが、期待どおりに機能していないようです。
ElectronはOSデスクトップ環境とシームレスに統合され、アプリに「ネイティブ」なユーザーエクスペリエンスのルックアンドフィールを提供します。したがって、Electronには メニューモジュール 、アプリの複雑なデスクトップメニュー構造の作成に専念しています。
メニューモジュールは広大なトピックであり、ほとんど独自のチュートリアルに値します。よくお読みになることを強くお勧めします Electronのデスクトップ環境統合チュートリアル このモジュールのすべての機能を発見します。
この現在のチュートリアルの範囲では、カスタムメニューを作成し、それにカスタムコマンドを追加し、標準のquitコマンドを実装する方法を説明します。
通常、ElectronメニューのJavaScriptロジックは、メインプロセスが定義されているアプリのメインスクリプトファイルに属します。ただし、それを別のファイルに抽象化し、リモートモジュールを介してメニューモジュールにアクセスすることはできます。
buildFromTemplate()
簡単なメニューを定義するには、var appMenu = Menu.buildFromTemplate([ { label: 'Electron', submenu: [{ label: 'Credits', click: function() { alert('Built with Electron & Loki.js.'); } }] } ]);
を使用する必要があります方法:
label
配列の最初の項目は、常に「デフォルト」メニュー項目として使用されます。
Electron
の値デフォルトのメニュー項目では、プロパティはそれほど重要ではありません。開発モードでは、常にsetApplicationMenu()
が表示されます。ビルドフェーズでデフォルトのメニュー項目にカスタム名を割り当てる方法については、後で説明します。
最後に、このカスタムメニューをアプリのデフォルトメニューとしてMenu.setApplicationMenu(appMenu);
で割り当てる必要があります。方法:
Command+A
電子は「 アクセラレータ 」、実際のキーボードの組み合わせにマップされる事前定義された文字列のセット。例:Ctrl+Shift+Z
またはCommand
。
File
アクセラレータはWindowsまたはLinuxでは機能しません。パスワードキーチェーンアプリケーションでは、... { label: 'File', submenu: [ { label: 'Create Password', accelerator: 'CmdOrCtrl+N', click: function() { ipc.send('toggle-insert-view'); } }, { type: 'separator' // to create a visual separator }, { label: 'Quit', accelerator: 'CmdOrCtrl+Q', selector: 'terminate:' // OS X only!!! } ] } ...
を追加する必要があります2つのコマンドを提供するメニュー項目:
type
キーポイント:
separator
を使用して配列に項目を追加することにより、視覚的な区切り文字を追加できます。プロパティをCmdOrCtrl
に設定します。selector
アクセラレータはMacとPCの両方のキーボードと互換性がありますmdl-
プロパティはOSX互換のみです!さまざまなコード例で、gulp electron
で始まるクラス名への参照に気付いたと思います。このチュートリアルの目的のために、私はを使用することを選択しました マテリアルデザインライト UIフレームワークですが、お好きなUIフレームワークを自由に使用してください。
HTML5でできることはすべて、Electronで行うことができます。アプリのバイナリのサイズが大きくなり、サードパーティのライブラリを使いすぎるとパフォーマンスの問題が発生する可能性があることに注意してください。
あなたはElectronアプリを作りました、それは素晴らしく見えます、あなたはあなたのe2eテストをで書きました SeleniumとWebDriver 、そしてあなたはそれを世界に配布する準備ができています!
ただし、それでもパーソナライズし、デフォルトの「Electron」以外のカスタム名を付け、MacプラットフォームとPCプラットフォームの両方にカスタムアプリケーションアイコンを提供することもできます。
最近、 gulp 私たちが考えることができるすべてのプラグイン。私がしなければならなかったのはタイプ$ npm install gulp-electron --save-dev
だけですグーグルで、そして確かにあります gulp-electron プラグイン!
このプラグインはかなりです 使いやすい このチュートリアルの冒頭で詳述したフォルダー構造が維持されている限り。そうでない場合は、物事を少し動かす必要があるかもしれません。
このプラグインは、他のGulpプラグインと同じようにインストールできます。
var gulp = require('gulp'), electron = require('gulp-electron'), info = require('./src/package.json'); gulp.task('electron', function() { gulp.src('') .pipe(electron({ src: './src', packageJson: info, release: './dist', cache: './cache', version: 'v0.31.2', packaging: true, platforms: ['win32-ia32', 'darwin-x64'], platformResources: { darwin: { CFBundleDisplayName: info.name, CFBundleIdentifier: info.bundle, CFBundleName: info.name, CFBundleVersion: info.version }, win: { 'version-string': info.version, 'file-version': info.version, 'product-version': info.version } } })) .pipe(gulp.dest('')); });
そして、Gulpタスクを次のように定義できます。
src/
キーポイント:
platforms
フォルダーは、Gulpfile.jsが存在するフォルダーと同じにすることも、配布フォルダーと同じフォルダーにすることもできません。cache
を介してエクスポート先のプラットフォームを定義できますアレイ。packageJson
を定義する必要がありますフォルダー。Electronバイナリがダウンロードされ、アプリにパッケージ化できます。packaging
を介してgulpタスクに渡す必要があります。プロパティ。platformResources
がありますプロパティ。生成されたアプリのzipアーカイブも作成できます。icon
の1つプロパティは'icon': 'keychain.ico'
ですプロパティ。アプリのカスタムアイコンを定義できます。
.icns
OS Xには、
.png
のアイコンが必要ですファイル拡張子。変換を可能にする複数のオンラインツールがあります.ico
ファイルを.icns
におよび|_+_|無料で。
この記事では、Electronが実際に実行できることのほんの一部にすぎません。 Atomやのような素晴らしいアプリを考えてください スラック このツールを使用できるインスピレーションの源として。
このチュートリアルがお役に立てば幸いです。コメントを残して、Electronでの経験を共有してください。