成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

angularjs+requirejs實現(xiàn)按需加載的全面實踐

TerryCai / 1566人閱讀

摘要:想同時實現(xiàn)這些目標(biāo),就必須有一套按需加載的機(jī)制,頁面上展現(xiàn)的內(nèi)容和所有需要依賴的文件,都可以根據(jù)業(yè)務(wù)邏輯需要按需加載。最近都是基于做開發(fā),所以本文主要圍繞提供的各種機(jī)制,探索全面實現(xiàn)按需加載的套路。注意必須設(shè)置,否則變化以后,不截獲。

在進(jìn)行有一定規(guī)模的項目時,通常希望實現(xiàn)以下目標(biāo):1、支持復(fù)雜的頁面邏輯(根據(jù)業(yè)務(wù)規(guī)則動態(tài)展現(xiàn)內(nèi)容,例如:權(quán)限,數(shù)據(jù)狀態(tài)等);2、堅持前后端分離的基本原則(不分離的時候,可以在后端用模版引擎直接生成好頁面);3、頁面加載時間短(業(yè)務(wù)邏輯復(fù)雜就需要引用第三方的庫,但很可能加載的庫和用戶本次操作沒關(guān)系);4,還要代碼好維護(hù)(加入新的邏輯時,影響的文件盡量少)。

想同時實現(xiàn)這些目標(biāo),就必須有一套按需加載的機(jī)制,頁面上展現(xiàn)的內(nèi)容和所有需要依賴的文件,都可以根據(jù)業(yè)務(wù)邏輯需要按需加載。最近都是基于angularjs做開發(fā),所以本文主要圍繞angularjs提供的各種機(jī)制,探索全面實現(xiàn)按需加載的套路。

一、一步一步實現(xiàn)

基本思路:1、先開發(fā)一個框架頁面,它可以完成一些基本的業(yè)務(wù)邏輯,并且支持?jǐn)U展的機(jī)制;2、業(yè)務(wù)邏輯變復(fù)雜,需要把部分邏輯拆分到子頁面中,子頁面按需加載;3、子頁面中的展現(xiàn)內(nèi)容也變了復(fù)雜,又需要進(jìn)行拆分,按需加載;4、子頁面的內(nèi)容復(fù)雜到依賴外部模塊,需要按需加載angular模塊。

1、框架頁

提到前端的按需加載,就會想到AMD( Asynchronous Module Definition),現(xiàn)在用requirejs的非常多,所以首先考慮引入requires。

index.html


注意:采用手動啟動angular的方式,因此html中沒有ng-app。

spa-loader.js

require.config({
    paths: {
        "domReady": "/static/js/domReady",
        "angular": "http://cdn.bootcss.com/angular.js/1.4.8/angular.min",
        "angular-route": "http://cdn.bootcss.com/angular.js/1.4.8/angular-route.min",
    },
    shim: {
        "angular": {
            exports: "angular"
        },
        "angular-route": {
            deps: ["angular"]
        },
    },
    deps: ["/test/lazyspa/spa.js"],
    urlArgs: "bust=" + (new Date()).getTime()
});

spa.js

define(["require", "angular", "angular-route"], function(require, angular) {
    var app = angular.module("app", ["ngRoute"]);
    require(["domReady!"], function(document) {
        angular.bootstrap(document, ["app"]); /*手工啟動angular*/
        window.loading.finish();
    });
});
2、按需加載子頁面

angular的routeProvider+ng-view已經(jīng)提供完整的子頁面加載的方法,直接用。
注意必須設(shè)置html5Mode,否則url變化以后,routeProvider不截獲。

index.html


spa.js

