apeescape2.com
  • メイン
  • デザイナーライフ
  • モバイルデザイン
  • プロセスとツール
  • 人とチーム
技術

Electron:クロスプラットフォームのデスクトップアプリが簡単に

今年の初めに、Githubは有名なオープンソースエディターのコアであるAtom-Shellをリリースしました 原子 、名前をに変更しました 電子 特別な日のために。

Electronは、Node.jsベースのデスクトップアプリケーションのカテゴリの他の競合他社とは異なり、Node.jsのパワーを組み合わせることで、このすでに確立された市場に独自のひねりをもたらします( io.js 最近のリリースまで) Chromiumエンジン サーバー側とクライアント側の両方のJavaScriptの長所を提供します。

増え続けるNPMモジュールのリポジトリだけでなく、Bowerレジストリ全体を利用して、パフォーマンスの高いデータ駆動型のクロスプラットフォームデスクトップアプリケーションを構築し、クライアント側のすべてのニーズを満たすことができる世界を想像してみてください。



入る 電子 。

Electronを使用したクロスプラットフォームデスクトップアプリの構築

Electronを使用したクロスプラットフォームデスクトップアプリの構築 つぶやき

このチュートリアルでは、Electron、Angular.js、およびを使用して簡単なパスワードキーチェーンアプリケーションを構築します。 Loki.js 、おなじみの構文を備えた軽量でメモリ内のデータベース MongoDB開発者 。

このアプリケーションの完全なソースコードが利用可能です ここに 。

このチュートリアルでは、次のことを前提としています。

  • リーダーには、Node.jsとBowerがマシンにインストールされています。
  • 彼らはNode.js、Angular.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つのタイプのプロセスを区別します。

  • メインプロセス :アプリケーションのエントリポイント。アプリを実行するたびに実行されるファイル。通常、このファイルはアプリのさまざまなウィンドウを宣言し、オプションでElectronのIPCモジュールを使用してグローバルイベントリスナーを定義するために使用できます。
  • レンダラープロセス :アプリケーションの特定のウィンドウのコントローラー。各ウィンドウは、独自のレンダラープロセスを作成します。

コードをわかりやすくするために、レンダラープロセスごとに個別のファイルを使用する必要があります。アプリのメインプロセスを定義するには、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; }); }

キーポイント:

  • を設定する必要があります 公演 プロパティに false BrowserWindowコンストラクターのoptionsオブジェクトで、アプリケーションの起動時にデフォルトでウィンドウが開かないようにします。
  • ウィンドウが起動するたびにBrowserWindowインスタンスを破棄する必要があります 閉まっている イベント。

「挿入」ウィンドウの開閉

アイデアは、エンドユーザーが「メイン」ウィンドウのボタンをクリックしたときに「挿入」ウィンドウをトリガーできるようにすることです。これを行うには、メインウィンドウからメインプロセスにメッセージを送信して、挿入ウィンドウを開くように指示する必要があります。これは、ElectronのIPCモジュールを使用して実現できます。 IPCモジュールには実際には2つのバリエーションがあります。

  • 1つは メインプロセス 、アプリがWindowsから送信されたメッセージをサブスクライブできるようにします。
  • 1つは レンダラープロセス 、アプリがメインプロセスにメッセージを送信できるようにします。

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(); });

キーポイント:

  • イベントには好きな名前を付けることができます。例は任意です。
  • BrowserWindowインスタンスがすでに作成されているかどうかを確認することを忘れないでください。作成されていない場合は、インスタンス化してください。
  • BrowserWindowインスタンスには、いくつかの便利なメソッドがあります。
    • 閉じています() ウィンドウが現在closedにあるかどうかに関係なく、ブール値を返します。状態。
    • isVisible() :ウィンドウが現在表示されているかどうかに関係なく、ブール値を返します。
    • show()/ hide() :ウィンドウを表示および非表示にする便利なメソッド。

次に、実際にレンダラープロセスからそのイベントを発生させる必要があります。 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()

キーポイント:

  • まず、データベースを初期化する必要があります。このプロセスでは、Lokiオブジェクトの新しいインスタンスを作成し、引数としてデータベースファイルへのパスを指定し、そのバックアップファイルが存在するかどうかを調べ、必要に応じて作成し( 'Keychain'コレクションを含む)、のコンテンツをロードします。メモリ内のこのファイル。
  • insert()を使用して、データベース内の特定のコレクションを取得できます。方法。
  • コレクションオブジェクトは、saveDatabase()を含むいくつかのメソッドを公開しますメソッド。コレクションに新しいドキュメントを追加できます。
  • データベースの内容をファイルに永続化するために、LokiオブジェクトはgetCollection()を公開します。方法。
  • フォームのデータをリセットし、IPCイベントを送信して、ドキュメントが保存されたらウィンドウを閉じるようにメインプロセスに指示する必要があります。

