标签 ionic 下的文章

Ionic V2 与 Ionic V1 一样提供了 CLI 工具与 GUI 的工具。

安装 Ionic V2

安装 Ionic 2,可以使用下面的命令:

npm install -g ionic@beta
完全不需要担心 Ionic V1 版本的项目,Ionic@Beta 可以完全兼容 Ionic V1 的项目。

安装完成之后可以使用下面的命令创建新项目:

ionic start cutePuppyPics --v2

运行新创建的项目, cd 进入项目目录之后,运行 ionic serve 命令即可:

cd cutePuppyPics
ionic serve

执行之后,即可像 Ionic v1 一样在浏览器中查看项目了。

构建

要构建 Ionic 项目,需要先安装 cordova :

sudo npm install -g cordova

为 iOS 构建

ionic platform add ios
你需要先安装 XCode,XCode 允许你直接为 iOS 系统的目标设备构建应用。添加了 iOS 系统之后,即可使用下面的命令运行模拟器:
ionic emulate ios

为 Android 构建

ionic platform add android

接下来你还需要安装 Android SDK ,Android SDK 允许你为 Android 目录设备构建应用,虽然 Android SDK 本身就带了一个模拟器,但是更加推荐你使用 Genymotion:

ionic run android

迁移

Ionic 1.x 是基于 Angular 1.x 技术的,同样的 Ionic 2.x 基于 Angular 2.x,所以,虽然 Ionic 本身的理念没有太多改变,但是代码的写法也因为 Angular 的改变而变得很不一样,Angular 2.x 使用了完全不一样的语法与代码结构(要了解 Angular 2.x 的变化,可以查看 学习 Angular 2 这个网站。

下面这个是 Ionic 1.x 中的写法:

.config(function($stateProvider) {
  $stateProvider
  .state('main', {
    url: '/',
    templateUrl: 'templates/main.html',
    controller: 'MainController'
  })
})

.controller('MainController', function() {

})

现在在 Ionic 2.x 中可以像下面这样写:

@Page({
  templateUrl: 'main/main.html'
})

export class MainCmp {
  constructor() {

  }
}

从 Angular 1.x 迁移

ControllerAs 语法是一个 Angular 1.x 提供的功能,它可以我们不需要使用 $scope 变量即可做到数据绑定,而是将数据直接绑定至 Controller 上,在 Angular 2.x 中,ControllerAs 的实现更加简单了,对比下面是 Angular 1.x 实现:

index.html

<ion-content ng-controller="MainController">
  <ion-item>
    {{ data.text }}
  </ion-item>
</ion-content>

app.js

.controller('MainController', function($scope) {
  $scope.data = {
    text: 'hello world'
  }
})

将上面的示例改成 ControllerAs 语法只需要改变很小的一点代码:

index.html

<ion-content ng-controller="MainController as main">
  <ion-item>
    {{ data.text }}
  </ion-item>
</ion-content>

app.js

.controller('MainController', function() {
  this.data = {
    text: 'Hello World'
  }
})

TypeScript

TypeScript 是一个提供了 ES6 类 与类型注释的 JavaScript 超集 ,这使得我们可以在 Ionic 项目中按照 ES6 的标准来写。

app.js

.controller('MainController', function() {
  this.data = {
    text: 'Hello World'
  }
})

app.ts

export class MainController {
  constructor() {
    this.data = {
      text: 'Hello World'
    }
  }
}

项目结构

在 Angular 1.x 中,最好的项目实践是将所有的 JavaScript 脚本都放在一起,模板文件也放在一起,但是他们两者却是分开的,比如下面这样:

|-www/
|
|--js/
|--|-app.js
|--|-HomeController.js
|--|-DetailController.js
|
|--templates/
|--|-Home.html
|--|-Detail.html
|
|-index.html

但是在 Angular 2.x 中,推荐像下面这样的:

|-www/
|
|--Home/
|--|-HomeController.js
|--|-Home.html
|
|--Detail/
|--|-DetailController.js
|--|-Detail.html
|
|-index.html
|-app.js

doraemoney-loan-app.jpg

6月14号,和另外两个同事商量着不能再像最近这几个月这样了,似乎每一个公司的产品经理与码农们都是死对头,我也没有逃出这个怪圈,每天在对产品的“精雕细琢”中,让我对产品越发的反感,不经意间,看了看自己的 Git Commits List,好长啊,每天都有好多,然后就想着看看自己的干了些什么,突然之间,发现这就是一个循环啊,基本上是下面这样的:

for var keepGoing = true; keepGoing  {
    // 4B中
}

不行啊,我们得自己整一个,但是不能在上班时间整,因为这是一个只有我们参与的事情,而且也不希望他人对我们的指指点点,所以,决定每天的空余时间抽出几个小时,计划着一个星期之内整一个新的东西出来,恩,是的,App,最后还是花了我们3个人十天的时间。

这还是一个借款给有需要的人的App,没有风控模型,但是我们有完善的催债模型和真实性模型,我们只做一件事情,让借款给你的人更快的相信你在按时还款,所以,我们选择了通讯录、通话记录、地理位置、手机型号等这些通过一个App能快速获取到的数据。

然后我们开始了规划,简单的设计,接口的定义以及数据结构的定义,这花了一天的时间,我们按着花了三天时间把整个系统开发完了,也就是所有的功能都有了,接着花了两天时间把所有的功能接口串连上,最后再连了四天的时间测试、调试与Bug修复,Ok,一个全新的App就这么出来了。

技术使用的是:

  • Java
  • Ionic
  • Cordova
  • 一些必要的插件

Java

选择Java的原因很简单,也很纯粹,我们的核心业务系统就是Java的,为了能更快速的开发,我们还是直接使用Java,这样很多接口的代码可以直接复制过来改改就能使用,这为我们节省了很多开发的时间。

Ionic

这个不用想,简单的App开发中的神器,有了这个东西,即使我对App开发一无所知,我也能仅使用我自己会的前端技术实现一个完善的App。

Cordova

这为我们的App兼容到各种平台 iOA/Andoird等提供支持。

我是怎么做的

关于本地的数据存储

因为数据量很少,所以直接使用了 LocalStorage,我自己写了一个 AngularJS 与 LocalStorage 的数据绑定的 Angular Module,代码如下:

/**
 * 本地存储
 */
app.factory('$storage', [
  '$rootScope',
  '$window',
  function(
      $rootScope,
      $window
  ){
    // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
    var webStorage = $window['localStorage'] || (console.warn('This browser does not support Web Storage!'), {}),
        storage = {
          $default: function(items) {
            for (var k in items) {
              angular.isDefined(storage[k]) || (storage[k] = items[k]);
            }

            return storage;
          },
          $reset: function(items) {
            for (var k in storage) {
              '$' === k[0] || delete storage[k];
            }

            return storage.$default(items);
          }
        },
        _laststorage,
        _debounce;

    for (var i = 0, k; i < webStorage.length; i++) {
      // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)

      (k = webStorage.key(i)) && 'storage-' === k.slice(0, 8) && (storage[k.slice(8)] = angular.fromJson(webStorage.getItem(k)));
    }

    _laststorage = angular.copy(storage);

    $rootScope.$watch(function() {
      _debounce || (_debounce = setTimeout(function() {
        _debounce = null;

        if (!angular.equals(storage, _laststorage)) {
          angular.forEach(storage, function(v, k) {
            angular.isDefined(v) && '$' !== k[0] && webStorage.setItem('storage-' + k, angular.toJson(v));

            delete _laststorage[k];
          });

          for (var k in _laststorage) {
            webStorage.removeItem('storage-' + k);
          }

          _laststorage = angular.copy(storage);
        }
      }, 100));
    });

    // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
    'localStorage' === 'localStorage' && $window.addEventListener && $window.addEventListener('storage', function(event) {
      if ('storage-' === event.key.slice(0, 10)) {
        event.newValue ? storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete storage[event.key.slice(10)];

        _laststorage = angular.copy(storage);

        $rootScope.$apply();
      }
    });

    return storage;
  }
]);

使用起来很简单:

$storage.token = 'TOKEN_STRING'; // 这就会在localStorage 中存储一个 `key` 为 `storage-token` 而 `value` 为 `TOKEN_STRING` 的键值对,这是一个单向存储的过程,也就是我们再手工修改 `localStorage` 里面的值是没有用的,`100ms` 之后就会被 `$storage.token` 的值覆盖,这是一个更新存储的时间。

数据请求

因为我们这边的接口走的不是 AngularJS 的默认请求方式,数据结构为类似表单提交,所以,我还修改了 Angular 中的 $http,转换对象为 x-www-form-urlencoded 序列代的字符串:

/**
 * 配置
 */
app.config([
  '$ionicConfigProvider',
  '$logProvider',
  '$httpProvider',
  function(
      $ionicConfigProvider,
      $logProvider,
      $httpProvider
  ) {
    // .. 其它代码
    // 开启日志
    $logProvider.debugEnabled(true);

    /**
     * 服务器接口端要求在发起请求时,同时发送 Content-Type 头信息,且其值必须为: application/x-www-form-urlencoded
     * 可选添加字符编码,在此处我默认将编码设置为 utf-8
     *
     * @type {string}
     */

    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
    $httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';

    /**
     * 请求只接受服务器端返回 JSON 数据
     * @type {string}
     */
    $httpProvider.defaults.headers.post['Accept'] = 'application/json';

    /**
     * AngularJS 对默认提交的数据结构为 json 格式的,但是我们NiuBilitity的服务器端不能解析 JSON 数据,所以
     * 我们经 x-www-form-urlencoded 的方式提交,此处将对数据进行封装为 foo=bar&bar=other 的方式
     * @type {*[]}
     */
    $httpProvider.defaults.transformRequest = [function(data) {
      /**
       * 转换对象为 x-www-form-urlencoded 序列代的字符串
       * @param {Object} obj
       * @return {String}
       */
      var param = function(obj) {
        var query = '';
        var name, value, fullSubName, subName, subValue, innerObj, i;

        for (name in obj) {
          value = obj[name];

          if (value instanceof Array) {
            for (i = 0; i < value.length; ++i) {
              subValue = value[i];
              fullSubName = name + '[' + i + ']';
              innerObj = {};
              innerObj[fullSubName] = subValue;
              query += param(innerObj) + '&';
            }
          } else if (value instanceof Object) {
            for (subName in value) {
              subValue = value[subName];
              fullSubName = name + '[' + subName + ']';
              innerObj = {};
              innerObj[fullSubName] = subValue;
              query += param(innerObj) + '&';
            }
          } else if (value !== undefined && value !== null) {
            query += encodeURIComponent(name) + '='
                + encodeURIComponent(value) + '&';
          }
        }

        return query.length ? query.substr(0, query.length - 1) : query;
      };

      return angular.isObject(data) && String(data) !== '[object File]'
          ? param(data)
          : data;
    }];

  }
]);

JSON 请求数据结构

我们的数据结构是下面这样的:

Request

{
  "apiVersion" : "0.0.1",
  "token" : "TOKEN_STRING",
  "requestId" : "ID_STRING",
  "data" : {
    // Data goes here
  }
}

Response

{
  "apiVersion" : "0.0.1",
  "data" : {},
  "error" : {
    "code" : ERROR_CODE_NUMBER,
    "message" : "Error Message Here",
    "errors" : [
      {
        "code" : 0,
        "message" : "",
        "location" : ""
      }
    ]
  }
}

说明

在上面的这些数据结构中,请求的很好理解,响应的 json 结构只有三个字段, apiVersion 表示了当前请求的接口版本号, data 就是数据对象, error 则是错误对象,一般情况下,一个 error 只有 code 与 message 两个值,但是有一些情况下可能会需要提供一些额外的错误信息,那么都放入了 error.errors 这个数组中。

App前端是下面这样的判断的:

  1. 当 error 为 null 时,表示请求成功,此时从 data 中取数据;
  2. 当 error 不为 null 时,表示请求失败,此时从 error 中取错误信息,而完全不管 data ,我采取的方式是直接抛弃(其实前后端已经约定了,所以不存在 error 不为 null 时,data 中还有数据的情况出现。

关于 $http

我没有直接将接口的 url 地址、$http 请求等暴露给 Controller,而是做了一层封装,我叫作为 sack(也就是 App 的名称):

app.factory('sack', [
  '$http',
  '$q',
  '$log',
  '$location',
  '$ionicPopup',
  '$storage',
  'API_VERSION',
  'API_PROTOCOL',
  'API_HOSTNAME',
  'API_URI_MAP',
  'util',
  function(
      $http,
      $q,
      $log,
      $location,
      $ionicPopup,
      $storage,
      API_VERSION,
      API_PROTOCOL,
      API_HOSTNAME,
      API_URI_MAP,
      util
  ){
    var HTTPUnknownError = {code: -1, message: '出现未知错误'};
    var HTTPAuthFaildError = {code: -1, message: '授权失败'};
    var APIPanicError = {code: -1, message: '服务器端出现未知错误'};
    var _host = API_PROTOCOL + '://' + API_HOSTNAME + '/',
        _map = API_URI_MAP,
        _apiVersion = API_VERSION,
        _token = (function(){return $storage.token;}()) ;

    setInterval(function(){
      _token = (function(){return $storage.token;}());
      //$log.info("Got Token: " + _token);
    }, 1000);

    var appendTransform = function(defaultFunc, transFunc) {
      // We can't guarantee that the default transformation is an array
      defaultFunc = angular.isArray(defaultFunc) ? defaultFunc : [defaultFunc];

      // Append the new transformation to the defaults
      return defaultFunc.concat(transFunc);
    };

    var _prepareRequestData = function(originData) {
      originData.token = _token;
      originData.apiVersion = _apiVersion;
      originData.requestId = util.getRandomUniqueRequestId();
      return originData;
    };

    var _prepareRequestJson = function(originData) {
      return angular.toJson({
        apiVersion: _apiVersion,
        token: _token,
        requestId: util.getRandomUniqueRequestId(),
        data: originData
      });
    };

    var _getUriObject = function(uon) {
      // 若传入的参数带有 _host 头
      if((typeof uon === 'string' && (uon.indexOf(_host) == 0) ) || uon === '') {
        return {
          uri: uon.replace(_host, ''),
          methods: ['post']
        };
      }

      if(typeof _map === 'undefined') {
        return {
          uri: '',
          methods: ['post']
        };
      }

      var _uon = uon.split('.'),
          _ns,
          _n;

      if(_uon.length == 1) {
        return {
          uri: '',
          methods: ['post']
        };
      }
      _ns = _uon[0];
      _n = _uon[1];

      _mod = _map[_ns];

      if(typeof _mod === 'undefined') {
        return {
          uri: '',
          methods: ['post']
        };
      }

      _uriObject = _mod[_n];

      if(typeof _uriObject === 'undefined') {
        return {
          uri: '',
          methods: ['post']
        };
      }

      return _uriObject;
    };

    var _getUri = function(uon) {
      return _getUriObject(uon).uri;
    };

    var _getUrl = function(uon) {
      return _host + _getUri(uon);
    };

    var _auth = function(uon) {
      var _uo = _getUriObject(uon),
          _authed = false;
      $log.log('Check Auth of : ' + uon);
      $log.log('Is this api need auth: ' + angular.toJson(_uo.needAuth));
      $log.log('Is check passed: ' + angular.toJson(!(!_token && _uo.needAuth)));
      $log.log('Token is: ' + _token);
      if(!_token && _uo.needAuth) {
        
        $ionicPopup.alert({
          title: '提示',
          subTitle: '您当前的登录状态已失效,请重新登录。'
        }).then(function(){
          $location.path('/sign');
        });
        
        $location.path('/sign');
      } else {
        _authed = true;
      }
      return _authed;
    };

    var get = function(uon) {
      return $http.get(_getUrl(uon));
    };

    var post = function(uon, data, headers) {
      var _url = _getUrl(uon),
          _data = _prepareRequestData(data);
      $log.info('========> POST START [ ' + uon + ' ] ========>');
      $log.log('REQUEST URL  : ' + _url);
      $log.log('REQUEST DATA : ' + angular.toJson(_data));

      return $http.post(_url, _data, {
        transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
          $log.log('RECEIVED JSON : ' + angular.toJson(value));
          if(typeof value.ex != 'undefined') {
            return {
              error: APIPanicError
            };
          }
          return value;
        })
      });
    };

    var promise = function(uon, data, headers) {
      var defer = $q.defer();

      if(!_auth(uon)) {
        defer.reject(HTTPAuthFaildError);
        return defer.promise;
      }

      post(uon, data, headers).success(function(res){
        if(res.error) {
          defer.reject(res.error);
        } else {
          defer.resolve(res.data);
        }
      }).error(function(res){
        defer.reject(HTTPUnknownError);
      });
      return defer.promise;
    };

    var postJson = function(uon, data, headers) {
      var _url = _getUrl(uon),
          _json = _prepareRequestJson(data);
      $log.info('========> POST START [ ' + uon + ' ] ========>');
      $log.log('REQUEST URL  : ' + _url);
      $log.log('REQUEST JSON : ' + _json);
      return $http.post(_url, _json, {
        transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
          $log.log('RECEIVED JSON : ' + angular.toJson(value));
          if(typeof value.ex != 'undefined') {
            return {
              error: APIPanicError
            };
          }
          return value;
        })
      });
    };

    var promiseJson = function(uon, data, headers) {
      var defer = $q.defer();

      if(!_auth(uon)) {
        defer.reject(HTTPAuthFaildError);
        return defer.promise;
      }

      postJson(uon, data, headers).success(function(res){
        if(res.error) {
          defer.reject(res.error);
        } else {
          defer.resolve(res.data);
        }
      }).error(function(res){
        defer.reject(HTTPUnknownError);
      });
      return defer.promise;
    };

    return {
      get: get,
      post: post,
      promise: promise,

      postJson: postJson,
      promiseJson: promiseJson,
      _auth: _auth,
      HTTPAuthFaildError: HTTPAuthFaildError
    };
  }
]);

