【Hybrid开发高级系列】AngularJS模块级开发模式专题

1 架构设计思路

1.1 App总体架构思路

        基于Hybrid开发模式的AngularJS开发,相比传统Web站点的开发模式有着很明显的差别,最主要体现在Window对象的作用域不同,在传统web开发中,所有angular module都是共享同一个Window对象,而在移动开发领域,我们更倾向于基于多WebView并存的形式来做页面内容组织,这样做最大的好处便是能尽可能多的缓存页面状态,实现类似纯原生应用的快速页面呈现体验。

        基于此论断,我们将同一个模块的页面放在同一个webview中承载,理想状态下,每一个模块均使用一个独立的webview来承载并缓存,页面呈现时基于webview缓存机制来做呈现,尽可能减少html加载时间以求达到类似原生页面的效果,因为IOS的webview加载时间较快,所以目前暂时是基于此方案来做全局路由跳转与页面状态缓存。

        而对于Android这类单个webview内存占用很大的场景,可以将webview的缓存复用与Fragment的页面状态缓存进行剥离,从而达到统一路由管理与内存可控并存的效果。

1.2 模块级开发模式设计思路

        当前APP采用Hybrid开发模式,web端采用AngularJS框架进行开发,结合AngularJS的模块解耦与路由控制特点,初步发展出结合移动端开发特点的MVC开发模式,以提高APP开发效率与交付质量。

        AngularJS的一大特点就是功能模块化设计、依赖动态注入,针对基于本地html页面的hybrid开发场景,我们尝试将业务需求基于业务流程进行模块级划分,每个业务流程作为一个独立模块单元来进行设计,模块间的页面跳转与状态同步由Native端的统一路由模块进行管理控制,模块内的页面跳转采用Angular自动的UI-Route机制处理,模块内的状态同步基于数据模块绑定来做简化处理。

        对于模块内的业务开发,结合AngularJS数据双向绑定的特点,初步采用基于MVC架构分层的开发模式来做代码开发。

2 设计分层

2.1 分层职责说明

        结合AngularJS的双向数据绑定能力,Hybrid开发中,对于Angular业务Module的开发也采用MVC架构,总体职责分配是:

    Module层

        数据模型层统一进行模块级数据对象的状态管理,数据状态的变化通过AngularJS的数据绑定能力自动更新到页面,这是数据建模产生的最大价值。

    Controller层

        页面控制器层主要职责是完成数据绑定关系映射、处理用户交互事件;当然考虑到AngularJS模块机制的特殊性,对于模块级的控制行为也可以划归为Controller层,主要包括模块内路由行为初始化、模块级生命周期事件监听与处理。

     View层

        视图呈现层在web端,对应就是html结构设计与css样式处理。

    Service层

        服务请求层,主要职责是管理与服务端交互的请求,目前主要是HTTP请求。这一层后续的重构优化空间还很大。

    原生交互层

        该层用于统一管理h5与原生进行的交互,主要基于思迪框架的插件机制,在此基础上进行业务友好性封装。

        下面以定投列表模块代码为例,进行具体讲解。

2.2 数据模型层module

2.2.1 数据属性

