SPA

大纲

lowinwu|2015.11.25

1、使用意义

4、转账实践

3、问题&措施

2、业界方案

SPA

使用意义

切换速度

SPA

速度体现

1、页面间跳转,

2、页面回退,

资源重复下载、执行

资源重复下载,执行

SPA

业界方案1-pjax(ajax+pushstate)

http://userbag.co.uk/demo/pjax/

https://github.com/welefen/pjax

SPA

业界方案1-pjax(ajax+pushstate)

缺点
1、不支持刷新功能,需接合cgi
2、依赖jquery         3、不支持hash方式,兼容性差

SPA

业界方案2-requirejs + angular + angular-route

http://beletsky.net/2013/11/using-angular-dot-js-with-require-dot-js.html

define(['angular', 'require', 'angular-route'], function (angular, require) {

    var app = angular.module('webapp', [
        'ngRoute'
    ]);
    app.config(['$routeProvider', '$controllerProvider',
        function($routeProvider, $controllerProvider) {
            $routeProvider.
                when('/module1', {
                    templateUrl: 'module1/tpl.html',
                    controller: 'module1Controller',
                    resolve: {
                        keyName: function ($q) {
                            var deferred = $q.defer();
                            require(['module1/module1.js'], function (controller) {
                                $controllerProvider.register('module1Controller', controller);      //由于是动态加载的controller,所以要先注册,再使用
                                deferred.resolve();
                            });
                            return deferred.promise;
                        }
                    }
                }).
                otherwise({
                    redirectTo: '/module1'     
                });
        }]);

    return app;
});
define(['angular'], function (angular) {

    //angular会自动根据controller函数的参数名,导入相应的服务returnfunction($scope, $http, $interval){
        $scope.info = 'kenko';      //向view/模版注入数据//模拟请求cgi获取数据,数据返回后,自动修改界面,不需要啰嗦的$('#xxx').html(xxx)
        $http.get('module2/tpl.html').success(function(data) {
            $scope.info = 'vivi';
        });
    };
});

SPA

业界方案2

缺点:1、压缩包78K

            2、开发复杂

            2、语法复杂

SPA

http://coenraets.org/blog/2013/06/building-modular-web-applications-with-backbone-js-and-requirejs-sample-app/

Modularized View
define(function (require) {
    "use strict";
    var $           = require('jquery'),
        _           = require('underscore'),
        Backbone    = require('backbone'),
        tpl         = require('text!tpl/Employee.html'),
        template = _.template(tpl);
    return Backbone.View.extend({
        render: function () {
            this.$el.html(template());
            return this;
        }
    });
});
Modularized Model

define(function (require) {
    "use strict";
    var $           = require('jquery'),
        Backbone    = require('backbone'),

        Employee = Backbone.Model.extend({

            urlRoot: "http://localhost:3000/employees",

            initialize: function () {
                this.reports = new EmployeeCollection();
                this.reports.url = this.urlRoot + "/" + this.id + "/reports";
            }
        }),
        EmployeeCollection = Backbone.Collection.extend({

            model: Employee,

            url: "http://localhost:3000/employees"

        });
    return {
        Employee: Employee,
        EmployeeCollection: EmployeeCollection
    };

});
Modularized Router
define(function (require) { 
    var $           = require('jquery'),
        Backbone    = require('backbone'),
        $content = $("#content");
    return Backbone.Router.extend({
        routes: {
            "":                 "home",
            "employees/:id":    "employee"
        },
        home: function () {
            require(["app/views/Home"], function (HomeView) {
                var view = new HomeView({el: $content});
                view.render();
            });
        },
        employee: function (id) {
            require(["app/views/Employee", "app/models"], function (EmployeeView, models) {
                var employee = new models.Employee({id: id});
                employee.fetch({
                    success: function (data) {
                        var view = new EmployeeView({model: data, el: $content});
                        view.render();
                    }
                });
            });
        }
 
    });
 
});

SPA

业界方案3

缺点: 1、MVC死板 

             2、开发流程复杂 

SPA

面临问题

URL监听

代码隔离

穿透事件

首屏保障

缓存策略

文件合并

跨域方案

切屏方案

......

SPA

措  施

2、功能模块化加载

1、路由注册与监控

SPA

路由监控

