項目演化系列--移動端開發


前言

  現在移動端的大勢所趨,凡是項目勢必都會有移動端的需求,那么今天就來講講移動端開發吧。

  當今android、ios的開發,如果組建原生開發團隊來開發的話,費用還是很大的,而且現在不少android應用也都是結合html來進行開發的。

  最近阿里也順勢推出了weex,我還沒去體驗,不過按照阿里以往的尿性,當初推出kissy時也是號稱各種牛逼烘烘的技術,結果開發的過程當中卻出現了各種各樣的坑,等到能真的實際使用上也是好幾年以后的事情了。

  cordova跟weex是比較相似的,從2011年開始到現在,經過了這么多年的發展,api更加穩定,資源比較豐富。

  廢話就到這里了,開始碼代碼吧,文章中使用的是cordova+angularjs。

創建項目

  首先需要安裝nodejs,然后通過npm安裝cordova,完成了環境的需求后,就可以通過如下命令創建一個cordova項目:

cordova create 項目名

  至於項目內的文件結構這里就不多介紹了,園內有許多優秀的文章有詳細說明。

  以上只是稍微簡介一下,由於cordova的特殊性,使我們可以使用構建web的方式來構建手機應用,因此接下來的文章會介紹如何構建一個既可以在瀏覽器上進行測試,又可以在手機中運行的應用。

示例

  首先看一下例子,代碼如下:

<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運行都沒問題了。

結尾

  許久沒有寫博客了,表達可能不是很清晰,如果有什么問題可以留言給我,那么今次的文章就到這里了,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM