前言
現(xiàn)在移動(dòng)端的大勢(shì)所趨,凡是項(xiàng)目勢(shì)必都會(huì)有移動(dòng)端的需求,那么今天就來(lái)講講移動(dòng)端開(kāi)發(fā)吧。
當(dāng)今android、ios的開(kāi)發(fā),如果組建原生開(kāi)發(fā)團(tuán)隊(duì)來(lái)開(kāi)發(fā)的話,費(fèi)用還是很大的,而且現(xiàn)在不少android應(yīng)用也都是結(jié)合html來(lái)進(jìn)行開(kāi)發(fā)的。
最近阿里也順勢(shì)推出了weex,我還沒(méi)去體驗(yàn),不過(guò)按照阿里以往的尿性,當(dāng)初推出kissy時(shí)也是號(hào)稱各種牛逼烘烘的技術(shù),結(jié)果開(kāi)發(fā)的過(guò)程當(dāng)中卻出現(xiàn)了各種各樣的坑,等到能真的實(shí)際使用上也是好幾年以后的事情了。
cordova跟weex是比較相似的,從2011年開(kāi)始到現(xiàn)在,經(jīng)過(guò)了這么多年的發(fā)展,api更加穩(wěn)定,資源比較豐富。
廢話就到這里了,開(kāi)始碼代碼吧,文章中使用的是cordova+angularjs。
創(chuàng)建項(xiàng)目
首先需要安裝nodejs,然后通過(guò)npm安裝cordova,完成了環(huán)境的需求后,就可以通過(guò)如下命令創(chuàng)建一個(gè)cordova項(xiàng)目:
cordova create 項(xiàng)目名
至于項(xiàng)目?jī)?nèi)的文件結(jié)構(gòu)這里就不多介紹了,園內(nèi)有許多優(yōu)秀的文章有詳細(xì)說(shuō)明。
以上只是稍微簡(jiǎn)介一下,由于cordova的特殊性,使我們可以使用構(gòu)建web的方式來(lái)構(gòu)建手機(jī)應(yīng)用,因此接下來(lái)的文章會(huì)介紹如何構(gòu)建一個(gè)既可以在瀏覽器上進(jìn)行測(cè)試,又可以在手機(jī)中運(yùn)行的應(yīng)用。
示例
首先看一下例子,代碼如下:
<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">
設(shè)備信息: {{ 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提供的調(diào)用Native并不是立即就可使用的,因此需要讓angular的初始化延遲到deviceready事件中進(jìn)行,代碼調(diào)整如下:
//移除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的話,編譯時(shí)需要手動(dòng)將路徑添加上去。
重構(gòu)
觀察以上的代碼,其中有不少內(nèi)容是可以分離出去的,比如:每個(gè)路由的頁(yè)面html、每個(gè)路由對(duì)應(yīng)的控制器代碼、路由配置代碼等,接下來(lái)我們一步步分離這些代碼,并使用nodejs來(lái)合并這些代碼重新生成當(dāng)前的index.html。
首先在項(xiàng)目文件夾下創(chuàng)建一個(gè)src的文件,并創(chuàng)建index.tpl作為模板,用ejs來(lái)生成最終的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>
創(chuàng)建一個(gè)app.js用來(lái)執(zhí)行生成,代碼如下:
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');
});
通過(guò)變量DEV來(lái)控制瀏覽器測(cè)試或者app html,接下來(lái)將各個(gè)路由html分離到src/html目錄中去,分別為init.html和main.html,只要修改app.js讀取src/html目錄并根據(jù)原先的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是類似的,這里就不再提供重復(fù)的代碼了。
而$routeProvider配置的配置是從view直接映射過(guò)來(lái)的,因此只要稍微的修改一下便可以解決了,這里也不重復(fù)編碼了。
接下來(lái)引入cordova的組件,如:device,通過(guò)命令:cordova plugin add cordova-plugin-device來(lái)進(jìn)行安裝,由于使用的時(shí)候是直接用device對(duì)象的,因此需要對(duì)其包裝一下,代碼放置在src/release中,代碼如下:
app.factory('sys.device', [
function () {
return {
uuid: device.uuid
}
}
]);
那么可以創(chuàng)建一份依賴于瀏覽器環(huán)境的,目錄為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的時(shí)候,如果DEV為true則讀取src/dev目錄否則讀取src/release目錄,這樣的話,瀏覽器測(cè)試和編譯app運(yùn)行都沒(méi)問(wèn)題了。
結(jié)尾
許久沒(méi)有寫博客了,表達(dá)可能不是很清晰,如果有什么問(wèn)題可以留言給我,那么今次的文章就到這里了,謝謝。