1、注册路由

3、监控路由变化

2、ajax与tap/click事件

刷新&第三方跳转

onhashchange

重新包装&写hash

SPA

多模块加载

描述:复杂交互中,

如何解决?

按需加载代码隔离

SPA

多模块加载

方案1:html与js全注入首页

//注册路由
 regRouters:function(){
           $.SPA.regRouter("index",function(){ //回退首页以及进入首页处理
                Send.showPage("index");
           });
           $.SPA.regRouter("input",function(){
                Send.showPage("input");
           });    
           $.SPA.regRouter("input-confirm",function(){
                Send.showPage("input-confirm");
           });
}
<div class="container">
    <?cs include:PARSE_PATH("/hybrid/mch/weixin/transfer_hk/mod/guide.shtml") ?> 
    <!--表单[[-->
    <div class="main hide js-send"></div>
    <!--表单]]-->
    <?cs include:PARSE_PATH("/hybrid/mch/weixin/transfer_hk/mod/input_confirm.shtml") ?>
</div>

->消耗首屏&无效加载&js污染

SPA

多模块加载

方案2:动态加载js

regRouters:function(){//注册路由
   $.SPA.r({
       id: 'main_content',//挂载点
       type:'index',//设置为首页
       animate: '',                          //入场动画效果
       script:'/res/scripts/app/transfer_hk/record.js'
    });
},
init:fucntion(){
  this.regRouter();//注册路由
  $.SPA.startRun();//启动监听
}
boot:function(id,options){
    $.SPA.signLocaton({state:{path:id},func:function(){},type:true});
    var uiModule = $.SPA.routers[id];
    dust.use([uiModule['script']],function(module){
    .......

->消耗首屏时间&无效加载

SPA

多模块加载

方案3:动态加载html

define(function(require,exports,module){
    $.PHtml.fetch("send",function(data){});
}
<div class="container">
    <div id="index"></div>
    <!--表单[[-->
    <div class="main hide js-send"></div>
    <!--表单]]-->
    <div id="input-confirm"></div>
</div>

->代码维护成本高&偶合散乱

 $.PHtml.r([
 {
 ver:'1.0',
 url:"https://wsk.qq.com//hybrid/mch/weixin/transfer_hk/template/send.shtml",
 id : "send"
}]);

SPA

多模块加载

方案4:业务html嵌入业务js中

define(function(require,exports,module){
   var body = [ 
	'<div>',
         '<ul class="list"...........
        ].join("");
   var Detail = {
    	title: '订单详情',
    	body : body,
        init : function($view,options){
        	this.render();
        	$view.addClass("detail").removeClass("hide");
        	$.hideLoading();
        },
        ....
   }
}
<div class="container">
    <div id="index"></div>
    <!--表单[[-->
    <div class="main hide js-send"></div>
    <!--表单]]-->
    <div id="input-confirm"></div>
</div>

->代码强偶合

SPA

多模块加载

方案5:业务js,require对应业务html

define(function(require,exports,module){
   var tpl =  require("text!/hybrid/mch/weixin/transfer_hk/mod/detail.shtml");
   var Detail = {
    	title: '订单详情',
    	body : tpl ,
        init : function($view,options){
        	this.render();
        	$view.addClass("detail").removeClass("hide");
        	$.hideLoading();
        },
        ....
   }
}
<div class="container">
    <div id="index"></div>
    <!--表单[[-->
    <div class="main hide js-send"></div>
    <!--表单]]-->
    <div id="input-confirm"></div>
</div>

SPA

多模块加载

方案5:实现原理?

 var load = function () {
  ......
  emit("resolve",emitData);
  emit("request",emitData);
 if (!emitData.requested && url) {//非模板文件
        _loadScript(url, function () {
// normal text
register({
  name: "text",

  ext: [".tpl", ".html",".shtml"],

  exec: function(uri, content) {
    globalEval('define("' + uri + '", [], "' + jsEscape(content) + '")')
  }
})


function xhr(url, callback) {
  var r = global.XMLHttpRequest ?
      new global.XMLHttpRequest() :
      new global.ActiveXObject("Microsoft.XMLHTTP")

  r.open("GET", url, true)

ajax请求,增加define逻辑

SPA

多模块加载

方案5:架构位置

SPA

文件合并下载

方案1:

ajax动态合并下载

针对非js拦截修改

SPA

文件合并下载 

方案1:JS,html合并

动态合并 mgp_file_merge.cgi

CGI难维护,需支持合并html,目前单独加载html

/res/scripts/app/transfer_hk/send/sendmain.js<itemSplit>define(function(require,exports,module){
    require("/res/scripts/mod/global/spa.js");
    var hkwexin = require("/res/scripts/mod/global/hkweixin.js");
    var SendMain = {
        regRouter : function(){
        	//首页面
            $.SPA.r({
               id: 'js-guide',//挂载点
               animate: '',  //入场动画效果
               type : 'index',
               script:'',
               beforeinit:function(){
               	   $(".js-index").removeAttr("class").addClass("js-index hide-mod-pop-loading guide");
               	   
               	   $("#start_use").unbind($.Env.CLICK_EVENT).bind($.Env.CLICK_EVENT,function(){
               	   	   hkwexin.selectSingleContact({success:function(data){
                               var openid = data["info"]["openid"];
                               $.SPA.boot('input_page',{openid:openid});
                       }});
               	   	
               	   });
               },
               afterinit:function(){
               	   $.hideLoading();
               }
            });
            //注册输入路由:
            $.SPA.r({
            	beforeclose:function(){alert(3);},
               id: 'input_page',//挂载点
               animate: 'fadeIn',    //入场动画效果
               script:'/res/scripts/app/transfer_hk/send/inputpage.js'
            });
            //注册新的路由:
            $.SPA.r({
               id: 'ok_page',//挂载点
               animate: '',                          //入场动画效果
               classname: '',                        //自定义样式名
               script:'/res/scripts/app/transfer_hk/send/okpage.js'
            });
            
        },
        init : function(tmplate){
        	
        	this.initWX();
            this.regRouter();
            $.SPA.startRun();
        },
        initWX:function(){
        	
            hkwexin.initWX(para);
            
            hkwexin.hideShare({});
        }
    }

    return SendMain;

});<resSplit>/res/scripts/app/transfer_hk/send/inputpage.js<itemSplit>/**
 * 输入界面
 */
define(function(require,exports,module){
    var tpl =  require("text!/hybrid/mch/weixin/transfer_hk/mod/input_page.shtml");
    var InputExtend = require("/res/scripts/mod/global/inputextend.js");
    var datavalid = require("/res/scripts/mod/global/datavalid.js");
    $.Amount = require("/res/scripts/mod/global/amount.js");
    var PATH = $.Env.CGI_HOST+"cgi-bin/app/transfer/";
    var CGI = {//
        create_list : PATH + 'ia_transfers_create_list.cgi'//
    };
    var InputPage = {
        title: '轉賬',
        body : tpl,
        openid:"",
        //更新body
        beforeinit:function(options){//处理数据
        	options = options || [{}]
            $(".js-index").removeAttr("class").addClass("js-index hide-mod-pop-loading input");
            this.openid = options[0]["openid"];
        },
        init : function($view,options){
            this.render(options);
            $.hideLoading();
            this.bindEvent();
        },
        showTips: function(msg){
            $.Tips.showTips(msg, 2, 1, {top:"15px"});
        },
        render : function(options){
        	 $("#headImg").attr("src",HEAD_IMG_URL);
        	 $("#money").attr("placeholder","每筆最高限額為HK$"+$.Amount.fen2Yuan(PAY_MAX_AMOUNT));
        	 $("#receiver").val();
        	 new InputExtend('del',["money","receiver"]);
        	 
        	 new InputExtend('format',["money"]);
        	 
        },
        onValidFail:function(msg){
            InputPage.showTips(msg);
        },
        checkFormRules:function(){
            //配置
            return {
                "money":function(v){
                    if(!v){
                        return $.Lang.get("limitMoneyEmpty",'zh_cn');
                    }
                    return true;
                },
                "receiver":function(v){
                    if(!v){
                        return "姓名不能為空";
                    }
                    return true;
                }
            }
            
        },
        getNameRules:function(type){
            var data =  {
                "money":"transfer_amount",
                "receiver":"receiver_name"
               };
            if(type){
                data =  {
                 "transfer_amount":"money",
                "receiver_name":"receiver"
               };
            }
            return data;
        },
        validate:function(sucfn){
        	 datavalid.valid("form-translate",sucfn,this.onValidFail,this.checkFormRules(),null,this.getNameRules());
        },
        getParams:function(){
            
            var amount = $.trim($("#money").val());
            var name = $.trim($("#receiver").val());
            var params = {
               "curtype":'HKD',
               "transfer_amount":$.Amount.yuan2Fen(amount),
               "receiver_name":name
            };
            return params;
        },
        createListResult:function(data){
        	$.SPA.boot('ok_page',data);
        },
        bindEvent:function(){
        	$("#createlist").bind("tap",function(e){
                InputPage.validate(function(){
                     $.ajaxEx({
                        url : CGI["create_list"],
                        data : InputPage.getParams(),
                        onSuccess :InputPage.createListResult.bind(this)
                    });
                });
                
            });
        	
        }
    }
    return InputPage;
})

SPA

文件合并(cdn)

方案2:JS,html合并

动态合并 https://mqq-imgcache.gtimg.cn/c/=

单独请求html,改造define实现存储

/**
 * 椤甸潰鍥炶溅浜嬩欢鐨勮嚜鍔ㄧ洃鍚ā鍧�
 * <p>
 * 鐢变簬绉诲姩绔」鐩殑椤甸潰涓€鑸兘浼氬寘鍚玣orm鍏冪礌(鍦ㄦ媺璧烽敭鐩樻椂鍙互鍑虹幇纭)锛屾墍浠ュ湪鏂囨湰妗嗗唴鍥炶溅鐨勬椂鍊欒〃鍗曠粡甯歌嚜鍔ㄦ彁浜や粠鑰屽紩璧蜂竴浜涗笉蹇呰鐨勯棶棰橈紝
 * 鍚屾椂涓轰簡鏀硅繘鐢ㄦ埛浣撻獙锛岀敱浜庨樆姝簡form琛ㄥ崟鐨勮嚜鍔ㄦ彁浜わ紝鎵€浠ュ湪鐢ㄦ埛杈撳叆瀹屾渶鍚庝竴涓〃鍗曢」鏃跺洖杞﹁Е鍙戣嚜鍔ㄦ彁浜�
 * </p>
 * 
 * <pre>
 * var watch = require("../../../mod/global/watch");
 * 
 * watch({
 *     "form"   : "form[name='yourname']",  //鍙己鐪� 榛樿鍙栨枃鏈妭鐐圭埗绾ф墍鍦ㄧ殑form鍏冪礌
 *     "target" : "鐩戝惉鐨刬nput妗嗙殑閫夋嫨鍣�",
 *     "button" : "琚姩鐩戝惉鐨勬寜閽簨浠剁殑鑺傜偣"
 * })
 * </pre>
 * 
 * @class watch
 * @module global
 * @author chauvetxiao
*/
define("g/js/mod/global/watch-debug", [], function(require, exports, module) {
    var param = {};
    //鐢变簬缁戝畾浜嬩欢涓嶈兘浼犻€掑弬鏁� 鎵€浠ョ紦瀛橀厤缃璞�
    var key = 0;
    //璁℃暟鍣�
    /**
     * 瀵归〉闈笂鐨勮〃鍗曟彁浜や簨浠惰繘琛岀洃鍚�
     * 
     * @method watch
     * @param  {Object} cfg 鐩戝惉鐨勫弬鏁伴厤缃」          
     */
    function watch(cfg) {
        cfg = cfg || {};
        var f = cfg.form ? $(cfg.form) : $(cfg.target).parents("form");
        var target = $(cfg.target);
        var button = $(cfg.button);
        var pre_bind = $("input[data-bind-watch='1']");
        key++;
        pre_bind.off("keydown", onKeydownHandler);
        //瑙g粦鏃ц妭鐐逛笂鍥炶溅浜嬩欢鐨勭粦瀹�
        if (target) {
            if (!target.data("bind-watch")) {
                target.on("keydown", onKeydownHandler);
                target.data("bind-watch", key);
                param[key] = cfg;
            }
        }
        if (!$(f).data("bind-watch")) {
            $(f).on("submit", function(e) {
                if (cfg.auto != true) {
                    e.preventDefault();
                }
            });
            $(f).data("bind-watch", key);
        }
    }
    /**
     * 鎸夐敭寮硅捣鏃剁殑鐩戝惉浜嬩欢
     * @method onKeydownHandler
     * @private
     * @param  {Event} e 閿洏浜嬩欢     
     */
    function onKeydownHandler(e) {
        if (e.keyCode == 13) {
            e.preventDefault();
            var cfg = param[$(e.target).data("bind-watch")] || {};
            var f = cfg.form ? $(cfg.form) : $(cfg.target).parent("form");
            var button = $(cfg.button);
            if (cfg.auto == true) {
                f && f.submit();
            } else {
                if (cfg.eventType) {
                    button.trigger(cfg.eventType);
                } else {
                    button.trigger("tap");
                    button.trigger("click");
                }
            }
        }
    }
    return watch;
});
.ico-icbc,.ico-cmb,.ico-ccb,.ico-abc,.ico-boc,.ico-cebb,.ico-ecitic,.ico-gdb,.ico-pingan,.ico-cmbc,.ico-cib,.ico-sdb,.ico-huarun,.ico-bosh,.ico-spdb,.ico-sdeb,.ico-nbcb,.ico-boco,.ico-bob,.ico-hkb,.ico-njcb,.ico-jsb,.ico-hzb,.ico-hxb,.ico-srcb,.ico-zjcb,.ico-post,.ico-psbc,.ico-cqcb,.ico-bsb,.ico-cbhb,.ico-nycbank,.ico-bocd,.ico-sznsh,.ico-hrbcb,.ico-jjccb,.ico-citic,.ico-ceb,.ico-pab,.ico-crb,.ico-comm,.ico-gzcb{position:relative;display:inline-block;vertical-align:middle;height:27px;line-height:27px;color:#333;font-size:1.2em;overflow:hidden;}.ico-icbc:before,.ico-cmb:before,.ico-ccb:before,.ico-abc:before,.ico-boc:before,.ico-cebb:before,.ico-ecitic:before,.ico-gdb:before,.ico-pingan:before,.ico-cmbc:before,.ico-cib:before,.ico-sdb:before,.ico-huarun:before,.ico-bosh:before,.ico-spdb:before,.ico-sdeb:before,.ico-nbcb:before,.ico-boco:before,.ico-bob:before,.ico-hkb:before,.ico-njcb:before,.ico-jsb:before,.ico-hzb:before,.ico-hxb:before,.ico-srcb:before,.ico-zjcb:before,.ico-post:before,.ico-psbc:before,.ico-cqcb:before,.ico-bsb:before,.ico-cbhb:before,.ico-nycbank:before,.ico-bocd:before,.ico-sznsh:before,.ico-hrbcb:before,.ico-jjccb:before,.ico-citic:before,.ico-ceb:before,.ico-pab:before,.ico-crb:before,.ico-comm:before,.ico-gzcb:before{content:" ";position:absolute;top:0;left:0;height:100%;width:100%;background:url("/res/weixin/creditcard/img/global/bank.png?v=20150923") no-repeat;-webkit-background-size:230px auto;background-size:230px auto;}@media only screen and(-webkit-min-device-pixel-ratio:1.5),only screen and(min-device-pixel-ratio:1.5){.ico-icbc:before,.ico-cmb:before,.ico-ccb:before,.ico-abc:before,.ico-boc:before,.ico-cebb:before,.ico-ecitic:before,.ico-gdb:before,.ico-pingan:before,.ico-cmbc:before,.ico-cib:before,.ico-sdb:before,.ico-huarun:before,.ico-bosh:before,.ico-spdb:before,.ico-sdeb:before,.ico-nbcb:before,.ico-boco:before,.ico-bob:before,.ico-hkb:before,.ico-njcb:before,.ico-jsb:before,.ico-hzb:before,.ico-hxb:before,.ico-srcb:before,.ico-zjcb:before,.ico-post:before,.ico-psbc:before,.ico-cqcb:before,.ico-bsb:before,.ico-cbhb:before,.ico-nycbank:before,.ico-bocd:before,.ico-sznsh:before,.ico-hrbcb:before,.ico-jjccb:before,.ico-citic:before,.ico-ceb:before,.ico-pab:before,.ico-crb:before,.ico-comm:before,.ico-gzcb:before{background-image:url("/res/weixin/creditcard/img/global/bank@2x.png?v=20150923");}}.ico-cmb{width:114px;}.ico-cmb:before{background-position:0 0;}.ico-boc{width:94px;}.ico-boc:before{background-position:0 -28px;}.ico-icbc{width:123px;}.ico-icbc:before{background-position:0 -56px;}.ico-ecitic,.ico-citic{width:112px;}.ico-ecitic:before,.ico-citic:before{background-position:0 -84px;}.ico-ccb{width:151px;}.ico-ccb:before{background-position:0 -112px;}.ico-abc{width:147px;}.ico-abc:before{background-position:0 -140px;}.ico-cmbc{width:184px;}.ico-cmbc:before{background-position:0 -168px;}.ico-gdb{width:115px;}.ico-gdb:before{background-position:0 -196px;}.ico-pingan,.ico-pab{width:140px;}.ico-pingan:before,.ico-pab:before{background-position:0 -224px;}.ico-cebb,.ico-ceb{width:158px;}.ico-cebb:before,.ico-ceb:before{background-position:0 -252px;}.ico-huarun,.ico-crb{width:99px;}.ico-huarun:before,.ico-crb:before{background-position:0 -280px;}.ico-spdb{width:96px;}.ico-spdb:before{background-position:0 -308px;}.ico-cib{width:144px;}.ico-cib:before{background-position:0 -336px;}.ico-sdb{width:169px;}.ico-sdb:before{background-position:0 -364px;}.ico-bosh{width:81px;}.ico-bosh:before{background-position:0 -392px;}.ico-sdeb{width:149px;}.ico-sdeb:before{background-position:0 -420px;}.ico-nbcb{width:94px;}.ico-nbcb:before{background-position:0 -448px;}.ico-boco,.ico-comm{width:122px;}.ico-boco:before,.ico-comm:before{background-position:0 -476px;}.ico-bob{width:125px;}.ico-bob:before{background-position:0 -504px;}.ico-hkb{width:189px;}.ico-hkb:before{background-position:0 -532px;}.ico-njcb{width:100px;}.ico-njcb:before{background-position:0 -560px;}.ico-jsb{width:116px;}.ico-jsb:before{background-position:0 -588px;}.ico-hzb{width:99px;}.ico-hzb:before{background-position:0 -616px;}.ico-hxb{width:120px;}.ico-hxb:before{background-position:0 -644px;}.ico-srcb{width:230px;}.ico-srcb:before{background-position:0 -672px;}.ico-zjcb{width:123px;}.ico-zjcb:before{background-position:0 -700px;}.ico-post{width:172px;}.ico-post:before{background-position:0 -728px;}.ico-psbc{width:172px;}.ico-psbc:before{background-position:0 -728px;}.ico-cqcb{width:151px;}.ico-cqcb:before{background-position:0 -756px;}.ico-bsb{width:139px;}.ico-bsb:before{background-position:0 -784px;}.ico-cbhb{width:157px;}.ico-cbhb:before{background-position:0 -812px;}.ico-nycbank{width:124px;}.ico-nycbank:before{background-position:0 -840px;}.ico-bocd{width:108px;}.ico-bocd:before{background-position:0 -868px;}.ico-sznsh{width:154px;}.ico-sznsh:before{background-position:0 -896px;}.ico-hrbcb{width:206px;}.ico-hrbcb:before{background-position:0 -924px;}.ico-jjccb{width:121px;}.ico-jjccb:before{background-position:0 -952px;}.ico-gzcb{width:106px;}.ico-gzcb:before{background-position:0 -980px;}.ico-icbc-s,.ico-cmb-s,.ico-ccb-s,.ico-abc-s,.ico-boc-s,.ico-cebb-s,.ico-ecitic-s,.ico-gdb-s,.ico-pingan-s,.ico-cmbc-s,.ico-cib-s,.ico-sdb-s,.ico-huarun-s,.ico-bosh-s,.ico-spdb-s,.ico-sdeb-s,.ico-nbcb-s,.ico-boco-s,.ico-bob-s,.ico-hkb-s,.ico-njcb-s,.ico-jsb-s,.ico-hzb-s,.ico-hxb-s,.ico-srcb-s,.ico-zjcb-s,.ico-post-s,.ico-psbc-s,.ico-cqcb-s,.ico-bsb-s,.ico-cbhb

SPA

文件合并终极方案

编译时

交由工具js中注入html

SPA

缓存处理

模块间传数据处理? sessionstoray

dust.cacheConfig(
{"/res/scripts/app/transfer_hk/app.js":"75D3429B50CAE23571EDEF4DCF35DFC9",
"/res/scripts/app/transfer_hk/detail.js":"7AF5377E24FFC9CFE519F3CCA3F68396",
"/res/scripts/app/transfer_hk/dust_config.js":"93584CD18610BA89C7444482F703BAB4",
"/res/scripts/app/transfer_hk/main.js":"A9BA65C9AF86592808E7B03C4F34C2DE",
"/res/scripts/app/transfer_hk/pay_result.js":"08CA5997EF09043357850E262F170BA4",
"/res/scripts/app/transfer_hk/receive.js":"DFA26B5A4B82C1DE2511DCA198958D71",
"/res/scripts/app/transfer_hk/record.js":"BD8843D4ACEDFCD72DD7A249B85B0537",
"/res/scripts/app/transfer_hk/send.js":"D3D6CEFCCFA3A803323A18DF61F9DC3D"
});

SPA

内存处理

>jQuery Core 1.4.1  html()时支持去掉其节点中所有事件与缓存数据 http://code.jquery.com/jquery/

Text

SPA

整体架构图

SPA

开发流程

SPA

模块生命

SPA

转账项目-UI

SPA

转账项目-开发

<!-- 结构区域]] -->
<div class="container">
    <!--引导頁[[-->
<div class="main js-guide hide" id="js-guide">
	<div class="pic"><img width="262" src="/res/hybrid/img/mch/weixin/transfer_hk/guide_pic.png" alt="轉賬"></div>
	<div class="btn-line">
		<a class="btn-opt btn-green" style="/*position: relative;top: 200px;*/" href="#input_page" >立即使用</a>
	</div>
</div>
<!--引导頁]]-->
 
<!--表单[[-->
<div class="main hide js-send" id="input_page"></div>
<!--表单]]-->

<!--表单[[-->
<div class="main hide js-input-confirm" id="ok_page"></div>
<!--表单]]-->
</div>
<?cs include:PARSE_PATH("/hybrid/mch/inc/js/js_wx_pay.html") ?>
<?cs include:PARSE_PATH("/hybrid/mch/inc/js/spa-dust.html") ?>
<?cs include:PARSE_PATH("/hybrid/mch/inc/js/transfer_hk/send.html") ?>
<script type="text/javascript">
        var G_NOT_NEED_BASE = 1;
        G_SPEED_TTL[1]=(new Date()).getTime();//dom初始化完成
        
        dust.debug(true);
        dust.use(["/res/scripts/app/transfer_hk/app.js","/res/scripts/app/transfer_hk/send/sendmain.js"],function(app,send){
            app.init(function(){
            
               send.init();
            }); 
        })
</script>
define(function(require,exports,module){
    require("/res/scripts/mod/global/spa.js");
    var SendMain = {
        regRouter : function(){
            //首页面
            $.SPA.r({
               id: 'js-guide',//挂载点
               animate: '',  //入场动画效果
               type : 'index',
               script:'',
               beforeinit:function(){
               	   $(".js-index").removeAttr("class").addClass("js-index hide-mod-pop-loading guide");
               	   $.hideLoading();
               }
            });
            //输入界面
            $.SPA.r({
               id: 'input_page',//挂载点
               animate: '',    //入场动画效果
               script:'/res/scripts/app/transfer_hk/send/inputpage.js'
            });
            //详情界面
            $.SPA.r({
               id: 'ok_page',//挂载点
               animate: '',                          //入场动画效果
               classname: '',                        //自定义样式名
               script:'/res/scripts/app/transfer_hk/send/okpage.js'
            });
            
        },
        init : function(tmplate){
            this.regRouter();
            $.SPA.startRun();
        }
    }
    return SendMain;
});
/**
 * 输入界面
 */
define(function(require,exports,module){
    var tpl =  require("text!/hybrid/mch/weixin/transfer_hk/mod/input_page.shtml");
    var InputExtend = require("/res/scripts/mod/global/inputextend.js");
    var datavalid = require("/res/scripts/mod/global/datavalid.js");
    $.Amount = require("/res/scripts/mod/global/amount.js");
    var PATH = $.Env.CGI_HOST+"cgi-bin/app/transfer/";
    var CGI = {//
        create_list : PATH + 'ia_transfers_create_list.cgi'//
    };
    var InputPage = {
        title: '轉賬',
        body : tpl,
        //更新body
        beforeinit:function(options){//处理数据
            
        },
        init : function($view,options,rr){
            this.render(options);
            $.hideLoading();
            this.bindEvent();
        },
        showTips: function(msg){
            $.Tips.showTips(msg, 2, 1, {top:"15px"});
        },
        render : function(options){
        	 $("#headImg").attr("src",HEAD_IMG_URL);
        	 $("#money").attr("placeholder","每筆最高限額為HK$"+$.Amount.fen2Yuan(PAY_MAX_AMOUNT));
        	 $(".js-index").removeAttr("class").addClass("js-index hide-mod-pop-loading input");
        	 
        	 new InputExtend('del',["money","receiver"]);
        	 
        	 new InputExtend('format',["money"]);
        	 
        },
        onValidFail:function(msg){
            InputPage.showTips(msg);
        },
        checkFormRules:function(){
            //配置
            return {
                "money":function(v){
                    if(!v){
                        return $.Lang.get("limitMoneyEmpty",'zh_cn');
                    }
                    return true;
                },
                "receiver":function(v){
                    if(!v){
                        return "姓名不能為空";
                    }
                    return true;
                }
            }
            
        },
        getNameRules:function(type){
            var data =  {
                "money":"transfer_amount",
                "receiver":"receiver_name"
               };
            if(type){
                data =  {
                 "transfer_amount":"money",
                "receiver_name":"receiver"
               };
            }
            return data;
        },
        validate:function(sucfn){
        	 datavalid.valid("form-translate",sucfn,this.onValidFail,this.checkFormRules(),null,this.getNameRules());
        },
        getParams:function(){
            
            var amount = $.trim($("#money").val());
            var name = $.trim($("#receiver").val());
            var params = {
               "curtype":'HKD',
               "transfer_amount":$.Amount.yuan2Fen(amount),
               "receiver_name":name
            };
            return params;
        },
        createListResult:function(data){
        	$.SPA.boot('ok_page',data);
        },
        bindEvent:function(){
        	$("#createlist").bind("tap",function(e){
                InputPage.validate(function(){
                     $.ajaxEx({
                        url : CGI["create_list"],
                        data : InputPage.getParams(),
                        onSuccess :InputPage.createListResult.bind(this)
                    });
                });
                
            });
        	
        }
    }
    return InputPage;
})
/**
 * 详情界面
 */
define(function(require,exports,module){
    var tpl =  require("text!/hybrid/mch/weixin/transfer_hk/mod/inputconfirm.shtml");
    $.fn.template = require("/res/scripts/mod/global/template.js");
    var InputPage = {
        title: '轉賬',
        body : tpl,
        //更新body
        beforeinit:function(options){//处理数据
             var tpl =  $(this.body).template();
             this.body = tpl(this.getModel(options));
        },
        init : function($view,options){
            this.render($view,options);
            this.bindEvent();
            $.hideLoading();
        },
        render : function($view,options){
             $(".js-index").removeAttr("class").addClass("js-index hide-mod-pop-loading input-confirm");
        },
        getModel:function(options){
            options = options||[];
            var retObj = {},data = options[0];
            if(data){
                retObj = {
                	headImg:HEAD_IMG_URL,
                    transfer_amount : $.Amount.fen2Yuan(data.transfer_amount),
                    receiver_name : data.receiver_name,
                    transfer_fee :  data.transfer_fee,
                    transfer_sum : $.Amount.fen2Yuan(data.transfer_sum)
                }
            }
            return retObj;
        },
        bindEvent:function(){
            
            
        }
    }
    return InputPage;
})

SPA

遗留问题

模块间切换效果(完善中)

性能(内存)监控

工具建设,html注入

css按需加载

......

SPA

Thanks

spa

By lowinwu

spa

spa单页面方案

  • 1,380