国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
使用 AngularJS 和 Electron 構建桌面應用

原文: Creating Desktop Applications With AngularJS and GitHub Electron

GitHub 的 Electron 框架(以前叫做 Atom Shell)允許你使用 HTML, CSS 和 JavaScript 編寫跨平臺的桌面應用。它是 io.js 運行時的衍生,專注于桌面應用而不是 web 服務端。

Electron 豐富的原生 API 使我們能夠在頁面中直接使用 JavaScript 獲取原生的內容。

這個教程向我們展示了如何使用 Angular 和 Electron 構建一個桌面應用。下面是本教程的所有步驟:

  1. 創(chuàng)建一個簡單的 Electron 應用

  2. 使用 Visual Studio Code 編輯器管理我們的項目和任務

  3. 使用 Electron 開發(fā)(原文為 Integrate)一個 Angular 顧客管理應用(Angular Customer Manager App)

  4. 使用 Gulp 任務構建我們的應用,并生成安裝包

創(chuàng)建你的 Electron 應用

起初,如果你的系統(tǒng)中還沒有安裝 Node,你需要先安裝它。我們應用的結構如下所示:

這個項目中有兩個 package.json 文件。

  • 開發(fā)使用項目根目錄下的 package.json 包含你的配置,開發(fā)環(huán)境的依賴和構建腳本。這些依賴和 package.json 文件不會被打包到生產環(huán)境構建中。

  • 應用使用app 目錄下的 package.json 是你應用的清單文件。因此每當在你需要為你項目安裝 npm 依賴的時候,你應該依照這個 package.json 來進行安裝。

package.json 的格式和 Node 模塊中的完全一致。你應用的啟動腳本(的路徑)需要在 app/package.json 中的 main 屬性中指定。

app/package.json 看起來是這樣的:

{  name: "AngularElectron",   version: "0.0.0",   main: "main.js" }

過執(zhí)行 npm init 命令分別創(chuàng)建這兩個 package.json 文件,也可以手動創(chuàng)建它們。通過在命令提示行里鍵入以下命令來安裝項目打包必要的 npm 依賴:

npm install --save-dev electron-prebuilt fs-jetpack asar rcedit Q

創(chuàng)建啟動腳本

app/main.js是我們應用的入口。它負責創(chuàng)建主窗口和處理系統(tǒng)事件。 main.js 應該如下所示:

// app/main.js// 應用的控制模塊var app = require('app'); // 創(chuàng)建原生瀏覽器窗口的模塊var BrowserWindow = require('browser-window');var mainWindow = null;// 當所有窗口都關閉的時候退出應用app.on('window-all-closed', function () {  if (process.platform != 'darwin') {    app.quit();  }});// 當 Electron 結束的時候,這個方法將會生效// 初始化并準備創(chuàng)建瀏覽器窗口app.on('ready', function () {  // 創(chuàng)建瀏覽器窗口.  mainWindow = new BrowserWindow({ width: 800, height: 600 });  // 載入應用的 index.html  mainWindow.loadUrl('file://' + __dirname + '/index.html');  // 打開開發(fā)工具  // mainWindow.openDevTools();  // 窗口關閉時觸發(fā)  mainWindow.on('closed', function () {    // 想要取消窗口對象的引用,如果你的應用支持多窗口,    // 通常你需要將所有的窗口對象存儲到一個數(shù)組中,    // 在這個時候你應該刪除相應的元素    mainWindow = null;  });  });

通過 DOM 訪問原生

正如我上面提到的那樣,Electron 使你能夠直接在 web 頁面中訪問本地 npm 模塊和原生 API。你可以這樣創(chuàng)建 app/index.html 文件:

<html><body>   <h1>Hello World!</h1>  We are using Electron   <script>  document.write(process.versions['electron']) </script>  <script> document.write(process.platform) </script>  <script type="text/javascript">      var fs = require('fs');     var file = fs.readFileSync('app/package.json');      document.write(file);   </script></body> </html>

app/index.html是一個簡單的 HTML 頁面。在這里,它通過使用 Node’s fs (file system) 模塊來讀取 package.json 文件并將其內容寫入到 document body 中。

運行應用

一旦你創(chuàng)建好了項目結構、 app/index.html 、 app/main.js 和app/package.json ,你很可能想要嘗試去運行初始的 Electron 應用來測試并確保它正常工作。

如果你已經在系統(tǒng)中全局安裝了 electron-prebuilt ,就可以通過下面的命令啟動應用:

electron app

