AngularJS按需動態載入template和controller?

初次使用angularjs做項目,但是發現angularjs在路由配置後會一次性載入所有依賴文件,這對於大一點的項目來說是不可接受的,使用requirejs也不能阻止路由配置處angularjs自己去載入文件。

然後我找到了angularAMD,在angularjs和angular-ui-router的環境下是可以實現template、controller的動態載入(也就是進入哪個頁面就載入改頁面相關內容)。

但是由於不想自己折騰太多的UI,我使用了很火的框架ionic,他對路由模塊可能坐了自己的封裝,使用了&而不是&,然後導致控制器能按需載入,模板卻一次性載入。

我谷歌了這些內容,發現相關話題非常少,難道是我理解有誤?angular不需要動態載入模塊嗎,但是對於有很多個頁面的項目,這肯定是不合理的,譬如我們要做的是hybird app,頁面量很大。不知道大家在使用angular時都是怎麼對待這個需求的


佔位 謝@張治中邀請

樓上的幾個答案我都看了一下

1、把單頁面應用做成應用中所有頁面都載入並且重新初始化Angularjs框架的行為在我個人的角度是無法接受的,不論是從交互體驗的角度上還是從技術追求的角度。當然,如果從公司的角度,加上其他因素的影響,最後根據各方面實際情況(開發周期、團隊技術儲備、產品經理交互需求等等)的妥協與折衷,這樣做其實也是可以的。

2、使用oclazyload的方案我調研了一下,是可行的,但是想說這個方案有些缺點,比如每次動態載入需要的腳本、模版資源會有很多不必要的網路開銷,路由定義比較複雜(多了一些配置項,其實不能算複雜,而是繁瑣),對於大型複雜業務應用,路由眾多,耗費的精力不可忽視。在實際做對外開放的產品時,我們一般會把使用requirejs管理依賴關係的代碼打包壓縮,加版本號,同時根據項目情況決定要不要按照業務模塊做拆分打包非同步按需載入。

3、不知道樓主開發的產品是不是移動端的單頁面應用,不知道樓主的應用是否業務複雜以及腳本文件的大小在什麼量級,所以我下面講解的技術方案可能在某些地方並不適合樓主的場景,但是原理是相通的,樓主可以參考一下然後看看是否可以解決你的項目中面臨的問題。如果需要,可以隨時找我一起討論你所面臨的問題以及採用哪種方式解決最好。

4、佔位,晚上有空更新。

===========2015-06-08更新============

最近一直比較忙,今天抽空更新一部分。

看了樓主的評論回復,移動端和PC瀏覽器端,對於Angular本身而言沒有區別,所以我說的這些應該也適用於移動端Angularjs應用。

首先說一下Angularjs的啟動原理,就知道為什麼很少有人做Angular代碼的非同步載入了。

好像知乎不支持markdown格式,寫起來好難受,只能拿 「等號」玩了

========================Angular框架啟動原理分析==============================

我們現在一般配合Requirejs 做代碼依賴管理的情況下,都是在RequireJs的入口文件中,執行以下代碼來啟動Angular框架:

angular.element(document).ready(function() {
angular.bootstrap(document, ["vpcConsole"]);
});

這種情況下,我們來扒代碼看看 angular.bootstrap都幹了什麼(無關緊要的代碼用省略號表示了):

function bootstrap(element, modules) {
var doBootstrap = function() {

//獲取Jquery的Dom對象
element = jqLite(element);

……

//這句話是最關鍵的點,我們會繼續深挖createInjector這個函數
var injector = createInjector(modules);
injector.invoke(["$rootScope", "$rootElement", "$compile", "$injector", "$animate",
function(scope, element, compile, injector, animate) {
scope.$apply(function() {
element.data("$injector", injector);
compile(element)(scope);
});
}]
);
return injector;
};

//以下代碼應該是為了支持測試用的,不用關心,感興趣可以看文檔說明:http://docs.angularjs.cn/guide/bootstrap
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;

if (window !NG_DEFER_BOOTSTRAP.test(window.name)) {
return doBootstrap();
}

window.name = window.name.replace(NG_DEFER_BOOTSTRAP, "");
angular.resumeBootstrap = function(extraModules) {
forEach(extraModules, function(module) {
modules.push(module);
});
doBootstrap();
};
}

