前言javascript
如今移动端的大势所趋,凡是项目势必都会有移动端的需求,那么今天就来说讲移动端开发吧。html
当今android、ios的开发,若是组建原生开发团队来开发的话,费用仍是很大的,并且如今很多android应用也都是结合html来进行开发的。java
最近阿里也顺势推出了weex,我还没去体验,不过按照阿里以往的尿性,当初推出kissy时也是号称各类牛逼烘烘的技术,结果开发的过程中却出现了各类各样的坑,等到能真的实际使用上也是好几年之后的事情了。node
cordova跟weex是比较类似的,从2011年开始到如今,通过了这么多年的发展,api更加稳定,资源比较丰富。android
废话就到这里了,开始码代码吧,文章中使用的是cordova+angularjs。ios
建立项目angularjs
首先须要安装nodejs,而后经过npm安装cordova,完成了环境的需求后,就能够经过以下命令建立一个cordova项目:web
cordova create 项目名
至于项目内的文件结构这里就很少介绍了,园内有许多优秀的文章有详细说明。npm
以上只是稍微简介一下,因为cordova的特殊性,使咱们能够使用构建web的方式来构建手机应用,所以接下来的文章会介绍如何构建一个既能够在浏览器上进行测试,又能够在手机中运行的应用。bootstrap
示例
首先看一下例子,代码以下:
<body ng-app="app" ng-controller="ctrl.index"> <div ng-view> </div> </body> <script id="init" type="text/ng-template"> 初始化中,{{ count }}秒后完成 </script> <script id="main" type="text/ng-template"> 设备信息: {{ deviceId }} </script> <script type="text/javascript" src="js/angular.min.js"></script> <script type="text/javascript" src="js/angular-route.min.js"></script> <script type="text/javascript"> var app = angular.module('app', ['ngRoute']); app.config([ '$routeProvider', function ($routeProvider) { $routeProvider.when('/init', { controller: 'ctrl.init', templateUrl: 'init' }).when('/main', { controller: 'ctrl.main', templateUrl: 'main' }); } ]); app.controller('ctrl.index', [ '$location', function ($location) { $location.path('/init'); } ]).controller('ctrl.init', [ '$interval', '$location', '$scope', function ($interval, $location, $scope) { $scope.count = 5; $interval(function () { if ($scope.count != 1) return $scope.count--; $location.path('/main'); }, 1000); } ]).controller('ctrl.main', [ '$scope', function ($scope) { $scope.deviceId = '未知'; } ]) </script>
因为cordova提供的调用Native并非当即就可以使用的,所以须要让angular的初始化延迟到deviceready事件中进行,代码调整以下:
//移除ng-app <body ng-controller="ctrl.index"> //angular延迟初始化 <script type="text/javascript"> if (true) { angular.bootstrap(document.body, ['app']); } else { document.addEventListener('deviceready', function () { angular.bootstrap(document.body, ['app']); }, false); } </script>
至于cordova.js的话,编译时须要手动将路径添加上去。
重构
观察以上的代码,其中有很多内容是能够分离出去的,好比:每一个路由的页面html、每一个路由对应的控制器代码、路由配置代码等,接下来咱们一步步分离这些代码,并使用nodejs来合并这些代码从新生成当前的index.html。
首先在项目文件夹下建立一个src的文件,并建立index.tpl做为模板,用ejs来生成最终的index.html,部分代码以下:
<% if (!DEV) { %> <script type="text/javascript" src="cordova.js"></script> <% } %> //其余代码略 <script type="text/javascript"> <% if (DEV) { %> angular.bootstrap(document.body, ['app']); <% } else { %> document.addEventListener('deviceready', function () { angular.bootstrap(document.body, ['app']); }, false); <% } %> </script>
建立一个app.js用来执行生成,代码以下:
var async = require('async'); var ejs = require('ejs'); var fs = require('fs'); var path = require('path'); global.DEV = false; async.waterfall([ function (fn) { fs.readFile( path.join(__dirname, 'index.tpl'), 'utf8', fn ); }, function (htmlTpl, fn) { var html = ejs.render(htmlTpl); fs.writeFile( path.join(__dirname, '../', 'www', 'index.html'), html, fn ); } ], function (err) { console.log(err || 'done'); });
经过变量DEV来控制浏览器测试或者app html,接下来将各个路由html分离到src/html目录中去,分别为init.html和main.html,只要修改app.js读取src/html目录并根据原先的html格式填充到模板中去便可,代码修改以下:
//index.tpl //略 <% views.forEach(function(view) { %> <script id="<%= view.id %>" type="text/ng-template"> <%- view.html %> </script> <% }); %> //略 //app.js //略 global.views = []; var viewDir = path.join(__dirname, 'view'); async.waterfall([ //略, function (fn) { fs.readdir(viewDir, fn); }, function (filenames, fn) { async.eachSeries(filenames, function (filename, readFn) { if(path.extname(filename) != '.html') return readFn(); fs.readFile( path.join(viewDir, filename), 'utf8', function (err, html) { if (err) return readFn(err); global.views.push({ id: filename.replace('.html', ''), html: html }); readFn(); } ); }, fn); }, //略 ], function (err) { console.log(err || 'done'); });
至于controller的分离跟view是相似的,这里就再也不提供重复的代码了。
而$routeProvider配置的配置是从view直接映射过来的,所以只要稍微的修改一下即可以解决了,这里也不重复编码了。
接下来引入cordova的组件,如:device,经过命令:cordova plugin add cordova-plugin-device来进行安装,因为使用的时候是直接用device对象的,所以须要对其包装一下,代码放置在src/release中,代码以下:
app.factory('sys.device', [ function () { return { uuid: device.uuid } } ]);
那么能够建立一份依赖于浏览器环境的,目录为src/dev,代码以下:
app.factory('sys.device', [ function () { return { uuid: 'uuid' }; } ]);
修改一下ctrl.main,将device引入,代码以下:
app.controller('ctrl.main', [ '$scope', 'sys.device', function ($scope, device) { $scope.deviceId = device.uuid; } ]);
在app.js生成index.html的时候,若是DEV为true则读取src/dev目录不然读取src/release目录,这样的话,浏览器测试和编译app运行都没问题了。
结尾
许久没有写博客了,表达可能不是很清晰,若是有什么问题能够留言给我,那么今次的文章就到这里了,谢谢。