这样里面最主要是使用一个方法: sack.promiseJson,这个方法是以 json 数据向服务器发送请求,然后返回一个 promise 的。

上面的 API_URI_MAP 的数据结构类似于下面这样的:

app.constant('API_URI_MAP', {
  user : {
    sign : {
      needAuth: false,
      uri : 'sack/user/sign.json',
      methods: [
        'post'
      ],
      params: {
        mobile: 'string', // 手机号码
        captcha: 'string' // 验证码
      }
    },
    unsign: {
      needAuth: true,
      uri: 'sack/user/unsign.json',
      methods: [
        'post'
      ],
      params: {
        token: 'string'
      }
    },
    //...
  }
  //...
});

然后,更具体的,在 Controller 中也不直接使用 sack.promiseJson 这个方法,而是使用封装好的服务进行,比如下面这个服务:

app.factory('UserService', function($rootScope, $q, $storage, API_CACHE_TIME, sack) {
  var sign = function(data) {
    return sack.promiseJson('user.sign', data);
  };

  return {
    sign: sign
  }
});

这样的好处是,我可以直接使用类似下面这样发起请求:

UserService.sign({mobile:'xxxxxxxxxxx',captcha:'000000'}).then(function(res){
  // 授权成功
}, function(err){
  // 授权失败
});

但是

好吧,又来但是了,App做完了之后,我们可爱的领导们感觉这个还可以,然后就又要开始发挥他们的各种NB的指导了,还好从一开始我们就没有使用上班时间,这使得我们有理由拒绝领导的指导,但是,公司却说了,不接受指导那就不让上,好吧,那就不上呗,这似乎惹怒了我们的领导们,所以,就直接没有跟我们通气的开始招兵买马要上App了,我瞬间就想问:

我们的战略不是说不做App么?现在怎么看到App比现在的简单就又开始做了

然后我又想到一种可能

  1. 我们把App上了,
  2. 另一个领导带招一些新人把也做了一个App
  3. 如果App还可以的话,把我们的功能直接复制过去,然后让我们的下线
  4. 然后领导又可以邀功了
  5. 如果App不可以的话,那我们是在浪费时间,把我们的下线,然后……

反正,似乎都跟我没半毛钱关系了,除非这个App运营的不好。

不知道 Cordova 、Ionic、AngularJS 为何物的,请点击下方链接自行恶补:

需求

最后开始接触一些基于 Ionic + Cordova + AngularJS技术的移动App的开发,然后就遇到了很多都是需要有一个分享功能的,尤其是微信的分享,这也是第一次搞这种事情啊,没办法,折腾了好多天,解决了这个问题之后发现,原来是如此的简单。