在上面的bootstrap方法裡面,我們找到了一行非常關鍵的代碼,調用了createInjector方法,接下來我們看下createInjector方法裡面幹了什麼事情(不重要的代碼統統省略號)。

function createInjector(modulesToLoad) {
/*
開始部分申明了一堆變數,無視掉
*/
……

//重要的代碼,loadModules方法需要深挖
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });

return instanceInjector;

/*
源碼裡面這裡寫了一堆函數定義,由於function 聲明會在js解析引擎裡面被提前,所以前面有return語句也沒關係
*/
……
}

然後再來看 loadModules裡面的關鍵語句(loadModules函數其實就定義在了 createInjector函數裡面)

function loadModules(modulesToLoad){
var runBlocks = [], moduleFn, invokeQueue, i, ii;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
loadedModules.put(module, true);

try {
if (isString(module)) {
//得到angular模塊
moduleFn = angularModule(module);
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);

//這裡使用for循環讀取了modelFn的_invokeQueue,然後做遍歷執行,這個_invokeQueue 是很關鍵的東西,等你知道了它的由來,就知道為什麼Angularjs天然不支持非同步載入了。
for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i &< ii; i++) { var invokeArgs = invokeQueue[i], provider = providerInjector.get(invokeArgs[0]); provider[invokeArgs[1]].apply(provider, invokeArgs[2]); } } else if (isFunction(module)) { runBlocks.push(providerInjector.invoke(module)); } else if (isArray(module)) { runBlocks.push(providerInjector.invoke(module)); } else { assertArgFn(module, "module"); } } catch (e) { /* 異常處理,不關心 */ …… } }); return runBlocks; }

上面的代碼中已經發現_invokeQueue 是個很重要的東西,那麼我們來看下它在什麼地方生成的(這個地方代碼比較長,但是非常非常關鍵,無用代碼直接省略號)。

function setupModuleLoader(window) {

……

function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}

//給window對象添加一個 angular 屬性
var angular = ensure(window, "angular", Object);

……