これで、新しいパスワードを生成して保存できる簡単なフォームができました。メインビューに戻って、これらのエントリを一覧表示しましょう。

パスワードの一覧表示

ここでいくつかのことが起こる必要があります:

  • コレクション内のすべてのドキュメントを取得できる必要があります。
  • ビューを更新できるように、新しいパスワードが保存されるたびにメインビューに通知する必要があります。

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つのコマンドを提供するメニュー項目:

  • パスワードの作成 :挿入ビューを開く Cmd(またはCtrl)+ N
  • 終了する :アプリを完全に終了します Cmd(またはCtrl)+ Q
type

キーポイント:

  • separatorを使用して配列に項目を追加することにより、視覚的な区切り文字を追加できます。プロパティをCmdOrCtrlに設定します。
  • selectorアクセラレータはMacとPCの両方のキーボードと互換性があります
  • mdl-プロパティはOSX互換のみです!

アプリのスタイリング

さまざまなコード例で、gulp electronで始まるクラス名への参照に気付いたと思います。このチュートリアルの目的のために、私はを使用することを選択しました マテリアルデザインライト UIフレームワークですが、お好きなUIフレームワークを自由に使用してください。

HTML5でできることはすべて、Electronで行うことができます。アプリのバイナリのサイズが大きくなり、サードパーティのライブラリを使いすぎるとパフォーマンスの問題が発生する可能性があることに注意してください。

配布用のElectronアプリのパッケージ化

あなたはElectronアプリを作りました、それは素晴らしく見えます、あなたはあなたのe2eテストをで書きました SeleniumとWebDriver 、そしてあなたはそれを世界に配布する準備ができています!

ただし、それでもパーソナライズし、デフォルトの「Electron」以外のカスタム名を付け、MacプラットフォームとPCプラットフォームの両方にカスタムアプリケーションアイコンを提供することもできます。

Gulpで構築する

最近、 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バイナリがダウンロードされ、アプリにパッケージ化できます。
  • アプリのpackage.jsonファイルの内容は、packagingを介してgulpタスクに渡す必要があります。プロパティ。
  • オプションのplatformResourcesがありますプロパティ。生成されたアプリのzipアーカイブも作成できます。
  • プラットフォームごとに、異なる「プラットフォームリソース」のセットがあります。 定義することができます 。

アプリアイコンの追加

iconの1つプロパティは'icon': 'keychain.ico' ですプロパティ。アプリのカスタムアイコンを定義できます。

.icns

OS Xには、.pngのアイコンが必要ですファイル拡張子。変換を可能にする複数のオンラインツールがあります.icoファイルを.icnsにおよび

|_+_|
無料で。

結論

この記事では、Electronが実際に実行できることのほんの一部にすぎません。 Atomやのような素晴らしいアプリを考えてください スラック このツールを使用できるインスピレーションの源として。

このチュートリアルがお役に立てば幸いです。コメントを残して、Electronでの経験を共有してください。

ブランディングとは何ですか?すべてがブランディングです。

ブランドデザイン

ブランディングとは何ですか?すべてがブランディングです。
Laravelゼロダウンタイム展開

Laravelゼロダウンタイム展開

技術

人気の投稿
Javaでのリモートフレームバッファサーバーの実装
Javaでのリモートフレームバッファサーバーの実装
ApeeScapeの迅速で実用的なCSSチートシート
ApeeScapeの迅速で実用的なCSSチートシート
ソフトウェアデプロイメントの強化-DockerSwarmチュートリアル
ソフトウェアデプロイメントの強化-DockerSwarmチュートリアル
エクイティクラウドファンディングの次は?
エクイティクラウドファンディングの次は?
AngularJSからReactに切り替えた理由
AngularJSからReactに切り替えた理由
 
パーム油への投資家向けガイド
パーム油への投資家向けガイド
RADフレームワークのエンジニアリング内部... NookuのPHP開発者として
RADフレームワークのエンジニアリング内部... NookuのPHP開発者として
Ro-Luを見る:ユーザビリティのケーススタディ
Ro-Luを見る:ユーザビリティのケーススタディ
ブラウザからウェブサイトのデザインプロセスにアプローチする
ブラウザからウェブサイトのデザインプロセスにアプローチする
意味のあるデザインと楽しいUXの芸術
意味のあるデザインと楽しいUXの芸術
人気の投稿
  • 複雑なプロセスやアイデアを示すのに適したデータ図のタイプはどれですか?
  • SQLServerクエリ最適化のヒント
  • AC法人と法人としての違いは何ですか
  • cとc ++とは何ですか
  • sボディvscボディ
  • c corp vs s corp
カテゴリー
収益性と効率性 プロセスとツール 財務プロセス トレンド 設計プロセス 技術 投資家と資金調達 バックエンド 人とチーム ブランドデザイン

© 2021 | 全著作権所有

apeescape2.com