准备好你的App

我在这里创建一个名为 WechatShareDemoApp 的新的App,在工作目录中运行如下命令:

ionic start WechatShareDemoApp tabs

这会使用 Ionic Tabs Seed 创建一个基于标签导航的空的App,创建成功之后,使用任何一个你喜欢的编辑器编辑该项目,我使用的是 WebStorm。

为 DashCtrl 增加一个 share(title,desc,url,thumb) 文法

share(title,desc,url,thumb) 方法用来打开一个 ActionSheet 面板,在该面板中,会提供两个分享按钮,一个用于分享内容至朋友圈,一个用于分享至会话,代码如下:

// 上面代码省略
.controller('DashCtrl', function($scope, $ionicActionSheet) {
    $scope.share = function(title, desc, url, thumb) {
        $ionicActionSheet.show({
            buttons: [
                { text: '<b>分享至微信朋友圈</b>' },
                { text: '分享给微信好友' }
            ],
            titleText: '分享',
            cancelText: '取消',
            cancel: function() {
                // 取消时执行
            },
            buttonClicked: function(index) {
                if(index == 0) {
                    $scope.shareViaWechat(WeChat.Scene.timeline, title, desc, url, thumb);
                }
                if(index ==1 ) {
                    $scope.shareViaWechat(WeChat.Scene.session, title, desc, url, thumb);
                }
            }
        });
    };
})
// 下面代码省略

