摘要:原文的框架以前叫做允許你使用和編寫跨平臺(tái)的桌面應(yīng)用。這個(gè)教程向我們展示了如何使用和構(gòu)建一個(gè)桌面應(yīng)用。我們的應(yīng)用看起來會(huì)是這個(gè)樣子配置開發(fā)環(huán)境是微軟的一款跨平臺(tái)代碼編輯器。是基于和微軟自身的開發(fā)的。我們需要用我們最終構(gòu)建的應(yīng)用來替換它。
原文:Creating Desktop Applications With AngularJS and GitHub Electron
GitHub 的 Electron 框架(以前叫做 Atom Shell)允許你使用 HTML, CSS 和 JavaScript 編寫跨平臺(tái)的桌面應(yīng)用。它是 io.js 運(yùn)行時(shí)的衍生,專注于桌面應(yīng)用而不是 web 服務(wù)端。
Electron 豐富的原生 API 使我們能夠在頁面中直接使用 JavaScript 獲取原生的內(nèi)容。
這個(gè)教程向我們展示了如何使用 Angular 和 Electron 構(gòu)建一個(gè)桌面應(yīng)用。下面是本教程的所有步驟:
創(chuàng)建一個(gè)簡(jiǎn)單的 Electron 應(yīng)用
使用 Visual Studio Code 編輯器管理我們的項(xiàng)目和任務(wù)
使用 Electron 開發(fā)(原文為 Integrate)一個(gè) Angular 顧客管理應(yīng)用(Angular Customer Manager App)
使用 Gulp 任務(wù)構(gòu)建我們的應(yīng)用,并生成安裝包
創(chuàng)建你的 Electron 應(yīng)用起初,如果你的系統(tǒng)中還沒有安裝 Node,你需要先安裝它。我們應(yīng)用的結(jié)構(gòu)如下所示:
這個(gè)項(xiàng)目中有兩個(gè) package.json 文件。
開發(fā)使用
項(xiàng)目根目錄下的 package.json 包含你的配置,開發(fā)環(huán)境的依賴和構(gòu)建腳本。這些依賴和 package.json 文件不會(huì)被打包到生產(chǎn)環(huán)境構(gòu)建中。
應(yīng)用使用
app 目錄下的 package.json 是你應(yīng)用的清單文件。因此每當(dāng)在你需要為你項(xiàng)目安裝 npm 依賴的時(shí)候,你應(yīng)該依照這個(gè) package.json 來進(jìn)行安裝。
package.json 的格式和 Node 模塊中的完全一致。你應(yīng)用的啟動(dòng)腳本(的路徑)需要在 app/package.json 中的 main 屬性中指定。
app/package.json 看起來是這樣的:
{ name: "AngularElectron", version: "0.0.0", main: "main.js" }
過執(zhí)行 npm init 命令分別創(chuàng)建這兩個(gè) package.json 文件,也可以手動(dòng)創(chuàng)建它們。通過在命令提示行里鍵入以下命令來安裝項(xiàng)目打包必要的 npm 依賴:
npm install --save-dev electron-prebuilt fs-jetpack asar rcedit Q創(chuàng)建啟動(dòng)腳本
app/main.js 是我們應(yīng)用的入口。它負(fù)責(zé)創(chuàng)建主窗口和處理系統(tǒng)事件。main.js 應(yīng)該如下所示:
// app/main.js // 應(yīng)用的控制模塊 var app = require("app"); // 創(chuàng)建原生瀏覽器窗口的模塊 var BrowserWindow = require("browser-window"); var mainWindow = null; // 當(dāng)所有窗口都關(guān)閉的時(shí)候退出應(yīng)用 app.on("window-all-closed", function () { if (process.platform != "darwin") { app.quit(); } }); // 當(dāng) Electron 結(jié)束的時(shí)候,這個(gè)方法將會(huì)生效 // 初始化并準(zhǔn)備創(chuàng)建瀏覽器窗口 app.on("ready", function () { // 創(chuàng)建瀏覽器窗口. mainWindow = new BrowserWindow({ width: 800, height: 600 }); // 載入應(yīng)用的 index.html mainWindow.loadUrl("file://" + __dirname + "/index.html"); // 打開開發(fā)工具 // mainWindow.openDevTools(); // 窗口關(guān)閉時(shí)觸發(fā) mainWindow.on("closed", function () { // 想要取消窗口對(duì)象的引用,如果你的應(yīng)用支持多窗口, // 通常你需要將所有的窗口對(duì)象存儲(chǔ)到一個(gè)數(shù)組中, // 在這個(gè)時(shí)候你應(yīng)該刪除相應(yīng)的元素 mainWindow = null; }); });通過 DOM 訪問原生
正如我上面提到的那樣,Electron 使你能夠直接在 web 頁面中訪問本地 npm 模塊和原生 API。你可以這樣創(chuàng)建 app/index.html 文件:
Hello World!
We are using Electron
app/index.html 是一個(gè)簡(jiǎn)單的 HTML 頁面。在這里,它通過使用 Node’s fs (file system) 模塊來讀取 package.json 文件并將其內(nèi)容寫入到 document body 中。
運(yùn)行應(yīng)用一旦你創(chuàng)建好了項(xiàng)目結(jié)構(gòu)、app/index.html、app/main.js 和 app/package.json,你很可能想要嘗試去運(yùn)行初始的 Electron 應(yīng)用來測(cè)試并確保它正常工作。
如果你已經(jīng)在系統(tǒng)中全局安裝了 electron-prebuilt,就可以通過下面的命令啟動(dòng)應(yīng)用:
electron app
在這里,electron 是運(yùn)行 electron shell 的命令,app 是我們應(yīng)用的目錄名。如果你不想將 Election 安裝到你全局的 npm 模塊中,可以在命令提示行中通過下面命令使用本地 npm_modules 文件夾下的 electron 來啟動(dòng)應(yīng)用。
"node_modules/.bin/electron" "./app"
盡管你可以這樣來運(yùn)行應(yīng)用,但是我還是建議你在 gulpfile.js 中創(chuàng)建一個(gè) gulp task,這樣你就可以將你的任務(wù)和 Visual Studio Code 編輯器相結(jié)合,我們會(huì)在下一部分展示。
// 獲取依賴 var gulp = require("gulp"), childProcess = require("child_process"), electron = require("electron-prebuilt"); // 創(chuàng)建 gulp 任務(wù) gulp.task("run", function () { childProcess.spawn(electron, ["./app"], { stdio: "inherit" }); });
運(yùn)行你的 gulp 任務(wù):gulp run。我們的應(yīng)用看起來會(huì)是這個(gè)樣子:
配置 Visual Studio Code 開發(fā)環(huán)境Visual Studio Code 是微軟的一款跨平臺(tái)代碼編輯器。VS Code 是基于 Electron 和 微軟自身的 Monaco Code Editor 開發(fā)的。你可以在這里下載到 Visual Studio Code。
在 VS Code 中打開你的 electron 應(yīng)用。
配置 Visual Studio Code Task Runner有很多自動(dòng)化的工具,像構(gòu)建、打包和測(cè)試等。我們大多從命令行中運(yùn)行這些工具。VS Code task runner 使你能夠?qū)⒛阕远x的任務(wù)集成到項(xiàng)目中。你可以在你的項(xiàng)目中直接運(yùn)行 grunt,、gulp,、MsBuild 或者其他任務(wù),這并不需要移步到命令行。
VS Code 能夠自動(dòng)檢測(cè)你的 grunt 和 gulp 任務(wù)。按下 ctrl + shift + p 然后鍵入 Run Task 敲擊回車便可。
你將從 gulpfile.js 或 gruntfile.js 文件中獲取所有有效的任務(wù)。
注意:你需要確保 gulpfile.js 文件存在于你應(yīng)用的根目錄下。
ctrl + shift + b 會(huì)從你任務(wù)執(zhí)行器(task runner)中執(zhí)行 build 任務(wù)。你可以使用 task.json 文件來覆蓋任務(wù)集成。按下 ctrl + shift + p 然后鍵入 Configure Task 敲擊回車。這將會(huì)在你項(xiàng)目中創(chuàng)建一個(gè) .setting 的文件夾和 task.json 文件。要是你不止想要執(zhí)行簡(jiǎn)單的任務(wù),你需要在 task.json 中進(jìn)行配置。例如你或許想要通過按下 Ctrl + Shift + B 來運(yùn)行應(yīng)用,你可以這樣編輯 task.json 文件:
{ "version": "0.1.0", "command": "gulp", "isShellCommand": true, "args": [ "--no-color" ], "tasks": [ { "taskName": "run", "args": [], "isBuildCommand": true } ] }
根部分聲明命令為 gulp。你可以在 tasks 部分寫入你想要的更多任務(wù)。將一個(gè)任務(wù)的 isBuildCommand 設(shè)置為 true 意味著它和 Ctrl + Shift + B 進(jìn)行了綁定。目前 VS Code 只支持一個(gè)頂級(jí)任務(wù)。
現(xiàn)在,如果你按下 Ctrl + Shift + B,gulp run 將會(huì)被執(zhí)行。
你可以在這里閱讀到更多關(guān)于 visual studio code 任務(wù)的信息。
調(diào)試 Electron 應(yīng)用打開調(diào)試面板點(diǎn)擊配置按鈕就會(huì)在 .settings 文件夾內(nèi)創(chuàng)建一個(gè) launch.json 文件,包含了調(diào)試的配置。
我們不需要啟動(dòng) app.js 的配置,所以移除它。
現(xiàn)在,你的 launch.json 應(yīng)該如下所示:
{ "version": "0.1.0", // 配置列表。添加新的配置或更改已存在的配置。 // 僅支持 "node" 和 "mono",可以改變 "type" 來進(jìn)行切換。 "configurations": [ { "name": "Attach", "type": "node", // TCP/IP 地址. 默認(rèn)是 "localhost" "address": "localhost", // 建立連接的端口. "port": 5858, "sourceMaps": false } ] }
按照下面所示更改之前創(chuàng)建的 gulp run 任務(wù),這樣我們的 electron 將會(huì)采用調(diào)試模式運(yùn)行,5858 端口也會(huì)被監(jiān)聽。
gulp.task("run", function () { childProcess.spawn(electron, ["--debug=5858","./app"], { stdio: "inherit" }); });
在調(diào)試面板中選擇 “Attach” 配置項(xiàng),點(diǎn)擊開始(run)或者按下 F5。稍等片刻后你應(yīng)該就能在上部看到調(diào)試命令面板。
創(chuàng)建 AngularJS 應(yīng)用第一次接觸 AngularJS?瀏覽官方網(wǎng)站或一些 Scotch Angular 教程。
這一部分會(huì)講解如何使用 AngularJS 和 MySQL 數(shù)據(jù)庫創(chuàng)建一個(gè)顧客管理(Customer Manager)應(yīng)用。這個(gè)應(yīng)用的目的不是為了強(qiáng)調(diào) AngularJS 的核心概念,而是展示如何在 GiHub 的 Electron 中同時(shí)使用 AngularJS 和 NodeJS 以及 MySQL 。
我們的顧客管理應(yīng)用正如下面這樣簡(jiǎn)單:
顧客列表
添加新顧客
選擇刪除一個(gè)顧客
搜索指定的顧客
項(xiàng)目結(jié)構(gòu)我們的應(yīng)用在 app 文件夾下,目錄結(jié)構(gòu)如下所示:
主頁是 app/index.html 文件。app/scripts 文件夾包含所有用在該應(yīng)用中的關(guān)鍵腳本和視圖。有許多方法可以用來組織應(yīng)用的文件。
這里我更喜歡按照功能來組織腳本文件。每個(gè)功能都有它自己的文件夾,文件夾中有模板和控制器。獲取更多關(guān)于目錄結(jié)構(gòu)的信息,可以閱讀 AngularJS 最佳實(shí)踐: 目錄結(jié)構(gòu)
在開始 AngularJS 應(yīng)用之前,我們將使用 bower 安裝客戶端方面的依賴。如果你還沒有 Bower 先要安裝它。在命令提示行中將當(dāng)前工作目錄切換至你應(yīng)用的根目錄,然后依照下面的命令安裝依賴。
bower install angular angular-route angular-material --save設(shè)置數(shù)據(jù)庫
在這個(gè)例子中,我將使用一個(gè)名字為 customer-manager 的數(shù)據(jù)庫和一張名字為 customers 的表。下面是數(shù)據(jù)庫的導(dǎo)出文件,你可以依照這個(gè)快速開始。
CREATE TABLE `customer_manager`.`customers` ( `customer_id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NOT NULL, `address` VARCHAR(450) NULL, `city` VARCHAR(45) NULL, `country` VARCHAR(45) NULL, `phone` VARCHAR(45) NULL, `remarks` VARCHAR(500) NULL, PRIMARY KEY (`customer_id`) );創(chuàng)建一個(gè) Angular Service 和 MySQL 進(jìn)行交互
一旦你的數(shù)據(jù)庫和表都準(zhǔn)備好了,就可以開始創(chuàng)建一個(gè) AngularJS service 來直接從數(shù)據(jù)庫中獲取數(shù)據(jù)。使用 node-mysql 這個(gè) npm 模塊使 service 連接數(shù)據(jù)庫——一個(gè)使用 JavaScript 為 NodeJs 編寫的 MySQL 驅(qū)動(dòng)。在你 Angular 應(yīng)用的 app/ 目錄下安裝 node-mysql 模塊。
注意:我們將 node-mysql 模塊安裝到 app 目錄下而不是應(yīng)用的根目錄,是因?yàn)槲覀冃枰谧罱K的 distribution 中包含這個(gè)模塊。
在命令提示行中切換工作目錄至 app 文件夾然后按照下面所示安裝模塊:
npm install --save mysql
我們的 angular service —— app/scripts/customer/customerService.js 如下所示:
(function () { "use strict"; var mysql = require("mysql"); // 創(chuàng)建 MySql 數(shù)據(jù)庫連接 var connection = mysql.createConnection({ host: "localhost", user: "root", password: "password", database: "customer_manager" }); angular.module("app") .service("customerService", ["$q", CustomerService]); function CustomerService($q) { return { getCustomers: getCustomers, getById: getCustomerById, getByName: getCustomerByName, create: createCustomer, destroy: deleteCustomer, update: updateCustomer }; function getCustomers() { var deferred = $q.defer(); var query = "SELECT * FROM customers"; connection.query(query, function (err, rows) { if (err) deferred.reject(err); deferred.resolve(rows); }); return deferred.promise; } function getCustomerById(id) { var deferred = $q.defer(); var query = "SELECT * FROM customers WHERE customer_id = ?"; connection.query(query, [id], function (err, rows) { if (err) deferred.reject(err); deferred.resolve(rows); }); return deferred.promise; } function getCustomerByName(name) { var deferred = $q.defer(); var query = "SELECT * FROM customers WHERE name LIKE "" + name + "%""; connection.query(query, [name], function (err, rows) { if (err) deferred.reject(err); deferred.resolve(rows); }); return deferred.promise; } function createCustomer(customer) { var deferred = $q.defer(); var query = "INSERT INTO customers SET ?"; connection.query(query, customer, function (err, res) if (err) deferred.reject(err); deferred.resolve(res.insertId); }); return deferred.promise; } function deleteCustomer(id) { var deferred = $q.defer(); var query = "DELETE FROM customers WHERE customer_id = ?"; connection.query(query, [id], function (err, res) { if (err) deferred.reject(err); deferred.resolve(res.affectedRows); }); return deferred.promise; } function updateCustomer(customer) { var deferred = $q.defer(); var query = "UPDATE customers SET name = ? WHERE customer_id = ?"; connection.query(query, [customer.name, customer.customer_id], function (err, res) { if (err) deferred.reject(err); deferred.resolve(res); }); return deferred.promise; } } })();
customerService 是一個(gè)簡(jiǎn)單的自定義 angular service,它提供了對(duì)表 customers 的基礎(chǔ) CRUD 操作。直接在 service 中使用了 node 模塊 mysql。如果你已經(jīng)擁有了一個(gè)遠(yuǎn)程的數(shù)據(jù)服務(wù),你也可以使用它來替代之。
控制器 & 模板app/scripts/customer/customerController 中的 customerController 如下所示:
(function () { "use strict"; angular.module("app") .controller("customerController", ["customerService", "$q", "$mdDialog", CustomerController]); function CustomerController(customerService, $q, $mdDialog) { var self = this; self.selected = null; self.customers = []; self.selectedIndex = 0; self.filterText = null; self.selectCustomer = selectCustomer; self.deleteCustomer = deleteCustomer; self.saveCustomer = saveCustomer; self.createCustomer = createCustomer; self.filter = filterCustomer; // 載入初始數(shù)據(jù) getAllCustomers(); //---------------------- // 內(nèi)部方法 //---------------------- function selectCustomer(customer, index) { self.selected = angular.isNumber(customer) ? self.customers[customer] : customer; self.selectedIndex = angular.isNumber(customer) ? customer: index; } function deleteCustomer($event) { var confirm = $mdDialog.confirm() .title("Are you sure?") .content("Are you sure want to delete this customer?") .ok("Yes") .cancel("No") .targetEvent($event); $mdDialog.show(confirm).then(function () { customerService.destroy(self.selected.customer_id).then(function (affectedRows) { self.customers.splice(self.selectedIndex, 1); }); }, function () { }); } function saveCustomer($event) { if (self.selected != null && self.selected.customer_id != null) { customerService.update(self.selected).then(function (affectedRows) { $mdDialog.show( $mdDialog .alert() .clickOutsideToClose(true) .title("Success") .content("Data Updated Successfully!") .ok("Ok") .targetEvent($event) ); }); } else { //self.selected.customer_id = new Date().getSeconds(); customerService.create(self.selected).then(function (affectedRows) { $mdDialog.show( $mdDialog .alert() .clickOutsideToClose(true) .title("Success") .content("Data Added Successfully!") .ok("Ok") .targetEvent($event) ); }); } } function createCustomer() { self.selected = {}; self.selectedIndex = null; } function getAllCustomers() { customerService.getCustomers().then(function (customers) { self.customers = [].concat(customers); self.selected = customers[0]; }); } function filterCustomer() { if (self.filterText == null || self.filterText == "") { getAllCustomers(); } else { customerService.getByName(self.filterText).then(function (customers) { self.customers = [].concat(customers); self.selected = customers[0]; }); } } } })();
我們的顧客模板(app/scripts/customer/customer.html)使用了 angular material 組件來構(gòu)建 UI,如下所示:
Customers
{{it.name}} {{ _ctrl.selected.name }}
Add Save Cancel Delete
app.js 包含模塊初始化腳本和應(yīng)用的路由配置,如下所示:
(function () { "use strict"; var _templateBase = "./scripts"; angular.module("app", [ "ngRoute", "ngMaterial", "ngAnimate" ]) .config(["$routeProvider", function ($routeProvider) { $routeProvider.when("/", { templateUrl: _templateBase + "/customer/customer.html" , controller: "customerController", controllerAs: "_ctrl" }); $routeProvider.otherwise({ redirectTo: "/" }); } ]); })();
最后是我們的首頁 app/index.html
Customer Manager
如果你已經(jīng)如上面那樣配置過 VS Code task runner 的話,使用 gulp run 命令或者按下 Ctrl + Shif + B 來啟動(dòng)你的應(yīng)用。
構(gòu)建 AngularJS 應(yīng)用為了構(gòu)建我們的 Angular 應(yīng)用,需要安裝 gulp-uglify, gulp-minify-css 和 gulp-usemin 依賴包。
npm install --save gulp-uglify gulp-minify-css gulp-usemin
打開你的 gulpfile.js 并且引入必要的模塊。
var childProcess = require("child_process"); var electron = require("electron-prebuilt"); var gulp = require("gulp"); var jetpack = require("fs-jetpack"); var usemin = require("gulp-usemin"); var uglify = require("gulp-uglify"); var projectDir = jetpack; var srcDir = projectDir.cwd("./app"); var destDir = projectDir.cwd("./build");
如果構(gòu)建目錄已經(jīng)存在的話,清理一下它。
gulp.task("clean", function (callback) { return destDir.dirAsync(".", { empty: true }); });
復(fù)制文件到構(gòu)建目錄。我們并不需要使用復(fù)制功能來復(fù)制 angular 應(yīng)用的代碼,在下一部分中 usemin 將會(huì)為我們做這件事請(qǐng):
gulp.task("copy", ["clean"], function () { return projectDir.copyAsync("app", destDir.path(), { overwrite: true, matching: [ "./node_modules/**/*", "*.html", "*.css", "main.js", "package.json" ] }); });
我們的構(gòu)建任務(wù)將使用 gulp.src() 獲取 app/index.html 然后傳遞給 usemin。然后它會(huì)將輸出寫入到構(gòu)建目錄并且把 index.html 中的引用用優(yōu)化版代碼替換掉 。
注意: 千萬不要忘記在 app/index.html 像這樣定義 usemin 塊:
構(gòu)建任務(wù)如下所示:
gulp.task("build", ["copy"], function () { return gulp.src("./app/index.html") .pipe(usemin({ js: [uglify()] })) .pipe(gulp.dest("build/")); });為發(fā)行(distribution)做準(zhǔn)備
在這一部分我們將把 Electron 應(yīng)用打包至生產(chǎn)環(huán)境。在根目錄創(chuàng)建構(gòu)建腳本 build.windows.js。這個(gè)腳本用于 Windows 上。對(duì)于其他平臺(tái)來說,你應(yīng)該創(chuàng)建那個(gè)平臺(tái)特定的腳本并且根據(jù)平臺(tái)來運(yùn)行。
可以在 node_modules/electron-prebuilt/dist 目錄中找到一個(gè)典型的 electron distribution。這里是構(gòu)建 electron 應(yīng)用的步驟:
我們首要的任務(wù)是復(fù)制 electron distribution 到我們的 dist 目錄。
每一個(gè) electron distribution 都包含一個(gè)默認(rèn)的應(yīng)用在 dist/resources/default_app 中 。我們需要用我們最終構(gòu)建的應(yīng)用來替換它。
為了保護(hù)我們的應(yīng)用源碼和資源,你可以選擇將你的應(yīng)用打包成一個(gè) asar 歸檔,這會(huì)改變一點(diǎn)你的源碼。一個(gè) asar 歸檔是一個(gè)簡(jiǎn)單的類似 tar 的格式,它會(huì)將你所有的文件拼接成單個(gè)文件,Electron 可以在不解壓整個(gè)文件的情況下從中讀取任意文件。
注意:這一部分描述的是 windows 平臺(tái)下的打包。其他平臺(tái)中的步驟是一樣的,只是路徑和使用的文件不一樣而已。你可以在 github 中獲取 OSx 和 linux 的完整構(gòu)建腳本。
安裝構(gòu)建 electron 必要的依賴:npm install --save q asar fs-jetpack recedit
接下來,初始化我們的構(gòu)建腳本,如下所示:
var Q = require("q"); var childProcess = require("child_process"); var asar = require("asar"); var jetpack = require("fs-jetpack"); var projectDir; var buildDir; var manifest; var appDir; function init() { // 項(xiàng)目路徑是應(yīng)用的根目錄 projectDir = jetpack; // 構(gòu)建目錄是最終應(yīng)用被構(gòu)建后放置的目錄 buildDir = projectDir.dir("./dist", { empty: true }); // angular 應(yīng)用目錄 appDir = projectDir.dir("./build"); // angular 應(yīng)用的 package.json 文件 manifest = appDir.read("./package.json", "json"); return Q(); }
這里我們使用 fs-jetpack node 模塊進(jìn)行文件操作。它提供了更靈活的文件操作。
復(fù)制 Electron Distribution從 electron-prebuilt/dist 復(fù)制默認(rèn)的 electron distribution 到我們的 dist 目錄
function copyElectron() { return projectDir.copyAsync("./node_modules/electron-prebuilt/dist", buildDir.path(), { overwrite: true }); }清理默認(rèn)應(yīng)用
你可以在 resources/default_app 文件夾內(nèi)找到一個(gè)默認(rèn)的 HTML 應(yīng)用。我們需要用我們自己的 angular 應(yīng)用來替換它。按照下面所示移除它:
注意:這里的路徑是針對(duì) windows 平臺(tái)的。對(duì)于其他平臺(tái)過程是一致的,只是路徑不一樣而已。在 OSX 中路徑應(yīng)該是 Contents/Resources/default_app
function cleanupRuntime() { return buildDir.removeAsync("resources/default_app"); }創(chuàng)建 asar 包
function createAsar() { var deferred = Q.defer(); asar.createPackage(appDir.path(), buildDir.path("resources/app.asar"), function () { deferred.resolve(); }); return deferred.promise; }
這將會(huì)把你 angular 應(yīng)用的所有文件打包到一個(gè) asar 包文件里。你可以在 dist/resources/ 目錄中找到 asar 文件。
替換為自己的應(yīng)用資源下一步是將默認(rèn)的 electron icon 替換成你自己的,更新產(chǎn)品的信息然后重命名應(yīng)用。
function updateResources() { var deferred = Q.defer(); // 將你的 icon 從 resource 文件夾復(fù)制到構(gòu)建文件夾下 projectDir.copy("resources/windows/icon.ico", buildDir.path("icon.ico")); // 將 Electron icon 替換成你自己的 var rcedit = require("rcedit"); rcedit(buildDir.path("electron.exe"), { "icon": projectDir.path("resources/windows/icon.ico"), "version-string": { "ProductName": manifest.name, "FileDescription": manifest.description, } }, function (err) { if (!err) { deferred.resolve(); } }); return deferred.promise; } // 重命名 electron exe function rename() { return buildDir.renameAsync("electron.exe", manifest.name + ".exe"); }創(chuàng)建原生安裝包
你可以使用 wix 或 NSIS 創(chuàng)建 windows 安裝包。這里我們盡可能使用更小更靈活的 NSIS,它很適合網(wǎng)絡(luò)應(yīng)用。使用 NSIS 可以創(chuàng)建支持應(yīng)用安裝時(shí)需要的任何事情的安裝包。
在 resources/windows/installer.nsis 中創(chuàng)建 NSIS 腳本
!include LogicLib.nsh !include nsDialogs.nsh ; -------------------------------- ; Variables ; -------------------------------- !define dest "{{dest}}" !define src "{{src}}" !define name "{{name}}" !define productName "{{productName}}" !define version "{{version}}" !define icon "{{icon}}" !define banner "{{banner}}" !define exec "{{productName}}.exe" !define regkey "Software${productName}" !define uninstkey "SoftwareMicrosoftWindowsCurrentVersionUninstall${productName}" !define uninstaller "uninstall.exe" ; -------------------------------- ; Installation ; -------------------------------- SetCompressor lzma Name "${productName}" Icon "${icon}" OutFile "${dest}" InstallDir "$PROGRAMFILES${productName}" InstallDirRegKey HKLM "${regkey}" "" CRCCheck on SilentInstall normal XPStyle on ShowInstDetails nevershow AutoCloseWindow false WindowIcon off Caption "${productName} Setup" ; Don"t add sub-captions to title bar SubCaption 3 " " SubCaption 4 " " Page custom welcome Page instfiles Var Image Var ImageHandle Function .onInit ; Extract banner image for welcome page InitPluginsDir ReserveFile "${banner}" File /oname=$PLUGINSDIRanner.bmp "${banner}" FunctionEnd ; Custom welcome page Function welcome nsDialogs::Create 1018 ${NSD_CreateLabel} 185 1u 210 100% "Welcome to ${productName} version ${version} installer.$ $ $ $ Click install to begin." ${NSD_CreateBitmap} 0 0 170 210 "" Pop $Image ${NSD_SetImage} $Image $PLUGINSDIRanner.bmp $ImageHandle nsDialogs::Show ${NSD_FreeImage} $ImageHandle FunctionEnd ; Installation declarations Section "Install" WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR" WriteRegStr HKLM "${uninstkey}" "DisplayName" "${productName}" WriteRegStr HKLM "${uninstkey}" "DisplayIcon" ""$INSTDIRicon.ico"" WriteRegStr HKLM "${uninstkey}" "UninstallString" ""$INSTDIR${uninstaller}"" ; Remove all application files copied by previous installation RMDir /r "$INSTDIR" SetOutPath $INSTDIR ; Include all files from /build directory File /r "${src}*" ; Create start menu shortcut CreateShortCut "$SMPROGRAMS${productName}.lnk" "$INSTDIR${exec}" "" "$INSTDIRicon.ico" WriteUninstaller "${uninstaller}" SectionEnd ; -------------------------------- ; Uninstaller ; -------------------------------- ShowUninstDetails nevershow UninstallCaption "Uninstall ${productName}" UninstallText "Don"t like ${productName} anymore? Hit uninstall button." UninstallIcon "${icon}" UninstPage custom un.confirm un.confirmOnLeave UninstPage instfiles Var RemoveAppDataCheckbox Var RemoveAppDataCheckbox_State ; Custom uninstall confirm page Function un.confirm nsDialogs::Create 1018 ${NSD_CreateLabel} 1u 1u 100% 24u "If you really want to remove ${productName} from your computer press uninstall button." ${NSD_CreateCheckbox} 1u 35u 100% 10u "Remove also my ${productName} personal data" Pop $RemoveAppDataCheckbox nsDialogs::Show FunctionEnd Function un.confirmOnLeave ; Save checkbox state on page leave ${NSD_GetState} $RemoveAppDataCheckbox $RemoveAppDataCheckbox_State FunctionEnd ; Uninstall declarations Section "Uninstall" DeleteRegKey HKLM "${uninstkey}" DeleteRegKey HKLM "${regkey}" Delete "$SMPROGRAMS${productName}.lnk" ; Remove whole directory from Program Files RMDir /r "$INSTDIR" ; Remove also appData directory generated by your app if user checked this option ${If} $RemoveAppDataCheckbox_State == ${BST_CHECKED} RMDir /r "$LOCALAPPDATA${name}" ${EndIf} SectionEnd
在 build.windows.js 文件中創(chuàng)建一個(gè)叫做 createInstaller 的函數(shù),如下所示:
function createInstaller() { var deferred = Q.defer(); function replace(str, patterns) { Object.keys(patterns).forEach(function (pattern) { console.log(pattern) var matcher = new RegExp("{{" + pattern + "}}", "g"); str = str.replace(matcher, patterns[pattern]); }); return str; } var installScript = projectDir.read("resources/windows/installer.nsi"); installScript = replace(installScript, { name: manifest.name, productName: manifest.name, version: manifest.version, src: buildDir.path(), dest: projectDir.path(), icon: buildDir.path("icon.ico"), setupIcon: buildDir.path("icon.ico"), banner: projectDir.path("resources/windows/banner.bmp"), }); buildDir.write("installer.nsi", installScript); var nsis = childProcess.spawn("makensis", [buildDir.path("installer.nsi")], { stdio: "inherit" }); nsis.on("error", function (err) { if (err.message === "spawn makensis ENOENT") { throw "Can"t find NSIS. Are you sure you"ve installed it and" + " added to PATH environment variable?"; } else { throw err; } }); nsis.on("close", function () { deferred.resolve(); }); return deferred.promise; }
你應(yīng)該安裝了 NSIS,并且確保它在你的路徑中是可用的。creaeInstaller 函數(shù)會(huì)讀取安裝包腳本并且依照 NSIS 運(yùn)行時(shí)使用 makensis 命令來執(zhí)行。
將他們組合到一起創(chuàng)建一個(gè)函數(shù)把所有的片段放在一起,為了使 gulp 任務(wù)可以獲取到然后輸出它:
function build() { return init() .then(copyElectron) .then(cleanupRuntime) .then(createAsar) .then(updateResources) .then(rename) .then(createInstaller); } module.exports = { build: build };
接著,在 gulpfile.js 中創(chuàng)建 gulp 任務(wù)來執(zhí)行這個(gè)構(gòu)建腳本:
var release_windows = require("./build.windows"); var os = require("os"); gulp.task("build-electron", ["build"], function () { switch (os.platform()) { case "darwin": // 執(zhí)行 build.osx.js break; case "linux": //執(zhí)行 build.linux.js break; case "win32": return release_windows.build(); } });
運(yùn)行下面命令,你應(yīng)該就會(huì)得到最終的產(chǎn)品:
gulp build-electron
你最終的 electron 應(yīng)用應(yīng)該在 dist 目錄中,并且目錄結(jié)構(gòu)應(yīng)該和下面是相似的:
總結(jié)Electron 不僅僅是一個(gè)支持打包 web 應(yīng)用成為桌面應(yīng)用的原生 web view。它現(xiàn)在包含 app 的自動(dòng)升級(jí)、Windows 安裝包、崩潰報(bào)告、通知和一些其它有用的原生 app 功能——所有的這些都通過 JavaScript API 調(diào)用。
到目前為止,很大范圍的應(yīng)用使用 electron 創(chuàng)建,包括聊天應(yīng)用、數(shù)據(jù)庫管理器、地圖設(shè)計(jì)器、協(xié)作設(shè)計(jì)工具和手機(jī)原型等。
下面是 Github Electron 的一些有用的資源:
官方網(wǎng)站 – http://electron.atom.io/
官方文檔 – https://github.com/atom/electron/tree/master/docs
Awesome Electron – https://github.com/sindresorhus/awesome-electron
Electron 應(yīng)用樣板 – https://github.com/szwacz/electron-boilerplate
使用 ReactJs 的 Electron 樣板 – https://github.com/airtoxin/Electron-React-Boilerplate
https://github.com/chentsulin/electron-react-boilerplate
本文中應(yīng)用源碼(譯者注)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91579.html
摘要:如何在中使用動(dòng)畫前端掘金本文講一下中動(dòng)畫應(yīng)用的部分。與的快速入門指南推薦前端掘金是非常棒的框架,能夠創(chuàng)建功能強(qiáng)大,動(dòng)態(tài)功能的。自發(fā)布以來,已經(jīng)廣泛應(yīng)用于開發(fā)中。 如何在 Angular 中使用動(dòng)畫 - 前端 - 掘金本文講一下Angular中動(dòng)畫應(yīng)用的部分。 首先,Angular本生不提供動(dòng)畫機(jī)制,需要在項(xiàng)目中加入Angular插件模塊ngAnimate才能完成Angular的動(dòng)畫機(jī)制...
摘要:在年成為最大贏家,贏得了實(shí)現(xiàn)的風(fēng)暴之戰(zhàn)。和他的競(jìng)爭(zhēng)者位列第二沒有前端開發(fā)者可以忽視和它的生態(tài)系統(tǒng)。他的殺手級(jí)特性是探測(cè)功能,通過檢查任何用戶的功能,以直觀的方式讓開發(fā)人員檢查所有端點(diǎn)。 2016 JavaScript 后起之秀 本文轉(zhuǎn)載自:眾成翻譯譯者:zxhycxq鏈接:http://www.zcfy.cc/article/2410原文:https://risingstars2016...
摘要:你們說能不能就用的開發(fā)模式來實(shí)現(xiàn)客戶端啊這樣版版版就都有了。有道云筆記可能就是最貼近我們想法的產(chǎn)品,有客戶端,有版。這個(gè)項(xiàng)目由發(fā)起和維護(hù)。 最近一個(gè)多月一直在用 AngularJS 做公司的一個(gè)項(xiàng)目(還沒有做完),我之前主要是用 PHP 開發(fā)服務(wù)端的,AngularJS 也是現(xiàn)學(xué)現(xiàn)賣,整個(gè)過程還是比較有意義的,覺得很有必要寫篇文章記錄一下。 緣起 事情是這樣的……我們團(tuán)隊(duì)的產(chǎn)品是一款 ...
摘要:你們說能不能就用的開發(fā)模式來實(shí)現(xiàn)客戶端啊這樣版版版就都有了。有道云筆記可能就是最貼近我們想法的產(chǎn)品,有客戶端,有版。這個(gè)項(xiàng)目由發(fā)起和維護(hù)。 最近一個(gè)多月一直在用 AngularJS 做公司的一個(gè)項(xiàng)目(還沒有做完),我之前主要是用 PHP 開發(fā)服務(wù)端的,AngularJS 也是現(xiàn)學(xué)現(xiàn)賣,整個(gè)過程還是比較有意義的,覺得很有必要寫篇文章記錄一下。 緣起 事情是這樣的……我們團(tuán)隊(duì)的產(chǎn)品是一款 ...
閱讀 4031·2021-11-22 13:53
閱讀 1733·2021-09-23 11:52
閱讀 2448·2021-09-06 15:02
閱讀 965·2019-08-30 15:54
閱讀 913·2019-08-30 14:15
閱讀 2394·2019-08-29 18:39
閱讀 666·2019-08-29 16:07
閱讀 428·2019-08-29 13:13