hjMPModule.factory("dataHolder",['hjMPService',function(hjMPService) {

                  return {

                                    mpList: [],

                                    selectItem : {},

                                    dataRefsh: false,

                                    scrollTop: 0,

                                    showErrorMsg: false,

                                    errorTip: '',

2.2.2 数据操作方法

        //根据索引号查询指定定投项

        getMpItem : function(contractNo){

            for(var i = 0; i < this.mpList.length; i++)

            {

                var item = this.mpList[i];

                if(item.conno == conNo)

                    return item;

            }

            return null;

        },

        removeMpItem : function(conNo)

        {

            var item = this.getMpItem(conNo);

            if(item != null)

            {

                var index = this.mpList.indexOf(item);

                this.mpList.splice(index,1);

            }

        },

        setDefaultData : function()

        {

            this.mpList = [];

            this.dataRef = false;

            this.scrollTop = 0;

            this.showErrorMsg = false;

            this.errorTip = ''

        },

        queryMyMpList : function(callback, callbackError)

        {

            var self = this;

            var hjUserId = hj.UserUtil.getUserId();

            hjMPService.queryMyMpList({

               userId: userId

            }).then(function(data) {

                if (data.mpList != null)

                    self.mpList = data.mpList;


                if(callback)

                    callback(data);

            }, function(error) {

                if(callbackError)

                {

                    callbackError(error);

                }

            });

        },

        updateMpStatus :function(status, payPassword, callback, callbackError)

        {

            var self = this;

            var promise = hjMPService.updateMpSt({

                userId: hj.UserUtil.getCmfUserId(),

                conNo: self.selectItem.conno,

                contractSt: status,

                payPassword: payPassword

            });


            promise.then(function(data) {

                self.selectItem.contractst = status;


                if (status === 'N') {

                    self.selectItem.contractst= 'N';

                } else if (status === 'P') {

                    self.selectItem.contractst= 'P';

                } else if (status === 'C') {

                    self.removeMpItem(self.selectItem.conNo);

                }


                if(callback)

                    callback(data);

            }, function(error) {

                if(callbackError)

                    callbackError(error);

            })

        }

  };

}]);

2.3 控制器层controller

2.3.1 Angular模块初始化

        模块初始化职责:

    1、模块内路由规则设定;

    2、模块内路由跳转监听;

    3、模块级生命周期事件监听;

    4、模块级页面状态维护;

2.3.2 数据对象绑定

//我的定投列表       

mpModule.controller("getMpListController",["$scope", '$state', 'dataHolder', '$window', 'hjMPService', 'useLoaddFact', function($scope, $state, dataHolder, $window, hjMPService, useLoadFact) {

    $scope.dataHolder = dataHolder;

            $scope.fVoteNoData = false;

            dataHolder.contractst = '';

2.3.3 页面字段绑定

2.3.4 交互事件处理

//修改协议状态事件

$scope.modifyStatus = function($obj, type) {

    dataHolder.selectItem = $obj.item;

    dataHolder.scrollTop = scrollObj.scrollTop;

    dataHolder.dataRefsh = false;


    if($obj.item.contractst == "A" && type !== 'undo') {

        return;

    }


    if (type&& type === 'undo')

        dataHolder.contractst = 'C';

    $scope.$parent.go("modifyStatus");

};

2.4 页面结构与呈现层html+css


2.5 服务请求层Service

var hjMPModule = angular.module('hj_Fund', ['hj_Location']);

hjMPModule.factory('hjMPService',['$http', '$q', 'hjServer', function($http, $q, hjServer) {

    function getPromise(url, jsonObj) {

        var deferred = $q.defer();

        $http.jsonp(url, jsonObj).success(function(data) {

            if(data.resultCode === "00") {

                deferred.resolve(data);

            }else {

               deferred.reject(data.resultMsg);

            }

        }).error(function(data){

            deferred.reject("系统错误,请稍候再试!");

        });

        return deferred.promise;

}


        后续重构方向:

    1、基于业务集进行服务模块搭建;

    2、基于请求状态来做请求管理,防止重复请求;

2.6 原生交互层

2.6.1 路由引擎hj.route


2.6.2 原生视图使用hj.NativeViewUtil


2.6.3 用户信息处理hj.UserUtil


3 参考链接

$stateProvider

http://blog.csdn.net/mazhimazh/article/details/42524187

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容

  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,034评论 0 2
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,771评论 2 17
  • AngularJS是什么?AngularJs(后面就简称ng了)是一个用于设计动态web应用的结构框架。首先,它是...
    200813阅读 1,607评论 0 3
  • 《ijs》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 10...
    叶染柒丶阅读 5,171评论 0 7
  • 磁铁有话: 每个人身上都有一种能量,像磁铁一样,彼此之间都有着自己的吸力,这种力将他们汇聚在一起,形成团队,团队力...
    毅栈阅读 406评论 0 1