share(title,desc,url,thumb) 的运行方式是,点击该方法被调用时,打开一个 ActionSheet ,点击 分享至微信朋友圈 按钮被点击时,执行 $scope.shareViaWechat(WeChat.Scene.timeline, title, desc, url, thumb),点击 分享给微信好友 时,执行 $scope.shareViaWechat(WeChat.Scene.session, title, desc, url, thumb),前者分享内容至朋友圈,后者分享内容至会话,但是,$scope.shareViaWechat 方法本身是不存在的,所以,我们还需要添加该方法。

为 DashCtrl 添加 $scope.shareViaWechat 方法

该方法使用了第三方的 Cordova 插件提供的方法 WeChat.share,该方法可以将内容分享至微信中,代码如下:

// 前面代码省略
.controller('DashCtrl', function($scope, $ionicActionSheet, $ionicPopup) {

    //......        

    $scope.shareViaWechat = function(scene, title, desc, url, thumb) {
        // 创建消息体
        var msg = {
            title: title ? title : "行者无疆",
            description: desc ? desc : "A real traveller's province is boundless.",
            url: url ? url : "https://pub.ofcrab.com",
            thumb: thumb ? thumb : null
        };

        WeChat.share(msg, scene, function() {
            $ionicPopup.alert({
                title: '分享成功',
                template: '感谢您的支持!',
                okText: '关闭'
            });
        }, function(res) {
            $ionicPopup.alert({
                title: '分享失败',
                template: '错误原因:' + res + '。',
                okText: '我知道了'
            });
        });
    };
})
// 后面代码省略

在上面的代码中, WeChat 是由第三方插件提供的,至现在为止,分享功能已经做完,我们现在需要在 views 中添加分享功能的激活按钮。

添加分享按钮

打开 templates/tab-dash.html,在 ion-content 结束前,添加如下代码:

<button class="button button-assertive button-outline button-block" ng-click="share()">分享</button>

该按钮会打开分享的 ActionSheet,我们不需要传任何参数,因为在上面的分享方法中,对没有设定的参数已经提供了默认的内容了。

此时,应用的界面如下图所示:

微信分享 ActionSheet.jpg

添加微信分享插件

虽然样子已经成型了,但是还是不能进行分享的,我们需要安装一个插件,该插件使用到了微信官方的第三方开发库,需要先在微信开放平台申请一个AppId,然后还需要使用 Gen_Signature_Android221cbf.zip 插件从手机中根据包名获取一个 Signature Code,该代码还必须填写进入开放平台中,在该过程中,若我们的App是运行在Android平台的话,还涉及到 keystore 的问题,这里不做过多的阐述,若我们的项目为新项目的话,还需要先生成一个Android安装包(必须生成 Apk 包之后安装),安装至某一个手机中,然后在该手机中使用上面的 Gen_Signature_Android221cbf 应用获取 Signature 值,再填入开放平台的相应设置中。

您在申请了AppID之后,还需要了解到你需要将你的包名也设置进入开放平台中,该包名在 Ionic 项目中的, config.xml 文件中可以进行设置。

该插件必须在 Platform 添加之后安装,所以我们先添加一个 Platform,比如 Android:

ionic platform add android

在 App 的根目录下使用下面的命令即可:

cordova plugin add com.wordsbaking.cordova.wechat --variable APP_ID=[你的APPID]

上面的 APP_ID 为你在微信开放平台申请的 App ID,安装该插件时,必须添加至命令中。

打包并安装使用

将新生成的项目专稿 Eclipse 中,打包并安装测试。