在這里, electron 是運行 electron shell 的命令, app 是我們應用的目錄名。如果你不想將 Election 安裝到你全局的 npm 模塊中,可以在命令提示行中通過下面命令使用本地 npm_modules 文件夾下的 electron 來啟動應用。

"node_modules/.bin/electron" "./app" 

盡管你可以這樣來運行應用,但是我還是建議你在 gulpfile.js 中創(chuàng)建一個 gulp task ,這樣你就可以將你的任務和 Visual Studio Code 編輯器相結合,我們會在下一部分展示。

// 獲取依賴var gulp        = require('gulp'),   childProcess  = require('child_process'),   electron      = require('electron-prebuilt');// 創(chuàng)建 gulp 任務gulp.task('run', function () {   childProcess.spawn(electron, ['./app'], { stdio: 'inherit' }); });

運行你的 gulp 任務: gulp run 。我們的應用看起來會是這個樣子:

配置 Visual Studio Code 開發(fā)環(huán)境

Visual Studio Code 是微軟的一款跨平臺代碼編輯器。VS Code 是基于 Electron 和 微軟自身的 Monaco Code Editor 開發(fā)的。你可以在 這里 下載到 Visual Studio Code。

在 VS Code 中打開你的 electron 應用。

配置 Visual Studio Code Task Runner

有很多自動化的工具,像構建、打包和測試等。我們大多從命令行中運行這些工具。VS Code task runner 使你能夠將你自定義的任務集成到項目中。你可以在你的項目中直接運行 grunt,、gulp,、MsBuild 或者其他任務,這并不需要移步到命令行。

VS Code 能夠自動檢測你的 grunt 和 gulp 任務。按下 ctrl + shift + p 然后鍵入 Run Task 敲擊回車便可。

你將從 gulpfile.js 或 gruntfile.js 文件中獲取所有有效的任務。

注意:你需要確保 gulpfile.js 文件存在于你應用的根目錄下。

ctrl + shift + b 會從你任務執(zhí)行器(task runner)中執(zhí)行 build 任務。你可以使用 task.json 文件來覆蓋任務集成。按下 ctrl + shift + p 然后鍵入Configure Task 敲擊回車。這將會在你項目中創(chuàng)建一個 .setting 的文件夾和task.json 文件。要是你不止想要執(zhí)行簡單的任務,你需要在 task.json 中進行配置。例如你或許想要通過按下 Ctrl + Shift + B 來運行應用,你可以這樣編輯task.json 文件:

{   "version": "0.1.0",   "command": "gulp",   "isShellCommand": true,   "args": [ "--no-color" ],   "tasks": [     {       "taskName": "run",       "args": [],       "isBuildCommand": true     }   ] } 

根部分聲明命令為 gulp 。你可以在 tasks 部分寫入你想要的更多任務。將一個任務的 isBuildCommand 設置為 true 意味著它和 Ctrl + Shift + B 進行了綁定。目前 VS Code 只支持一個頂級任務。

現(xiàn)在,如果你按下 Ctrl + Shift + B , gulp run 將會被執(zhí)行。

你可以在 這里 閱讀到更多關于 visual studio code 任務的信息。

調試 Electron 應用

打開調試面板點擊配置按鈕就會在 .settings 文件夾內創(chuàng)建一個 launch.json文件,包含了調試的配置。

我們不需要啟動 app.js 的配置,所以移除它。

現(xiàn)在,你的 launch.json 應該如下所示:

{   "version": "0.1.0",   // 配置列表。添加新的配置或更改已存在的配置。  // 僅支持 "node" 和 "mono",可以改變 "type" 來進行切換。  "configurations": [    {       "name": "Attach",       "type": "node",       // TCP/IP 地址. 默認是 "localhost"      "address": "localhost",       // 建立連接的端口.      "port": 5858,       "sourceMaps": false      }    ] }

按照下面所示更改之前創(chuàng)建的 gulp run 任務,這樣我們的 electron 將會采用調試模式運行,5858 端口也會被監(jiān)聽。

gulp.task('run', function () {   childProcess.spawn(electron, ['--debug=5858','./app'], { stdio: 'inherit' }); }); 

在調試面板中選擇 “Attach” 配置項,點擊開始(run)或者按下 F5。稍等片刻后你應該就能在上部看到調試命令面板。

創(chuàng)建 AngularJS 應用

第一次接觸 AngularJS?瀏覽 官方網站 或一些 Scotch Angular 教程 。

這一部分會講解如何使用 AngularJS 和 MySQL 數(shù)據庫創(chuàng)建一個顧客管理(Customer Manager)應用。這個應用的目的不是為了強調 AngularJS 的核心概念,而是展示如何在 GiHub 的 Electron 中同時使用 AngularJS 和 NodeJS 以及 MySQL 。

我們的顧客管理應用正如下面這樣簡單:

  • 顧客列表

  • 添加新顧客

  • 選擇刪除一個顧客

  • 搜索指定的顧客

項目結構

我們的應用在 app 文件夾下,目錄結構如下所示:

主頁是 app/index.html 文件。 app/scripts 文件夾包含所有用在該應用中的關鍵腳本和視圖。有許多方法可以用來組織應用的文件。

這里我更喜歡按照功能來組織腳本文件。每個功能都有它自己的文件夾,文件夾中有模板和控制器。獲取更多關于目錄結構的信息,可以閱讀 AngularJS 最佳實踐: 目錄結構

在開始 AngularJS 應用之前,我們將使用 bower 安裝客戶端方面的依賴。如果你還沒有 Bower 先要安裝它。在命令提示行中將當前工作目錄切換至你應用的根目錄,然后依照下面的命令安裝依賴。

bower install angular angular-route angular-material --save 

設置數(shù)據庫

在這個例子中,我將使用一個名字為 customer-manager 的數(shù)據庫和一張名字為customers 的表。下面是數(shù)據庫的導出文件,你可以依照這個快速開始。

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)建一個 Angular Service 和 MySQL 進行交互

一旦你的數(shù)據庫和表都準備好了,就可以開始創(chuàng)建一個 AngularJS service 來直接從數(shù)據庫中獲取數(shù)據。使用 node-mysql 這個 npm 模塊使 service 連接數(shù)據庫——一個使用 JavaScript 為 NodeJs 編寫的 MySQL 驅動。在你 Angular 應用的 app/ 目錄下安裝 node-mysql 模塊。

注意:我們將 node-mysql 模塊安裝到 app 目錄下而不是應用的根目錄,是因為我們需要在最終的 distribution 中包含這個模塊。

在命令提示行中切換工作目錄至 app 文件夾然后按照下面所示安裝模塊:

npm install --save mysql 

我們的 angular service —— app/scripts/customer/customerService.js 如下所示:

(function () {    'use strict';    var mysql = require('mysql');    // 創(chuàng)建 MySql 數(shù)據庫連接    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 是一個簡單的自定義 angular service,它提供了對表customers 的基礎 CRUD 操作。直接在 service 中使用了 node 模塊 mysql 。如果你已經擁有了一個遠程的數(shù)據服務,你也可以使用它來替代之。

控制器 & 模板

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ù)據        getAllCustomers();        //----------------------        // 內部方法        //----------------------        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 組件來構建 UI,如下所示:

<div style="width:100%" layout="row">    <md-sidenav class="site-sidenav md-sidenav-left md-whiteframe-z2"                md-component-id="left"                md-is-locked-open="$mdMedia('gt-sm')">        <md-toolbar layout="row" class="md-whiteframe-z1">            <h1>Customers</h1>        </md-toolbar>        <md-input-container style="margin-bottom:0">            <label>Customer Name</label>            <input required name="customerName" ng-model="_ctrl.filterText" ng-change="_ctrl.filter()">        </md-input-container>        <md-list>            <md-list-item ng-repeat="it in _ctrl.customers">                <md-button ng-click="_ctrl.selectCustomer(it, $index)" ng-class="{'selected' : it === _ctrl.selected }">                    {{it.name}}                </md-button>            </md-list-item>        </md-list>    </md-sidenav>    <div flex layout="column" tabIndex="-1" role="main" class="md-whiteframe-z2">        <md-toolbar layout="row" class="md-whiteframe-z1">            <md-button class="menu" hide-gt-sm ng-click="ul.toggleList()" aria-label="Show User List">                <md-icon md-svg-icon="menu"></md-icon>            </md-button>            <h1>{{ _ctrl.selected.name }}</h1>        </md-toolbar>        <md-content flex id="content">            <div layout="column" style="width:50%">                <br />                <md-content layout-padding class="autoScroll">                    <md-input-container>                        <label>Name</label>                        <input ng-model="_ctrl.selected.name" type="text">                    </md-input-container>                    <md-input-container md-no-float>                        <label>Email</label>                        <input ng-model="_ctrl.selected.email" type="text">                    </md-input-container>                    <md-input-container>                        <label>Address</label>                        <input ng-model="_ctrl.selected.address"  ng-required="true">                    </md-input-container>                    <md-input-container md-no-float>                        <label>City</label>                        <input ng-model="_ctrl.selected.city" type="text" >                    </md-input-container>                    <md-input-container md-no-float>                        <label>Phone</label>                        <input ng-model="_ctrl.selected.phone" type="text">                    </md-input-container>                </md-content>                <section layout="row" layout-sm="column" layout-align="center center" layout-wrap>                    <md-button class="md-raised md-info" ng-click="_ctrl.createCustomer()">Add</md-button>                    <md-button class="md-raised md-primary" ng-click="_ctrl.saveCustomer()">Save</md-button>                    <md-button class="md-raised md-danger" ng-click="_ctrl.cancelEdit()">Cancel</md-button>                    <md-button class="md-raised md-warn" ng-click="_ctrl.deleteCustomer()">Delete</md-button>                </section>            </div>        </md-content>    </div></div>

app.js 包含模塊初始化腳本和應用的路由配置,如下所示:

(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

<html lang="en" ng-app="app">    <title>Customer Manager</title>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge"gt;    <meta name="description" content="">    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />    <!-- build:css assets/css/app.css -->    <link rel="stylesheet" href="../bower_components/angular-material/angular-material.css" />    <link rel="stylesheet" href="assets/css/style.css" />    <!-- endbuild --><body>    <ng-view></ng-view>    <!-- build:js scripts/vendor.js -->    <script src="../bower_components/angular/angular.js"></script>    <script src="../bower_components/angular-route/angular-route.js"></script>    <script src="../bower_components/angular-animate/angular-animate.js"></script>    <script src="../bower_components/angular-aria/angular-aria.js"></script>    <script src="../bower_components/angular-material/angular-material.js"></script>    <!-- endbuild -->       <!-- build:app scripts/app.js -->    <script src="./scripts/app.js"></script>    <script src="./scripts/customer/customerService.js"></script>    <script src="./scripts/customer/customerController.js"></script>    <!-- endbuild --></body></html>

如果你已經如上面那樣配置過 VS Code task runner 的話,使用 gulp run 命令或者按下 Ctrl + Shif + B 來啟動你的應用。

構建 AngularJS 應用

為了構建我們的 Angular 應用,需要安裝 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');

如果構建目錄已經存在的話,清理一下它。

gulp.task('clean', function (callback) {   return destDir.dirAsync('.', { empty: true }); });

復制文件到構建目錄。我們并不需要使用復制功能來復制 angular 應用的代碼,在下一部分中 usemin 將會為我們做這件事請:

gulp.task('copy', ['clean'], function () {     return projectDir.copyAsync('app', destDir.path(), {         overwrite: true, matching: [             './node_modules/**/*',             '*.html',             '*.css',             'main.js',             'package.json'        ]     }); });

我們的構建任務將使用 gulp.src() 獲取 app/index.html 然后傳遞給 usemin。然后它會將輸出寫入到構建目錄并且把 index.html 中的引用用優(yōu)化版代碼替換掉 。

注意: 千萬不要忘記在 app/index.html 像這樣定義 usemin 塊:

<!-- build:js scripts/vendor.js --><script src="../bower_components/angular/angular.js"></script><script src="../bower_components/angular-route/angular-route.js"></script><script src="../bower_components/angular-animate/angular-animate.js"></script><script src="../bower_components/angular-aria/angular-aria.js"></script><script src="../bower_components/angular-material/angular-material.js"></script><!-- endbuild -->    <!-- build:app scripts/app.js --><script src="./scripts/app.js"></script><script src="./scripts/customer/customerService.js"></script><script src="./scripts/customer/customerController.js"></script><!-- endbuild -->

構建任務如下所示:

gulp.task('build', ['copy'], function () {   return gulp.src('./app/index.html')     .pipe(usemin({       js: [uglify()]     }))     .pipe(gulp.dest('build/')); });

為發(fā)行(distribution)做準備

在這一部分我們將把 Electron 應用打包至生產環(huán)境。在根目錄創(chuàng)建構建腳本build.windows.js 。這個腳本用于 Windows 上。對于其他平臺來說,你應該創(chuàng)建那個平臺特定的腳本并且根據平臺來運行。

可以在 node_modules/electron-prebuilt/dist 目錄中找到一個典型的 electron distribution。這里是構建 electron 應用的步驟:

  • 我們首要的任務是復制 electron distribution 到我們的 dist 目錄。

  • 每一個 electron distribution 都包含一個默認的應用在dist/resources/default_app 中 。我們需要用我們最終構建的應用來替換它。

  • 為了保護我們的應用源碼和資源,你可以選擇將你的應用打包成一個 asar 歸檔,這會改變一點你的源碼。一個 asar 歸檔是一個簡單的類似 tar 的格式,它會將你所有的文件拼接成單個文件,Electron 可以在不解壓整個文件的情況下從中讀取任意文件。

注意:這一部分描述的是 windows 平臺下的打包。其他平臺中的步驟是一樣的,只是路徑和使用的文件不一樣而已。你可以在 github 中獲取 OSx 和 linux 的完整構建腳本。

安裝構建 electron 必要的依賴: npm install --save q asar fs-jetpack recedit

接下來,初始化我們的構建腳本,如下所示:

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() {     // 項目路徑是應用的根目錄    projectDir = jetpack;     // 構建目錄是最終應用被構建后放置的目錄    buildDir = projectDir.dir('./dist', { empty: true });     // angular 應用目錄    appDir = projectDir.dir('./build');     // angular 應用的 package.json 文件    manifest = appDir.read('./package.json', 'json');     return Q(); } 

這里我們使用 fs-jetpack node 模塊進行文件操作。它提供了更靈活的文件操作。

復制 Electron Distribution

從 electron-prebuilt/dist 復制默認的 electron distribution 到我們的 dist 目錄

function copyElectron() {      return projectDir.copyAsync('./node_modules/electron-prebuilt/dist', buildDir.path(), { overwrite: true }); } 

清理默認應用

你可以在 resources/default_app 文件夾內找到一個默認的 HTML 應用。我們需要用我們自己的 angular 應用來替換它。按照下面所示移除它:

注意:這里的路徑是針對 windows 平臺的。對于其他平臺過程是一致的,只是路徑不一樣而已。在 OSX 中路徑應該是 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; }

這將會把你 angular 應用的所有文件打包到一個 asar 包文件里。你可以在dist/resources/ 目錄中找到 asar 文件。

替換為自己的應用資源

下一步是將默認的 electron icon 替換成你自己的,更新產品的信息然后重命名應用。

function updateResources() {    var deferred = Q.defer();    // 將你的 icon 從 resource 文件夾復制到構建文件夾下    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,它很適合網絡應用。使用 NSIS 可以創(chuàng)建支持應用安裝時需要的任何事情的安裝包。

在 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 "Software\Microsoft\Windows\CurrentVersion\Uninstall\${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=$PLUGINSDIR\banner.bmp "${banner}"    FunctionEnd    ; Custom welcome page    Function welcome        nsDialogs::Create 1018        ${NSD_CreateLabel} 185 1u 210 100% "Welcome to ${productName} version ${version} installer.$\r$\n$\r$\nClick install to begin."        ${NSD_CreateBitmap} 0 0 170 210 ""        Pop $Image        ${NSD_SetImage} $Image $PLUGINSDIR\banner.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" '"$INSTDIR\icon.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}" "" "$INSTDIR\icon.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)建一個叫做 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;}

你應該安裝了 NSIS,并且確保它在你的路徑中是可用的。 creaeInstaller 函數(shù)會讀取安裝包腳本并且依照 NSIS 運行時使用 makensis 命令來執(zhí)行。

將他們組合到一起

創(chuàng)建一個函數(shù)把所有的片段放在一起,為了使 gulp 任務可以獲取到然后輸出它:

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 任務來執(zhí)行這個構建腳本:

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

運行下面命令,你應該就會得到最終的產品:

gulp build-electron

你最終的 electron 應用應該在 dist 目錄中,并且目錄結構應該和下面是相似的:

總結

Electron 不僅僅是一個支持打包 web 應用成為桌面應用的原生 web view。它現(xiàn)在包含 app 的自動升級、Windows 安裝包、崩潰報告、通知和一些其它有用的原生 app 功能——所有的這些都通過 JavaScript API 調用。

到目前為止,很大范圍的應用使用 electron 創(chuàng)建,包括聊天應用、數(shù)據庫管理器、地圖設計器、協(xié)作設計工具和手機原型等。

下面是 Github Electron 的一些有用的資源:


本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Vue.js + webpack 項目實踐
跟我學AngularJs:Service、Factory、Provider依賴注入使用與區(qū)別
AngularJS進階(十一)AngularJS實現(xiàn)表格數(shù)據的編輯,更新和刪除
Angular開發(fā)中的注意事項
gulp自動化任務腳本在HybridApp開發(fā)中的使用
使用OAuth打造webapi認證服務供自己的客戶端使用(二)
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服