github桌面应用下载慢_使用AngularJS和GitHub Electron创建桌面应用程序

本文详述了如何利用Angular和Electron框架构建功能齐全的桌面应用程序,涵盖项目搭建、代码编写、调试及打包发布全流程。通过整合AngularJS与NodeJS,实现与MySQL数据库的交互,展示了跨平台应用开发的高效与便捷。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

github桌面应用下载慢

GitHub's Electron framework (formerly known as Atom Shell) lets you write cross platform desktop application using HTML, CSS and JavaScript. It's a variant of io.js run-time which is focused on desktop applications instead of web servers.

GitHub的Electron框架(以前称为Atom Shell)使您可以使用HTML,CSS和JavaScript编写跨平台的桌面应用程序 。 它是io.js运行时的一种变体,它专注于桌面应用程序而不是Web服务器。

Electron's rich native APIs enables us to access native things directly from our pages with JavaScript.

Electron丰富的本机API使我们能够直接使用JavaScript从页面访问本机内容。

This tutorial shows us how to build a desktop application with Angular and Electron. The steps for this tutorial are as follows:

本教程向我们展示了如何使用Angular和Electron构建桌面应用程序。 本教程的步骤如下:

  1. Create a simple Electron application

    创建一个简单的电子应用程序
  2. Use Visual Studio Code Editor to manage our project and tasks

    使用Visual Studio代码编辑器管理我们的项目和任务
  3. Integrate an Angular Customer Manager App with Electron

    将Angular Customer Manager应用程序与Electron集成
  4. Use Gulp tasks to build our application and create installers

    使用Gulp任务来构建我们的应用程序并创建安装程序

创建电子应用程序 (Creating Your Electron Application)

To get started, install Node if you don't have it in your system already. Our application should be structured as follows:

首先,如果系统中尚未安装Node ,请安装它。 我们的应用程序的结构应如下:

project-structure

There are two package.json files in this project.

这个项目中有两个package.json文件。

  • For development

    The package.json directly inside the project root contains the configurations, dependiencies for your development environment and build scripts. These dependencies and package.json file will not be included inside the production build.

    为了发展

    直接在项目根目录下的package.json包含配置,开发环境和构建脚本的依赖关系。 这些依赖项和package.json文件不会包含在生产版本中。

  • For your application

    The package.json inside app folder is the manifest file for your application. So whenever you need to install npm dependencies to be used in your application directly, you should install it against this package.json

    为您的应用

    app文件夹中的package.json是您的应用程序的清单文件。 因此,每当需要安装直接在应用程序中使用的npm依赖项时,都应针对此package.json安装它

The format of package.json is exactly same as that of Node's module. Your application’s startup script should be specified in main property inside your app/package.json.

package.json的格式与Node模块的格式完全相同。 应用程序的启动脚本应在app / package.json内部的main属性中指定。

app/package.json might look like this:

app/package.json可能看起来像这样:

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

You can create both package.json files either by entering the npm init command. You can also manually create these files. Install npm dependencies that are required for packaging the application by entering following command in your command line prompt:

您可以通过输入npm init命令来创建两个package.json文件。 您也可以手动创建这些文件。 通过在命令行提示符下输入以下命令来安装打包应用程序所需的npm依赖项:

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

创建启动脚本 (Creating your Startup Script)

app/main.js is the entry point of our application. This script is responsible for creating the main window and handling the system events. main.js should look like the following:

app / main.js是我们应用程序的入口。 该脚本负责创建主窗口并处理系统事件。 main.js应该如下所示:

// app/main.js

// Module to control application life.
var app = require('app'); 

// Module to create native browser window.
var BrowserWindow = require('browser-window');
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function () {

  // Create the browser window.
  mainWindow = new BrowserWindow({ width: 800, height: 600 });

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the devtools.
  // mainWindow.openDevTools();
  // Emitted when the window is closed.
  mainWindow.on('closed', function () {

    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

});

来自DOM的本地访问 (Native access from the DOM)

As I mentioned above, Electron enables you to access local npm modules and native APIs directly from your web pages. Create your app/index.html file as follows:

如上所述,Electron使您可以直接从网页访问本地npm模块和本机API。 创建您的app/index.html文件,如下所示:

<html>
<body> 
  <h1>Hello World!</h1>
   We are using Electron 
   <script> document.write(process.versions['electron']) </script> on 
   <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 is a simple HTML page. Here it reads your package.json using Node's fs (file system) module and writes the content into the document body.

app / index.html是一个简单HTML页面。 在这里,它使用Node的fs (文件系统)模块读取package.json并将内容写入文档主体。

运行应用程序 (Run the Application)

Once you have created the project structure, app/index.html, app/main.js, app/package.json , you'll probably want to try running your initial Electron application to test it and make sure it's working as expected.

一旦创建了项目结构app/index.htmlapp/main.jsapp/package.json ,您可能想要尝试运行初始的Electron应用程序以对其进行测试,并确保其按预期运行。

If you've installed electron-prebuilt globally in your system, you can run our application with following command:

如果您已在系统中全局安装了electron-prebuilt ,则可以使用以下命令运行我们的应用程序:

electron app

Here electron is the command to run electron shell and app is our application folder name. If you don't want to install Electron into your global npm modules, then run with your local modules installed into npm_modules folder as follows in your command line prompt:

这里的electron是运行电子外壳的命令,而app是我们的应用程序文件夹名称。 如果您不想将Electron安装到全局npm模块中,请在命令行提示符下将本地模块安装到npm_modules文件夹中运行:

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

While you could do it that way, I recommend you create a gulp task to run your application in your gulpfile.js, so that you can integrate your task into Visual Studio Code Editor which we will check in next section.

尽管可以这样做,但我建议您创建一个gulpfile.js 任务以在gulpfile.js运行您的应用程序,以便将您的任务集成到Visual Studio Code Editor中,我们将在下一部分中进行检查。

// get the dependencies
var gulp        = require('gulp'), 
  childProcess  = require('child_process'), 
  electron      = require('electron-prebuilt');

// create the gulp task
gulp.task('run', function () { 
  childProcess.spawn(electron, ['./app'], { stdio: 'inherit' }); 
});

Run your gulp task: gulp run Our application might looks like this:

运行您的gulp任务: gulp run我们的应用程序可能如下所示:

electron-app

使用Visual Studio代码配置开发环境 (Configuring Development Environment With Visual Studio Code)

Visual Studio Code is a cross platform code editor from Microsoft. Behind the scene of VS Code is Electron and Microsoft's proprietary monaco code editor. You can download Visual Studio Code here.

Visual Studio Code是Microsoft的跨平台代码编辑器。 VS Code的背后是Electron和微软专有的monaco代码编辑器。 您可以在此处下载Visual Studio Code。

Open your electron application in VS Code.

在VS Code中打开您的电子应用程序。

open-application

配置Visual Studio代码任务运行器 (Configure Visual Studio Code Task Runner)

Lots of tools exist to automate our tasks like building, packaging or testing our application. Mostly we run these tools from the command line. VS Code's built in task runner enables you to integrate your custom tasks into your project. You can run your grunt, gulp, MsBuild or any other tasks directly from within your project without moving to the command line.

存在许多工具可以自动化我们的任务,例如构建,打包或测试我们的应用程序。 通常,我们从命令行运行这些工具。 VS Code的内置任务运行器使您可以将自定义任务集成到项目中。 您可以直接在项目中运行grunt,gulp,MsBuild或任何其他任务,而无需移至命令行。

VS Code can detect your grunt and gulp tasks automaticaly. Press ctrl + shift + p then type Run Task followed by Enter.

VS Code可以自动检测您的咕unt声和吞咽任务。 按ctrl + shift + p然后键入Run Task然后按Enter

run-task

You will get all available tasks from your gulpfile.js or gruntfile.js.

您将从gulpfile.jsgruntfile.js获得所有可用任务。

Note that, you should have your gulpfile.js in root directory of your application.

请注意,您应该在应用程序的根目录中包含gulpfile.js

run-task-gulp

ctrl + shift + b will execute build task from your task runner. You can override this intergration using the task.json file. Press ctrl + shift + p then type Configure Task followed by Enter. This will create .setting folder and a task.json in your project . You need to configure the tasks in tasks.json if you want to do more than simply run the task. For example you might want to run the application when you press the Ctrl + Shift + B . To do this edit the task.json file as follows:

ctrl + shift + b将从任务运行器执行build任务。 您可以使用task.json文件覆盖此task.json 。 按ctrl + shift + p然后键入Configure Task然后按Enter 。 这将在您的项目中创建.setting文件夹和task.json。 如果您想做的不仅仅是运行任务,还需要在task.json中配置任务。 例如,当您按Ctrl + Shift + B时,可能要运行该应用程序。 为此,如下编辑task.json文件:

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

Root section says that the command is gulp. You can have more tasks inside tasks section as you want. Setting isBuildCommand true for a task will bind the Ctrl + Shift + B to that task. Currently VS Code supports one top level task only.

根节说该命令是gulp 。 您可以根据需要在“ tasks部分中包含更多任务。 为任务设置isBuildCommand true会将Ctrl + Shift + B绑定到该任务。 当前,VS Code仅支持一项顶级任务。

Now if you press Ctrl + Shift + B, gulp run will be executed.

现在,如果您按Ctrl + Shift + B ,则将执行gulp run

You can read more about visual studio code tasks here

您可以在此处阅读有关Visual Studio代码任务的更多信息

调试电子应用 (Debugging Electron Application)

Open the debug panel and click configure button which will create a launch.json file inside .settings folder with debug configuration.

打开调试面板,然后单击配置按钮,这将在.settings文件夹中创建带有调试配置的launch.json文件。

debug

We don't need launch app.js configuration, so remove it.

我们不需要启动app.js配置,因此将其删除。

Now your launch.json should be as follows:

现在,您的launch.json应该如下所示:

{ 
  "version": "0.1.0", 
  // List of configurations. Add new configurations or edit existing ones. 
  // ONLY "node" and "mono" are supported, change "type" to switch. 
  "configurations": [
    { 
      "name": "Attach", 
      "type": "node", 
      // TCP/IP address. Default is "localhost". 
      "address": "localhost", 
      // Port to attach to.
      "port": 5858, 
      "sourceMaps": false 
     } 
   ] 
}

Change your gulp run task that we created before as follows, so that our electron will start in debug mode and will listen to port 5858:

如下更改您之前创建的gulp run任务,以使我们的电子设备以调试模式启动并侦听端口5858:

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

In the debug panel choose "Attach" configuration and click run or press F5. After few seconds you should see the debug command panel in the top.

在调试面板中,选择“附加”配置,然后单击“运行”或按F5。 几秒钟后,您应该在顶部看到调试命令面板。

debug-star

创建AngularJS应用程序 (Creating the AngularJS Application)

New to AngularJS? Check out the official website here or some of the Scotch Angular tutorials.

AngularJS的新手? 在此处查看官方网站或一些Scotch Angular教程

This section explains how to create a simple Customer Manager application using AngularJS with a MySQL database as the backend. The goal of this application is not to highlight the core concepts of AngularJS but to demonstrate how to use the AngularJS and NodeJS together with MySQL backend inside GitHub Electron.

本节说明如何使用以MySQL数据库为后端的AngularJS创建简单的Customer Manager应用程序。 该应用程序的目的不是突出AngularJS的核心概念,而是演示如何在GitHub Electron中将AngularJS和NodeJS与MySQL后端一起使用。

Our Customer Manager application is as simple as the following:

我们的客户经理应用程序很简单,如下所示:

  • List the customers

    列出客户
  • Add New Customer

    添加新客户
  • Option to delete a customer

    选择删除客户
  • Search for the customer

    寻找客户

项目结构 (Project Structure)

Our application is located inside app folder, and the structure of the application is shown below.

我们的应用程序位于app文件夹内,其结构如下所示。

angular-project-structure

The home page is the app/index.html file. The app/scripts folder contains all the key scripts and views used in this application. There are several techniques that can be used for organizing application files.

主页是app/index.html文件。 app/scripts文件夹包含此应用程序中使用的所有关键脚本和视图。 有几种技术可用于组织应用程序文件。

Here I prefer scripts organized by features. Each feature has its own folder with templates and controllers inside the same folder. For more info on folder structure, read: AngularJS Best Practices: Directory Structure

在这里,我更喜欢按功能组织的脚本。 每个功能都有其自己的文件夹,并且同一文件夹中包含模板和控制器。 有关文件夹结构的更多信息,请阅读: AngularJS最佳实践:目录结构

Before get started with the AngularJS application, we're going to install client side dependencies using bower. Install Bower if you don't have it already. Change the current working directory to the root of the application in your terminal then install dependencies as follows from your command line prompt :

在开始使用AngularJS应用程序之前,我们将使用bower安装客户端依赖项。 如果尚未安装Bower ,请安装它。 将当前工作目录更改为终端中应用程序的根目录,然后在命令行提示符下按如下所示安装依赖项:

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

设置数据库 (Setting Up the Database)

For this demo I'll be using a database called customer-manager and a table called customers. Here is the dump of database so that you can get up and run quickly.

在本演示中,我将使用一个名为customer-manager的数据库和一个名为customers的表。 这是数据库的转储,因此您可以快速启动并运行。

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`) 
);

创建一个与MySQL交互的Angular服务 (Creating an Angular Service to Interact with MySQL)

Once you have your database and table ready, let's create an AngularJS service to access the data directly from this database. The service connects to database using node-mysql npm module - a NodeJs driver for MySql written in JavaScript. Install node-mysql module in your app/ folder where your Angular application resides.

准备好数据库和表后,让我们创建一个AngularJS服务以直接从该数据库访问数据。 该服务使用node-mysql npm模块连接到数据库,该模块是用JavaScript编写的MySql的NodeJs驱动程序。 在Angular应用程序所在的app /文件夹中安装node-mysql模块。

Note , we install node-mysql module inside app folder, not in the application root, as we need to include this module inside the final distribution.

注意,我们将node-mysql模块安装在app文件夹中,而不是在应用程序根目录中,因为我们需要将此模块包含在最终发行版中。

Change the current working directory to app folder in your command prompt install using following command.

使用以下命令在命令提示符安装中将当前工作目录更改为app文件夹。

npm install --save mysql

Our angular service - app/scripts/customer/customerService.js, should looks like following:

我们的角度服务-app / scripts / customer / customerService.js如下所示:

(function () {
    'use strict';
    var mysql = require('mysql');

    // Creates MySql database connection
    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 is a simple custom angular service that provides basic CRUD operations on customers table . It uses node's mysql module directly inside the service. If you already have a remote data service, you can use it instead.

customerService是一个简单的自定义角度服务,可在customers表上提供基本的CRUD操作。 它直接在服务内部使用节点的mysql模块。 如果您已经拥有远程数据服务,则可以改用它。

控制器和模板 (Controller & Template)

OurcustomerControllerinsideapp/scripts/customer/customerController is as follows:

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;

        // Load initial data
        getAllCustomers();

        //----------------------
        // Internal functions 
        //----------------------

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

})();

Our Customer template(app/scripts/customer/customer.html) uses angular material components to build user interface, and its is as follows:

我们的客户模板( app / scripts / customer / customer.html )使用角形材料组件来构建用户界面,其内容如下:

<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 contains module initialization script and application route config as follows:

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: '/' });
        }
    ]);

})();

Finally here is our index page app/index.html

最后是我们的索引页面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>

Run your application using gulp run command or press Ctrl + Shif + B if you already configured VS Code task runner as above.

使用gulp run命令运行您的应用程序,或者按Ctrl + Shif + B如果您已按上述方式配置VS Code任务运行程序)。

angular-app

编译AngularJS应用程序 (Build the AngularJS Application)

To build our Angular application install gulp-uglify, gulp-minify-css and gulp-usemin packages.

要建立我们的角应用程序安装gulp-uglifygulp-minify-cssgulp-usemin包。

npm install --save gulp-uglify gulp-minify-css gulp-usemin

Open your gulpfile.js and import required modules

打开您的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');

Clean build directory if it already exists.

清理构建目录(如果已存在)。

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

Copy files into build directory, We don't need to copy the angular application code using copy function. usemin will do this for us in next section:

将文件复制到构建目录,我们不需要使用复制功能复制角度应用程序代码。 在下一部分中, usemin将为我们做到这一点:

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

Our build task takes our app/index.html with gulp.src() and then we pipe it to usemin. It then writes the output into build directory and replace references in index.html with optimized version of code.

我们的构建任务将我们的app / index.html与gulp.src()一起使用,然后将其通过管道传递给usemin。 然后,将输出写入构建目录,并使用优化的代码版本替换index.html中的引用。

Note: Don't forget to define usemin blocks inside app/index.html as follows:

注意:不要忘记在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 -->

The build task should look as follows:

构建任务应如下所示:

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

准备分发 (Preparing for Distribution)

In this section we will package our Electron application for production. Create your build script in root directory as build.windows.js. This script is intended to be used on Windows. For other platform you should create scripts specific to that platform and should run according to your platform.

在本节中,我们将打包我们的Electron应用程序以进行生产。 在根目录build.windows.js构建脚本创建为build.windows.js 。 该脚本旨在在Windows上使用。 对于其他平台,您应该创建特定于该平台的脚本,并应根据您的平台运行。

A typical electron distribution can be found inside node_modules/electron-prebuilt/dist directory. Here is our steps to build the electron application:

node_modules/electron-prebuilt/dist目录中可以找到典型的电子分布。 这是我们建立电子应用程序的步骤:

  • Our very first task to do is to copy electron distribution into our dist folder.

    我们要做的第一个任务是将电子分布复制到dist文件夹中。

  • Each electron distribution contains a default application inside dist/resources/default_app folder. We need to replace this application with our final angular application build.

    每个电子分布都在dist/resources/default_app文件夹中包含一个默认应用程序。 我们需要用最终的角度应用程序构建替换该应用程序。

  • To protect our application’s source code and resources from users, you can choose to package your app into an asar archive with little changes to your source code. An asar archive is a simple tar-like format that concatenate files into a single file, Electron can read arbitrary files from it without unpacking the whole file.

    为了保护我们应用程序的源代码和资源免受用户的侵害,您可以选择将应用程序打包到一个asar存档中,而对源代码的更改很少。 asar归档文件是一种简单的类似tar的格式,可将文件连接成一个文件,Electron可以从其中读取任意文件,而无需解压缩整个文件。

Note: this section describes about packaging in windows platform. All these steps are same for other platforms but paths and files used here is different for other platforms. You can get the full build scripts for OSx and linux here in github.

注意:本节介绍有关在Windows平台中打包的信息。 对于其他平台,所有这些步骤都是相同的,但是此处使用的路径和文件对于其他平台是不同的。 您可以在github中获得OSx和linux的完整构建脚本。

Install dependencies required to build the electron as following: npm install --save q asar fs-jetpack recedit.

安装构建电子所需的依赖项,如下所示: npm install --save q asar fs-jetpack recedit

Next initialize our build script as follows:

接下来,初始化构建脚本,如下所示:

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() { 
    // Project directory is the root of the application
    projectDir = jetpack; 
    // Build directory is our destination where the final build will be placed 
    buildDir = projectDir.dir('./dist', { empty: true }); 
    // angular application directory 
    appDir = projectDir.dir('./build'); 
    // angular application's package.json file 
    manifest = appDir.read('./package.json', 'json'); 
    return Q(); 
}

Here we use fs-jetpack node module for the file operation. It gives more flexibility on file operations.

在这里,我们使用fs-jetpack节点模块进行文件操作。 它为文件操作提供了更大的灵活性。

复制电子分布 (Copy Electron Distribution)

Copy the default electron distribution from electron-prebuilt/dist to our dist directory.

将默认的电子分布从electron-prebuilt/dist复制到我们的dist目录。

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

清理默认应用程序 (Cleanup the Default Application)

You can find a default HTML application inside resources/default_app folder. We need to replace this application with our angular application. Remove it as follows:

您可以在resources/default_app文件夹中找到默认HTML应用程序。 我们需要用我们的角度应用程序替换该应用程序。 如下删除它:

Note: Here path is specific to windows platform. For other platforms process is same but path is different. In OSX it's Contents/Resources/default_app

注意:这里的路径特定于Windows平台。 对于其他平台,过程相同,但路径不同。 在OSX中,它是目录/资源/ default_app

function cleanupRuntime() { 
     return buildDir.removeAsync('resources/default_app'); 
}

创建asar包 (Create asar package)

function createAsar() { 
     var deferred = Q.defer(); 
     asar.createPackage(appDir.path(), buildDir.path('resources/app.asar'), function () { 
         deferred.resolve(); 
     }); 
     return deferred.promise; 
}

This combines all your angular application files into single asar package file. You can find the destination asar file in dist/resources/ directory.

这会将您所有的角度应用程序文件合并为单个asar软件包文件。 您可以在dist/resources/目录中找到目标asar文件。

用自己的资源替换应用程序资源 (Replace application resources with your own)

Next step is to replace the default electron icon with your own, update the product information and rename the application.

下一步是用您自己的图标替换默认的电子图标,更新产品信息并重命名应用程序。

function updateResources() {
    var deferred = Q.defer();

    // Copy your icon from resource folder into build folder.
    projectDir.copy('resources/windows/icon.ico', buildDir.path('icon.ico'));

    // Replace Electron icon for your own.
    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;
}
//Rename the electron exe 
function rename() {
    return buildDir.renameAsync('electron.exe', manifest.name + '.exe');
}

创建本机安装程序 (Creating Native Installers)

You can either use wix or NSIS to create windows installer. Here we use NSIS which is designed to be small and flexible as possible and is very suitable for internet distribution. With NSIS you can create such installers that are capable of doing everything that is needed to setup your software.

您可以使用wix或NSIS创建Windows安装程序。 在这里,我们使用NSIS,它设计得尽可能小巧,灵活,非常适合Internet分发。 使用NSIS,您可以创建这样的安装程序,它们能够执行设置软件所需的一切。

Create NSIS script in resources/windows/installer.nsis

在resources / windows / installer.nsis中创建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

Create a function called createInstaller in your build.windows.js file as follows:

build.windows.js文件中创建一个名为createInstaller的函数,如下所示:

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;

}

You should have NSIS installed, and should be available in your path. creaeInstaller function reads installer script and execute it against NSIS runtim with makensis command.

您应该已经安装了NSIS,并且在您的路径中应该可用。 creaeInstaller函数读取安装程序脚本,并使用makensis命令针对NSIS runtim执行该脚本。

放在一起 (Putting It All Together)

Create a function to put all pieces together, then export it to be accessed from gulp task:

创建一个将所有部分放在一起的函数,然后将其导出以从gulp任务中进行访问:

function build() { 
    return init()
            .then(copyElectron) 
            .then(cleanupRuntime) 
            .then(createAsar) 
            .then(updateResources) 
            .then(rename) 
            .then(createInstaller); 
    }
    module.exports = { build: build };

Next create gulp task in gulpfile.js to execute this build script:

接下来在gulpfile.js创建gulpfile.js任务以执行以下构建脚本:

var release_windows = require('./build.windows'); 
 var os = require('os'); 
 gulp.task('build-electron', ['build'], function () { 
     switch (os.platform()) { 
         case 'darwin': 
         // execute build.osx.js 
         break; 
         case 'linux': 
         //execute build.linux.js 
         break; 
         case 'win32': 
         return release_windows.build(); 
     } 
});

You can have your final product by running:

您可以通过运行以下命令获得最终产品:

gulp build-electron

Your final electron application should be in dist folder and folder structure should be some thing similar to following.

您最终的电子应用程序应位于dist文件夹中,并且文件夹结构应类似于以下内容。

dist-structure

摘要 (Summary)

Electron is not just a native web view that let's you to wrap your web application into desktop application . It now includes automatic app updates, Windows installers, crash reporting, notifications, and other useful native app features — all exposed through JavaScript APIs.

Electron不仅是一种本地Web视图,还可以让您将Web应用程序包装到桌面应用程序中。 现在,它包括自动应用程序更新,Windows安装程序,崩溃报告,通知和其他有用的本机应用程序功能-所有这些功能都通过JavaScript API公开。

So far there is huge range of apps created using electron including chat apps, database explorers, map designers, collaborative design tools, and mobile prototyping apps.

到目前为止,有大量使用电子工具创建的应用程序,包括聊天应用程序,数据库浏览器,地图设计师,协作设计工具以及移动原型应用程序。

Here is some useful resources about Github Electron:

这是有关Github Electron的一些有用资源:

翻译自: https://scotch.io/tutorials/creating-desktop-applications-with-angularjs-and-github-electron

github桌面应用下载慢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值