app.config(["$locationProvider", "$routeProvider", function($locationProvider, $routeProvider) {
    /* 必須設(shè)置生效,否則下面的設(shè)置不生效 */
    $locationProvider.html5Mode(true);
    /* 根據(jù)url的變化加載內(nèi)容 */
    $routeProvider.when("/test/lazyspa/page1", {
        template: "
page1
", }).when("/test/lazyspa/page2", { template: "
page2
", }).otherwise({ template: "
main
", }); }]);
3、按需加載子頁面中的內(nèi)容

用routeProvider的前提是url要發(fā)生變化,但是有的時候只是子頁面中的局部要發(fā)生變化。如果這些變化主要是和綁定的數(shù)據(jù)相關(guān),不影響頁面布局,或者影響很小,那么通過ng-if一類的標(biāo)簽基本就解決了。但是有的時候要根據(jù)頁面狀態(tài),完全改變局部的內(nèi)容,例如:用戶登錄前和登錄后局部要發(fā)生的變化等,這就意味著局部的布局可能也挺復(fù)雜,需要作為獨立的單元來對待。

利用ng-include可以解決頁面局部內(nèi)容加載的問題。但是,我們可以再考慮更復(fù)雜一些的情況。這個頁面片段對應(yīng)的代碼是后端動態(tài)生成的,而且不僅僅有html還有js,js中定義了代碼片段對應(yīng)的controller。這種情況下,不僅僅要考慮動態(tài)加載html的問題,還要考慮動態(tài)定義controller的問題。controller是通過angular的controllerProvider的register方法注冊,因此需要獲得controllerProvider的實例。

spa.js

app.config(["$locationProvider", "$routeProvider", "$controllerProvider", function($locationProvider, $routeProvider, $controllerProvider) {
    app.providers = {
        $controllerProvider: $controllerProvider //注意這里?。?!
    };
    /* 必須設(shè)置生效,否則下面的設(shè)置不生效 */
    $locationProvider.html5Mode(true);
    /* 根據(jù)url的變化加載內(nèi)容 */
    $routeProvider.when("/test/lazyspa/page1", {
        /*!!!頁面中引入動態(tài)內(nèi)容!!!*/
        template: "
page1
", controller: "ctrlPage1" }).when("/test/lazyspa/page2", { template: "
page2
", }).otherwise({ template: "
main
", }); app.controller("ctrlPage1", ["$scope", "$templateCache", function($scope, $templateCache) { /* 用這種方式,ng-include配合,根據(jù)業(yè)務(wù)邏輯動態(tài)獲取頁面內(nèi)容 */ /* !!!動態(tài)的定義controller!!! */ app.providers.$controllerProvider.register("ctrlPage1Dyna", ["$scope", function($scope) { $scope.openAlert = function() { alert("page1 alert"); }; }]); /* !!!動態(tài)定義頁面的內(nèi)容!!! */ $templateCache.put("page1.html", "
"); }]); }]);
4、動態(tài)加載模塊

采用上面子頁面片段的加載方式存在一個局限,就是各種邏輯(js)要加入到啟動模塊中,這樣還是限制子頁面片段的獨立封裝。特別是,如果子頁面片段需要使用第三方模塊,且這個模塊在啟動模塊中沒有事先加載時,就沒有辦法了。所以,必須要能夠?qū)崿F(xiàn)模塊的動態(tài)加載。實現(xiàn)模塊的動態(tài)加載就是把angular啟動過程中加載模塊的方式提取出來,再處理一些特殊情況。

動態(tài)加載模塊深入分析可以參考這篇文章:
http://www.tuicool.com/articles/jmuymiE

但是,實際跑起來發(fā)現(xiàn)文章中的代碼有問題,就是“$injector”到底是什么?研究了angular的源代碼injector.js才大概搞明白是怎么回事。

一個應(yīng)用有兩個$injector,providerInjector和instanceInjector。invokeQueue和用providerInjector,runBlocks用instanceProvider。如果$injector用錯了,就會找到需要的服務(wù)。

routeProvider中動態(tài)加載模塊文件。

template: "
page2
", resolve: { load: ["$q", function($q) { var defer = $q.defer(); /* 動態(tài)加載angular模塊 */ require(["/test/lazyspa/module1.js"], function(loader) { loader.onload && loader.onload(function() { defer.resolve(); }); }); return defer.promise; }] }

動態(tài)加載angular模塊

angular._lazyLoadModule = function(moduleName) {
    var m = angular.module(moduleName);
    console.log("register module:" + moduleName);
    /* 應(yīng)用的injector,和config中的injector不是同一個,是instanceInject,返回的是通過provider.$get創(chuàng)建的實例 */
    var $injector = angular.element(document).injector();
    /* 遞歸加載依賴的模塊 */
    angular.forEach(m.requires, function(r) {
        angular._lazyLoadModule(r);
    });
    /* 用provider的injector運行模塊的controller,directive等等 */
    angular.forEach(m._invokeQueue, function(invokeArgs) {
        try {
            var provider = providers.$injector.get(invokeArgs[0]);
            provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
        } catch (e) {
            console.error("load module invokeQueue failed:" + e.message, invokeArgs);
        }
    });
    /* 用provider的injector運行模塊的config */
    angular.forEach(m._configBlocks, function(invokeArgs) {
        try {
            providers.$injector.invoke.apply(providers.$injector, invokeArgs[2]);
        } catch (e) {
            console.error("load module configBlocks failed:" + e.message, invokeArgs);
        }
    });
    /* 用應(yīng)用的injector運行模塊的run */
    angular.forEach(m._runBlocks, function(fn) {
        $injector.invoke(fn);
    });
};

定義模塊
module1.js

define(["angular"], function(angular) {
    var onloads = [];
    var loadCss = function(url) {
        var link, head;
        link = document.createElement("link");
        link.href = url;
        link.rel = "stylesheet";
        head = document.querySelector("head");
        head.appendChild(link);
    };
    loadCss("http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css");
    /* !!! 動態(tài)定義requirejs !!!*/
    require.config({
        paths: {
            "ui-bootstrap-tpls": "http://cdn.bootcss.com/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.min"
        },
        shim: {
            "ui-bootstrap-tpls": {
                deps: ["angular"]
            }
        }
    });
    /*!!! 模塊中需要引用第三方的庫,加載模塊依賴的模塊 !!!*/
    require(["ui-bootstrap-tpls"], function() {
        var m1 = angular.module("module1", ["ui.bootstrap"]);
        m1.config(["$controllerProvider", function($controllerProvider) {
            console.log("module1 - config begin");
        }]);
        m1.controller("ctrlModule1", ["$scope", "$uibModal", function($scope, $uibModal) {
            console.log("module1 - ctrl begin");
            /*!!! 打開angular ui的對話框 !!!*/
            var dlg = "";
            dlg += "";
            dlg += "";
            $scope.openDialog = function() {
                $uibModal.open({
                    template: dlg,
                    controller: ["$scope", "$uibModalInstance", function($scope, $mi) {
                        $scope.cancel = function() {
                            $mi.dismiss();
                        };
                        $scope.ok = function() {
                            $mi.close();
                        };
                    }],
                    backdrop: "static"
                });
            };
        }]);
        /* !!!動態(tài)加載模塊!!! */
        angular._lazyLoadModule("module1");
        console.log("module1 loaded");
        angular.forEach(onloads, function(onload) {
            angular.isFunction(onload) && onload();
        });
    });
    return {
        onload: function(callback) {
            onloads.push(callback);
        }
    };
});
二、完整的代碼

index.html



    
        
        
        
        SPA
    
    
        
        

spa-loader.js

window.loading = {
    finish: function() {
        /* 保留個方法做一些加載完成后的處理,我實際的項目中會在這里結(jié)束加載動畫 */
    },
    load: function() {
        require.config({
            paths: {
                "domReady": "/static/js/domReady",
                "angular": "http://cdn.bootcss.com/angular.js/1.4.8/angular.min",
                "angular-route": "http://cdn.bootcss.com/angular.js/1.4.8/angular-route.min",
            },
            shim: {
                "angular": {
                    exports: "angular"
                },
                "angular-route": {
                    deps: ["angular"]
                },
            },
            deps: ["/test/lazyspa/spa.js"],
            urlArgs: "bust=" + (new Date()).getTime()
        });
    }
};
window.loading.load();

spa.js

"use strict";
define(["require", "angular", "angular-route"], function(require, angular) {
    var app = angular.module("app", ["ngRoute"]);
    /* 延遲加載模塊 */
    angular._lazyLoadModule = function(moduleName) {
        var m = angular.module(moduleName);
        console.log("register module:" + moduleName);
        /* 應(yīng)用的injector,和config中的injector不是同一個,是instanceInject,返回的是通過provider.$get創(chuàng)建的實例 */
        var $injector = angular.element(document).injector();
        /* 遞歸加載依賴的模塊 */
        angular.forEach(m.requires, function(r) {
            angular._lazyLoadModule(r);
        });
        /* 用provider的injector運行模塊的controller,directive等等 */
        angular.forEach(m._invokeQueue, function(invokeArgs) {
            try {
                var provider = providers.$injector.get(invokeArgs[0]);
                provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
            } catch (e) {
                console.error("load module invokeQueue failed:" + e.message, invokeArgs);
            }
        });
        /* 用provider的injector運行模塊的config */
        angular.forEach(m._configBlocks, function(invokeArgs) {
            try {
                providers.$injector.invoke.apply(providers.$injector, invokeArgs[2]);
            } catch (e) {
                console.error("load module configBlocks failed:" + e.message, invokeArgs);
            }
        });
        /* 用應(yīng)用的injector運行模塊的run */
        angular.forEach(m._runBlocks, function(fn) {
            $injector.invoke(fn);
        });
    };
    app.config(["$injector", "$locationProvider", "$routeProvider", "$controllerProvider", function($injector, $locationProvider, $routeProvider, $controllerProvider) {
        /**
         * config中的injector和應(yīng)用的injector不是同一個,是providerInjector,獲得的是provider,而不是通過provider創(chuàng)建的實例
         * 這個injector通過angular無法獲得,所以在執(zhí)行config的時候把它保存下來
        */
        app.providers = {
            $injector: $injector,
            $controllerProvider: $controllerProvider
        };
        /* 必須設(shè)置生效,否則下面的設(shè)置不生效 */
        $locationProvider.html5Mode(true);
        /* 根據(jù)url的變化加載內(nèi)容 */
        $routeProvider.when("/test/lazyspa/page1", {
            template: "
page1
", controller: "ctrlPage1" }).when("/test/lazyspa/page2", { template: "
page2
", resolve: { load: ["$q", function($q) { var defer = $q.defer(); /* 動態(tài)加載angular模塊 */ require(["/test/lazyspa/module1.js"], function(loader) { loader.onload && loader.onload(function() { defer.resolve(); }); }); return defer.promise; }] } }).otherwise({ template: "
main
", }); }]); app.controller("ctrlMain", ["$scope", "$location", function($scope, $location) { console.log("main controller"); /* 根據(jù)業(yè)務(wù)邏輯自動到缺省的視圖 */ $location.url("/test/lazyspa/page1"); }]); app.controller("ctrlPage1", ["$scope", "$templateCache", function($scope, $templateCache) { /* 用這種方式,ng-include配合,根據(jù)業(yè)務(wù)邏輯動態(tài)獲取頁面內(nèi)容 */ /* 動態(tài)的定義controller */ app.providers.$controllerProvider.register("ctrlPage1Dyna", ["$scope", function($scope) { $scope.openAlert = function() { alert("page1 alert"); }; }]); /* 動態(tài)定義頁面內(nèi)容 */ $templateCache.put("page1.html", "
"); }]); require(["domReady!"], function(document) { angular.bootstrap(document, ["app"]); }); });

module1.js

"use strict";
define(["angular"], function(angular) {
    var onloads = [];
    var loadCss = function(url) {
        var link, head;
        link = document.createElement("link");
        link.href = url;
        link.rel = "stylesheet";
        head = document.querySelector("head");
        head.appendChild(link);
    };
    loadCss("http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css");
    require.config({
        paths: {
            "ui-bootstrap-tpls": "http://cdn.bootcss.com/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.min"
        },
        shim: {
            "ui-bootstrap-tpls": {
                deps: ["angular"]
            }
        }
    });
    require(["ui-bootstrap-tpls"], function() {
        var m1 = angular.module("module1", ["ui.bootstrap"]);
        m1.config(["$controllerProvider", function($controllerProvider) {
            console.log("module1 - config begin");
        }]);
        m1.controller("ctrlModule1", ["$scope", "$uibModal", function($scope, $uibModal) {
            console.log("module1 - ctrl begin");
            var dlg = "";
            dlg += "";
            dlg += "";
            $scope.openDialog = function() {
                $uibModal.open({
                    template: dlg,
                    controller: ["$scope", "$uibModalInstance", function($scope, $mi) {
                        $scope.cancel = function() {
                            $mi.dismiss();
                        };
                        $scope.ok = function() {
                            $mi.close();
                        };
                    }],
                    backdrop: "static"
                });
            };
        }]);
        angular._lazyLoadModule("module1");
        console.log("module1 loaded");
        angular.forEach(onloads, function(onload) {
            angular.isFunction(onload) && onload();
        });
    });
    return {
        onload: function(callback) {
            onloads.push(callback);
        }
    };
});
寫后感

年初定下的目標(biāo)是堅持每周寫一篇自己在開發(fā)過程碰到的問題總結(jié),本以為是個簡單的事情,寫起來才發(fā)現(xiàn)寫文章的時間比寫代碼的花的時間還要長。因為寫代碼的時候只要功能實現(xiàn)了就行了,但是,寫文章的時候就一定要把代碼搞清楚才敢寫,實際上就是逼著自己要認(rèn)真研究源代碼,雖然壓力很大,但收獲更大。另一方面,發(fā)現(xiàn)找到一個好題目挺難的,只是簡單的貼別人的代碼沒意思,可是自己想出來有價值,有意思的問題挺難的。因此大家要是覺得有啥有意思,有價值前端問題,分享一下吧,給我的年度寫作計劃幫幫忙

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/78737.html

相關(guān)文章

  • avalon 單頁面程序 (種子工程)之一 用requirejs引入avalon

    摘要:現(xiàn)在微軟終于痛定思痛決定放棄了不支持的安全更新,對我們前端來說,真的是重大利好啊言歸正傳,這篇文章的目的就是把怎么用構(gòu)建一個單頁面程序介紹以下,是對自己的一個總結(jié),也喜歡對大家有一定的借鑒作用,寫的不好不對的地方希望大家多評論評論謝謝。 這篇文章是寫在公司項目結(jié)束之后的,因為我個人不太會把沒有實踐過的東西寫出來,實踐是檢驗真理的唯一標(biāo)準(zhǔn)么,用的怎么樣,好不好用,在成熟實踐過的項目上能體...

    solocoder 評論0 收藏0
  • angularjs學(xué)習(xí)筆記——使用requirejs動態(tài)注入控制器

    摘要:最近一段時間在學(xué)習(xí),由于覺得直接使用它需要加載很多的文件,因此想使用來實現(xiàn)異步加載,并動態(tài)注入控制器。手動啟動,特別說明此處的不是那個框架,而是的一個手動啟動框架的函數(shù)中完成了各模塊的初始化,并且引入了。 最近一段時間在學(xué)習(xí)angularjs,由于覺得直接使用它需要加載很多的js文件,因此想使用requirejs來實現(xiàn)異步加載,并動態(tài)注入控制器。簡單搜索了下發(fā)現(xiàn)好多教程寫的都很復(fù)雜,所...

    王軍 評論0 收藏0
  • avalon 單頁面程序 (種子工程)之二 按需加載和路由系統(tǒng)

    摘要:的成功離開不這三個東西,分層架構(gòu),路由系統(tǒng),儲存系統(tǒng)。分層架構(gòu)是我們組織復(fù)雜代碼的關(guān)鍵,路由系統(tǒng)是將多個頁面壓縮在一個頁面的關(guān)鍵。在這個種子工程中,我都調(diào)用了同一個方法,就比較適合目錄動態(tài)生成,需要按需調(diào)用不同的頁面的情況。 SPA的成功離開不這三個東西,分層架構(gòu),路由系統(tǒng),儲存系統(tǒng)。分層架構(gòu)是我們組織復(fù)雜代碼的關(guān)鍵,路由系統(tǒng)是將多個頁面壓縮在一個頁面的關(guān)鍵。 其中avalon路由用到...

    fanux 評論0 收藏0
  • 前端資源系列(4)-前端學(xué)習(xí)資源分享&前端面試資源匯總

    摘要:特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會及時更新,平時業(yè)務(wù)工作時也會不定期更...

    princekin 評論0 收藏0
  • Regularjs是什么

    摘要:目前已經(jīng)在大大小小多個線上產(chǎn)品中使用了,也收集了一些有效的建議好了,該看下一個最簡單的組件長什么樣吧免費領(lǐng)取驗證碼內(nèi)容安全短信發(fā)送直播點播體驗包及云服務(wù)器等套餐更多網(wǎng)易技術(shù)產(chǎn)品運營經(jīng)驗分享請訪問網(wǎng)易云社區(qū)。文章來源網(wǎng)易云社區(qū) 本文由作者鄭海波授權(quán)網(wǎng)易云社區(qū)發(fā)布。 此文摘自regularjs的指南, 目前指南正在全面更新, 把老文檔的【接口/語法部分】統(tǒng)一放到了獨立的 Reference...

    seal_de 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<