return ensure(angular, "module", function() {

var modules = {};

return function module(name, requires, configFn) {
……
return ensure(modules, name, function() {

//這個就是我們上面提到的_invokeQueue;
var invokeQueue = [];

/** @type {!Array.&} */
var runBlocks = [];

var config = invokeLater("$injector", "invoke");

//這個就是我們調用 angular.module()以後得到的實例,注意它對外暴露的介面都是用什麼實現的(invokeLater做的實現)
var moduleInstance = {
//這個就是很重要的module的一個屬性,_invokeQueue,前面的代碼很清楚,它是一個數組,而且這個數組裡面的東西,只有在angular.bootstrap的時候才會被執行,明白這一點非常關鍵非常重要。
_invokeQueue: invokeQueue,
_runBlocks: runBlocks,
requires: requires,
name: name,
/*
下面這些就是我們經常調用的各種介面,注意它的實現,都是使用invokeLater來做了一次封裝,下面的代碼注釋裡面說明了invokeLater的作用。
*/
provider: invokeLater("$provide", "provider"),
factory: invokeLater("$provide", "factory"),
service: invokeLater("$provide", "service"),
value: invokeLater("$provide", "value"),
constant: invokeLater("$provide", "constant", "unshift"),
animation: invokeLater("$animateProvider", "register"),
filter: invokeLater("$filterProvider", "register"),
controller: invokeLater("$controllerProvider", "register"),
directive: invokeLater("$compileProvider", "directive"),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};

if (configFn) {
config(configFn);
}

return moduleInstance;

/*這裡是InvokeLater的實現,我們調用 Angular的module實例所註冊的factory、filter、controller、directive 等等都是通過這個方法放在了 invokeQueue裡面,也就是說,當我們在代碼裡面執行 xxxModule.directive("mydirective",["xxx",function(xxx){}])
的時候,其實這個directive並沒有真正被註冊,而是放在了一個invokeQueue裡面,知道這一點很重要。同理,我們定義一個controller的時候,也沒有真正註冊執行這個controller,而是這個controller被放在了一個數組裡面,等著angular.bootstrap真正去執行並實例化這個函數
*/
function invokeLater(provider, method, insertMethod) {
return function() {
invokeQueue[insertMethod || "push"]([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}

代碼扒到這裡就差不多了。如果你看明白了整個angular.bootstrap的時候的來龍去脈,就會發現,假設我當前頁面已經載入了相關資源,Angular框架已經運行起來(執行了angualr.bootstrap方法),那麼我後續通過按需載入引入的javascript腳本文件中的那些

xxxModule.controller("xxx",["yyy",function(yyy){}])、

xxxModule.directive("zzz",["www",function(www){}])

代碼都雖然會在requirejs引入的時候被執行一遍,但是執行的結果僅僅是把這些controller和directive和factory等等函數放在了一個invokeLater的數組裡面,我們的前端路由激活的時候,去通過angular尋找對應路由(視圖)的controller的時候,發現根本沒有這個controller,原因就是在這裡:Angular框架啟動以後(執行了bootstrap方法之後),它讀取controller構造函數、directive構造函數等的地方和我們執行 controller方法、directive方法所註冊進去的代碼不是一個地方(或者說不是緩存在一個變數裡面),所以,在按需載入的情況下,雖然我們的代碼看起來執行了,但是真正Angular的部分卻並沒有真正的執行,而是僅僅被放在一個地方等待著「invokeLater」(其實再等多久也沒用,因為不能再執行angular.bootstrap方法了)。

=======================非同步按需載入方案分析======================

AngularJs框架啟動原理分析完以後,就可以分析非同步按需載入方案了。首先我們來看一下我們做非同步按需載入方案需要解決哪些問題:

1、angularjs框架啟動後,調用 各種api無法真正註冊相關構造函數的問題

2、路由激活時,載入當前路由需要的腳本資源(考慮防止重複載入、考慮最大化利用客戶端緩存等等問題)

3、當前單頁面應用模塊劃分和打包(當上面兩個技術問題解決以後,就要考慮這個偏向業務的問題了)

我們先來看第一個問題怎麼解決:

1、解決非同步按需載入代碼後Angular原生代碼無法真正註冊各種構造函數的問題

經過上面的載入原理分析以後,我們就知道該怎麼辦了:把angular.module("xxx")的實例的factory、controller、directive、value、filter 等等方法都「變換」掉,讓我們的代碼執行這些方法的時候,直接把我們的函數放在運行期的對應的緩存的對象裡面,這樣一來非同步載入的代碼就會在執行的時候真正被註冊到Angular運行時可以讀取的地方(Angular運行時具體緩存各個構造函數的地方自己扒源碼吧,懶得貼出來了),這樣在路由激活的時候,就可以找到對應的controller,然後執行。

這個辦法可以參考下面的代碼(可以參考前面同學回答的oclazyload這個框架裡面的代碼,但是這個框架作了很多其他的事情,導致最核心的思想沒有很清晰的體現,不過也可以讀一讀,挺有意思):

(function()
{
var app = angular.module("app", []);

app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide)
{
app.controllerProvider = $controllerProvider;
app.compileProvider = $compileProvider;
app.routeProvider = $routeProvider;
app.filterProvider = $filterProvider;
app.provide = $provide;

// Register routes with the $routeProvider
});
})();

解決這個問題以後,我們來看第2個按路由載入代碼的問題如何解決:(精力有限,暫時更新到這裡,預計本周還會更新一次,先給個提示:按照路由去載入所需的代碼方案有很多種,常見的是就在定義路由的時候定義一個 resolve,在裡面做資源的載入,但是這種方法需要在路由定義的敵方寫比較多的東西,不是很喜歡這種方式。題主你的手機端應用建議你做好合理的模塊或者說頁面的劃分,我初步的建議是你每個頁面內部的資源全部都打包成一個腳本包括視圖模板也打包進去,最終看文件的大小是不是在移動端單次web請求允許的範圍之內,然後angularjs相關的和公共代碼打包成一個,應用啟動時載入這個公共的腳本,切換到其他頁面或者路由的時候公共的資源腳本已經運行起來,不需要再載入,而只需載入對應頁面或者路由的腳本就可以了。初步可以這樣,後續再詳細更新說明具體的方案和原因。)

===========2015-06-21更新============

抱歉最近非常忙,更新時間略長

之前的內容講解了Angularjs支持動態載入腳本相關的局限性(動態引入到瀏覽器裡面的代碼中,沒有經過處理的話,所有的 xxxModule.directive()、xxxModule.controller() 執行過後並沒有真正立刻被實例化,而是放在了一個緩存中等待app bootstrap的時候去執行,可是由於這些代碼是動態引入的,在它們引入之前,app 已經bootstrap過了。),現在開始講如何配合 requirejs 做相關的處理來支持動態載入:

先看下有幾個問題需要解決:

1、確定按需載入的靜態資源引入頁面的時機。

2、按需載入的靜態資源打包方式。

3、其他一些問題的整體考慮。

依次回答上面三個問題,加上之前更新的內容,然後關於AngularJs動態載入腳本的問題和相關的原理基本上可以說明白了。

1: 毫無疑問,在用戶瀏覽某個「頁面」的時候,應用會激活對應的路由,然後去載入對應的資源腳本。在我們實際開發過程中,一個業務模塊下面會有n個相關的子頁面來共同服務於某個業務需求,這種情況下,我們會給這n個子頁面定義一個最基本的父路由,在進入該父路由的時候,去判斷當前路由相關的資源腳本是否載入,如果沒有載入的話,則利用requirejs去 獲取資源腳本文件,等拿到並執行以後,再執行相關的業務邏輯(比如激活某個頁面的路由、實例化對應的controller函數);如果已經載入過了,則和正常的路由解析過程一致,直接依次執行其子路由直到用戶想要訪問的頁面對應的路由激活。所以說,在不引入其他第三方庫的情況下(或者已經引入了某個庫,不再適合引入其他庫的情況下),我們可能需要在路由定義的地方做手腳,加入自己的靜態資源載入服務。

2:由於每個業務模塊有n個頁面,所以一定有大量的其他代碼一起配合完成相關的業務邏輯,比如有很多directive,有很多filter,這些腳本有的是我們單頁面應用裡面其他業務模塊用不到的,有的是其他業務模塊可以共用的,所以我們在開發過程中,除了以業務模塊為維度統一划分開發文件結構以外,還會多出一個 common 或者 base 這樣類型的文件夾,專門負責放置應用內部各個業務模塊都使用的公共代碼或者公共組件等等。這種情況下,我們的項目中,app文件夾下面的一級文件夾就是 common + n 個業務模塊文件夾。在這種項目文件結構下,我們會把 common 和所有用到的第三方組件資源腳本打包、壓縮在一個文件中,在用戶打開瀏覽器載入整個單頁面應用的時候最先載入並執行,其他n個業務模塊,每一個文件夾下的腳本資源單獨打包壓縮成一個js文件,每個業務模塊文件夾下需要一個種子文件去引用依賴當前項目文件夾中的其他資源腳本,這樣在使用r.js做打包的時候,就可以以這個文件為種子文件將其依賴的腳本打包到一個文件裡面。在打包業務模塊的腳本的時候,一定會有某些腳本引用了 common 里的文件或者第三方組件中的文件,所以需要在打包配置項中,做一些配置,告訴打包工具不把聲明的這些文件打包到當前文件中。具體的配置項可以參考 r.js 中的 path配置項,其中某個子項如果使用 xxx:「empty:」 這樣的寫法的話,則該聲明意為不把 xxx 打包到當前打包的文件中。可以參考r.js的示例配置文件:https://github.com/jrburke/r.js/blob/master/build/example.build.js#L39

這樣,打包部分的方式和方法也說明了,具體相關配置不太熟悉的同學可以看requirejs的官方文檔或者直接看 r.js 的github項目r.js/example.build.js at master · jrburke/r.js · GitHub

大部分同學應該是使用 grunt-requirejs做打包的,其實和r.js是一樣的,所以看上面的鏈接地址的代碼和注釋就可以了。

3:在做這樣的技術改造或者技術升級的時候,需要考慮很多很多因素,而不是僅僅去找幾個相同的例子然後把自己的代碼改成那樣就好了,而是需要考慮已有代碼做升級的時候,如何才能改動最小,對已有代碼侵入最少,還要考慮到對開發人員的友好性,考慮到測試和線上調試的方便程度,還要考慮到開發團隊內部其他成員的接受程度等等。以題主目前的處境來看,其實你的業務代碼的按需載入已經由你採用的框架幫你實現,現在你的困惑主要集中在模板部分不能按需載入,所以,我們可以把目光直接聚焦到如何解決 模板不能按需載入的問題上,而不用再去考慮如何做業務腳本的按需載入的整體實現。所以,針對題主你的問題,我建議如下(假設你的HTML模板都已經打包到了一個文件中):1、使用 grunt-html2js 打包轉換HTML模板為js文件 2、打包後的模板緩存文件和業務模塊代碼打包到一起 3、業務腳本動態引入的時候,自動引入HTML模板的js文件,這樣就實現了HTML模板的動態引入。原理:Angularjs 的指令、State 等等在處理 templateUrl 的時候,會去服務端按照templateUrl的地址發起請求,但是在請求真正發起之前會使用這個Url作為key去 $templateCache 中查找有沒有該key對應的緩存,如果有的話,則不會真的發起請求,而是直接採用緩存中的HTML字元串。所以我們的 grunt-html2js會生成js文件,這個文件裡面,每個HTML模板都會以某個Url(這個Url取決於自己的配置)作為自己的key,然後把模板內容轉義為字元串,存入$templateCache中。所以,題主,你可以按照這個思路重新調整一下模板相關的部分,多做debug,觀察每個視圖激活的過程,看下templateUrl的處理,就知道該怎麼做了。

更新完畢,有問題直接留言就好了。


說一下我的觀點:

1.對於一般中小型體量的angular應用而言,按需載入是利大於弊的,且不說它帶來的方案上的複雜度,單說它試圖解決的減少首頁體積本身就是個偽命題。中小型應用中,angular體系下要寫的業務代碼量並不會很大,再配合壓縮合併gzip,體積更是有限,這點很多回答都有提到過。相反的,按需載入反而會帶來子路有模板頁首次載入緩慢,給用戶造成應用不流暢的感覺。

2.我是抵制在angular中引入requirejs的。requirejs本質是一個模塊載入器,按需載入只是他的副業,我們用它應該主要是用它來做模塊化的,但是大部分使用者在angular里引入requirejs來單純做按需載入。。所以我們單純為了按需載入卻要試用模塊化語法包裝我們的代碼(define),無意義的事做的太多了。。

2.大體量的應用(大部分可以視作組合應用,比如首頁頂部是一列tab,每個tab對應一個大的業務/產品模塊),這個時候是有必要做按需載入的。即點擊tab的時候才載入具體產品資源。但是這裡也分兩塊,一塊是產品tab的切換做路由切換view,如 點擊 產品a tab -&> a-index.html,而a-index.html中按原始的標籤方式引入css跟js,這樣也不影響每個產品獨立發布。但是每個產品內部又是資源全部打包合併的方式。兩種方式結合來做。方案是 ui-router + ocLazyload,需要對ui-router做少許改造,做到按需載入無感知,而不是手動require。具體可以看這裡 基於ui-router的非侵入式angular按需載入方案 · Issue #31 · kuitos/kuitos.github.io · GitHub

總之對於angular體系下的單頁應用,我覺得大部分產品是沒必要做按需載入的。


我們還真沒關心過這個問題,angular理論上是single page,但我們的實踐中實際上還多頁面的,一個子模塊一個頁面,每個子模塊是一個獨立的angular app,整個網站實際上多頁面的。


鄙人才疏學淺分析不出來那麼多,直接貼文檔和解決方案。

templates.maxPrefetch(value)

Sets the maximum number of templates to prefetch from the templateUrls defined in

$stateProvider.state. If set to 0, the user will have to wait

for a template to be fetched the first time when navigating to a new page. Default 30.

就是他了,預載入template,默認值30

.config(function($ionicConfigProvider) {
$ionicConfigProvider.templates.maxPrefetch(0);
});
這樣就不會去預載入了


今天有時間,項目也結了,自己來回答一下這個問題。

首先特別感謝 @賀科學先生的耐心解答,從原理層面幫我分析了我的問題該怎麼解決,留給我很大的思考空間。(本人接觸angularjs層面比較簽,還沒從研究源碼層面去發現原理,解決問題,這種方式真的讓我要好好學習)。由於6月份在參加期末考試,6月份之前就在實習的公司把架構定下來了,並沒有採用按需載入模塊的方式去折騰了。但是我還是會好好理解一下賀先生的方案。

先來談談我是如何處理這件事的,我的回答是項目完結後添加的,我說不出很深的東西,但都很實際。

結論:中小型項目中,angularjs真的沒必要折騰按需載入。

從結果來看,手機端那套js壓縮後不到50k,並不是很大。我們也使用了gzip傳輸時再壓了一次。

我們可以借用gulp、grunt等工具完成開發過程中遇到的一些問題。

1.將多個js文件合併成一個js文件,並壓縮。js文件可以按類型合併,所有contoller、service、directive、filter都並成各自的文件,最終一個app有5個這類文件:路由js、contoller.js、service.js、directive.js、filter.js。

2.為了開發時方便,開發時肯定是分文件寫,不可能把所有controller都寫一個controller.js里,那麼如何分文件寫,又能實時的看合併後的文件的效果呢?可以使用gulp的watch模塊,監聽文件變化,每次變化後都重新合併更新,不用擔心浪費資源,這點監聽任務對現代的計算機來說不算什麼,不到10ms的事情。在html中引入的應是合併後的發行目錄下的js文件。

當然我說的只是針對我的項目,對於我的項目,每個頁面都不是很複雜,js代碼500行之內絕對能搞定,但是頁面多,如果你給每個頁面都寫一個contrllor.js的話,可知跑完一個app,用戶得請求多少次網路。而我們將所有js文件分類打包成一個文件並壓縮,這樣不同類的js文件加起來才不到50k,而伺服器再使用gzip壓縮這些文件,就更小了,根本不恐怖。但是大型項目就不一樣了,我覺得通過分模塊的方式,每個模塊一個app一樣的解決方案,可能會比使用按需載入更有意義。

我們並沒有對html壓縮,為了可讀性,而且html壓縮並不是很有意義,倒是很容易出bug。

具體項目架構情況,請看我的博客:web app開發心得

學生黨,能力有限,但是追求技術,我的方案如有不科學的地方,還請各位前輩指出。


樓上說了那麼多,都沒用,這裡有個種子項目 ifyio/angularjs-lazy-loading-with-requirejs · GitHub

我以前也是這個問題,看了這個項目就懂了、、


ionic不是載入所有模板,是預先載入個數默認比較大,20還是30來著,改小點就好了,沒必要引入其它解決方案。參數名文檔里找找,記不起來了


ocLazyLoad

之前在看angular前端模板的時候有看到這種寫法,然後就模仿了過來,只是模仿,還不知其所以然。自己道行實在太淺,樓上幾個大神的回答簡直太專業,看起來幾乎看不懂。。

項目中

在index頁面里

在這個route.js 里


如果是router的按需載入, 完全是基於resovle來做的, resovle由於本身是promise, 所以這裡就可以用promise的方式去載入一些js文件, 然後再載入路由的頁面, 這裡很好理解, 沒什麼問題,

現在的問題是, 如何讓directive, 或者ng-include達到同樣的效果, 因為有時候確實不想變瀏覽器里顯示的地址

我現在能想到的是如果能動態給div元素綁定controller, 或許能夠達到目的, 就是ng-controller這裡能不能用js達到同樣的效果而不是一定要寫在頁面上, 看到過一個例子, 似乎是給元素添加這個屬性, 然後調element.injector, 但是這裡我還沒有嘗試, 需要嘗試後再說

由於include無法像resovle那樣通過promise控制真正載入模板的時機, 所以, 如果include里的元素綁定了controller, 這個controller必須定義在父頁面里(換句話說就是很早就被引入), 這是讓人很不爽的地方

按需載入是一種理念, 和工程大小無關


我前面工作中有過類似項目 然後做的一個demo

外部用requireJS 實現模塊化封裝 ,裡面重新註冊了服務,ui-router 通過$q.deffer() 來實現路由的按需求載入 算是一個基本的demo 肯定有很多不足,有興趣的可以看一下

github地址:

GitHub - yangfan0095/angularJs-uiRouter-RequireJsDemo: angularJs-uiRouter-RequireJs demo


我之前遇到過一個問題是:

使用了ui-router後,是否要把所有頁面的js(因為有很多不同的controller)全寫在一個文件里,一次性全載入下來。

後來發現在template里引入&就行了。但運行後,這個script不執行,結果在整個項目里引入了Jquery就可以了。搞定。

另一種是別人說的用requirejs



目前用的是UI-router +oclazyload


推薦閱讀:

angular中控制器之間的傳值該怎麼實現?
angular中,controller、directive和factory分別該在何時使用?
angularjs中不同頁面controller中數據傳遞的問題?
AngularJS的數據雙向綁定是怎麼實現的?
AngularJS 1.x 的缺點如何解決?

TAG:模塊化 | 前端開發框架和庫 | RequireJS | AngularJS | Ionic |