最近由於要用到angularJS開發項目,由於涉及到的靜態資源比較多,因此想把js文件經過requireJS來按需加載,這兩個框架之前都使用過,可是結合到一塊兒尚未用過,那就試一下,看可否達到目的。javascript
requireJS是爲了實現js文件異步加載和管理模塊之間依賴性的框架,詳情請看阮一峯 require.js的用法和RequireJS 中文網這裏就不作介紹了。html
咱們先來建立模版容器index.html
<!DOCTYPE html> <html> <head> <title></title> <script src="js/require/require.js" data-main="js/main"></script> </head> <body> <div ng-view></div> </body> </html>
惟一的一個js文件引入是require.js文件,這個是requireJS核心文件,這個script標籤的屬性data-main用來指定requireJS的入口文件,下方div是angularjs的dom容器,這裏由於要用到angularjs的手動調用,因此不用在div上指定ng-app屬性。html5
建立requireJS入口文件main.js
(function(){ require.config({ baseUrl : "js", paths : { "jquery" : "jquery/jquery-1.12.2.min", "angular" : "angular/angular", "angular-route" : "angular/angular-route", "domReady" : "require/domReady", "controllerModel" : "controller/controller" }, shim : { "angular" : { exports : "angular" }, "angular-route" : { deps : ["angular"], exports : "angular-route" } }, deps : ['app'] }); })();
- baseUrl 用來指定加載模塊的目錄,後期涉及到路徑都以這個目錄爲相對路徑。
- paths 指定模塊的加載路徑。
- shim 配置不兼容模塊
- deps 指定依賴模塊,requireJS會加載這個文件並執行。
咱們如今的文件目錄接口是這樣的:js文件目錄是這樣的
java
建立angularJS執行文件app.js
接下來咱們要建立angularJS模塊而且配置路由而後經過內置方法bootstrap來手動觸發angularJS。 app.js文件是經過requireJS來動態加載的,因此要按照AMD規範寫。jquery
(function(){ define(["angular","angular-route","mainController",'domReady!'],function(angular){ //建立angularJS模塊 var app = angular.module("webapp",[ 'ngRoute', 'webapp.controllers' ]); //設置angularJS路由 app.config(function($routeProvider,$locationProvider){ $routeProvider.when("/",{ templateUrl : "tpl/sy.html", controller : "syCtrl" }).when("/login",{ templateUrl : "tpl/login.html", controller : "loginCtrl" }); $locationProvider.html5Mode(false).hashPrefix("!"); }); //手動觸發angularJS angular.bootstrap(document,['webapp']); return app; }); })();
app.js依賴的js模塊有angularjs
- angular angularJS核心文件
- angular-route angularJS路由文件
- domReady! 這個是requireJS的插件可讓回調函數在頁面DOM結構加載完成後再運行。
- mainController 這個是咱們接下來要寫的控制器
建立模版
在建立控制器前咱們先建立下模版,按照上面路由描述,有兩個頁面一個是sy.html,一個是login.html,建立好這兩個模版,並把他們放到tpl文件夾中。
sy.htmlweb
<h1>{{data}}</h1> <a href="javascript:void(0)" ng-click="goLogin()">去login頁面</a>
login.htmlbootstrap
<h1>{{data}}</h1> <a href="javascript:void(0)" ng-click="goSy()">去sy頁面</a>
建立控制器mainController
在app.js文件中,經過requireJS加載mainController模塊,mainController模塊能夠認爲控制器的入口,這裏去異步加載全部控制器。數組
按照路由描述規則,咱們須要建立syCtrl和loginCtrl這兩個控制器,而且添加到控制器入口文件mainController.js中。app
mainController.js
(function(){ define([ 'controller/syCtrl', 'controller/loginCtrl' ],function(){ }); })();
接下來咱們建立syCtrl和loginCtrl這兩個控制器,在建立這兩個控制器前,咱們先看下angularJS是怎麼建立控制器的。
var angularController = angular.module("angularController",[]); angularController.controller("ctrlName",function(){ });
從上面代碼能夠看出angularJS的控制器都是先建立angular模塊,而後執行當前模塊controller方法來綁定控制器,在app.js文件中建立的新模塊依賴注入當前模塊angularController。
在app.js文件中咱們指定依賴的控制器模塊是webapp.controllers。咱們須要建立一個文件,這個文件是爲全部控制器文件提供webapp.controller模塊同時把全部控制器都綁定到webapp.controllers模塊中,而後app.js文件中設置依賴注入後控制器就能夠執行了。 咱們建立controller文件夾放到js文件夾中,建立以下文件:
controller.js文件就是咱們要建立的控制器模塊公用文件,下方兩個文件都是單個控制器文件,這兩個文件都依賴controller.js提供的模塊。
咱們在main.js文件中配置controller.js名稱是controllerModel因此在須要依賴controller.js均可以直接依賴controllerModel。
controller.js
(function(){ define(['angular'], function (angular) { return angular.module('webapp.controllers', []); }); })();
controllerModel模塊返回建立的angularJS模塊webapp.controllers,app.js文件中放到module方法第二個參數中設定依賴注入。
接下來咱們再建立syCtrl.js和login.js文件
syCtrl.js
(function(){ define(['controllerModel'],function(controllers){ controllers.controller('syCtrl',function($scope,$location){ $scope.data = "我是sy"; $scope.goLogin = function(){ $location.path("/login") } }) }) })();
login.js
(function(){ define(['controllerModel'],function(controllers){ controllers.controller('loginCtrl',function($scope,$location){ $scope.data = "我是login"; $scope.goSy = function(){ $location.path("/") } }) }) })();
這兩個控制器都依賴controllerModel,而後調用controllerModel模塊的controller方法來建立控制器,剩下內容就是在控制器中綁定數據。
貌似搞砸了
完成上面全部步驟後,這個應用終於完成了,咱們在打開頁面看下效果
打開首頁後是這樣的
點擊去login頁面後跳轉到login頁面而後點擊去sy頁面也能跳轉到sy頁面
功能貌似沒有問題,我要的按需加載實現了嗎?咱們看下文件的加載狀況,我須要的功能是在訪問sy頁面的時候調用syCtrl控制器,訪問login頁面調用loginCtrl控制器。惋惜,在訪問首頁的時候控制器就所有被加載,這樣的結果是必然的,由於咱們經過requireJS加載控制器的時候mainController.js文件是將全部的控制器都加載過來的。而mainController.js模塊在建立webapp模塊的時候加載執行,只執行一次,因此必須將全部控制器加載而且綁定到webapp模塊上。
個人本意是將各個控制器分紅單獨的文件,而後須要加載某個控制器的時候再去調用。在實際開發中,涉及到的模塊太多的時候咱們但願經過單獨的文件來管理單獨的模塊,而後經過grunt等工具在線下合併成一個文件在生產環境中使用,這樣合併後的文件若是很大的話會影響頁面的加載速度,若是不合並的話請求數又會太多,因此經過requireJS來異步加載各個模塊,咱們上面所作的不是沒有意義,畢竟咱們解決了加載文件太大的問題。
angularAMD 實現按需加載
咱們作了不少工做,可是沒有解決最根本的問題——按需加載。接下來咱們要解決這個問題。 咱們用另一個事例來簡單說明angularJS控制器、服務的按需加載,指令的按需加載我認爲沒有必要,自定義的指令大多做爲公用功能來處理,這樣的功能原本就是全局的,因此在實建立angular模塊的時候指定requireJS依賴關係直接調用就行。
安裝
bower install angularAMD bower install angular-ui-router
bower 會自動加載依賴的js文件,全部文件都放入bower_components文件夾
建立模版文件和js入口文件
index.html
<!DOCTYPE html> <html> <head> <!-- meta --> <meta charset="utf-8"> <!-- title --> <title></title> <!-- script --> <script data-main="main.js" src="bower_components/requirejs/require.js"></script> </head> <body> <!-- content --> <div ui-view></div> </body> </html>
模版文件和上次建立的模版文件同樣,都是經過requireJS入口文件來處理接下來的工做。
main.js
(function(){ require.config({ paths: { "angular": "bower_components/angular/angular", "angular-ui-router": "bower_components/angular-ui-router/release/angular-ui-router", "angularAMD": "bower_components/angularAMD/angularAMD", "ngload": "bower_components/angularAMD/ngload" }, shim: { "angular": { exports: "angular" }, "angular-ui-router": ["angular"], "angularAMD": ["angular"], "ngload": ["angularAMD"] }, deps : ["app"] }); })();
main.js文件的內容仍是和之前同樣,配置好各個模塊的url,而且指定依賴模塊app.js文件,app.js是建立angularJS模塊的入口。
接下來咱們建立app.js文件
app.js
(function(){ define(["angular", "angularAMD", "angular-ui-router"], function (angular, angularAMD) { //設置路由 var registerRoutes = function($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise("/home"); $stateProvider // home .state("home", angularAMD.route({ url: "/home", templateUrl: "home.html", controllerUrl: "home.js" })) // home .state("about", angularAMD.route({ url: "/about", templateUrl: "about.html", controllerUrl: "about.js" })) ; }; //實例化angularJS var app = angular.module("app", ["ui.router"]); //配置 app.config(["$stateProvider", "$urlRouterProvider", registerRoutes]); //手動啓動angularJS 並返回angularJS實例對象 return angularAMD.bootstrap(app); }); })();
咱們這裏建立的app.js文件和前文說道的app.js文件功能同樣,惟一的區別是依賴的模塊內容變化,一樣在使用對應模塊的時候也是不同的,可是實現的功能是同樣的。
同時這裏須要注意,配置路由的時候,不光要指定ur和模版url還要指定每一個模版對應的控制器的url
沒錯,這樣簡單的配置就能夠實現控制器的按需加載。咱們訪問home頁面angularJS會自動加載home.html模版和home.js控制器,加載完成後的執行方式和前邊是同樣的。
添加模版文件home.html和控制器文件home.js
home.html
<h1>{{ title }}</h1> <br/> <button ui-sref="about">About</button>
home.js
(function(){ define([], function () { return ["$scope", function ($scope) { $scope.title = "This is Home page"; }]; }); })();
home.js是home控制器,能夠看到寫法和方法的sy控制器、login控制器都不同,首先經過requrieJS來肯定依賴模塊,而後在回調函數返回數組,數組的最後一個元素是控制器的執行函數,前邊的都是控制器須要加載的服務,其實返回值就是建立控制器函數controller的第二個參數,第一個參數是控制器名稱,這個應該是內部自動指定了。
這樣咱們第一個路由就建立好了,訪問頁面如圖
添加about模版文件about.html和控制器about.js
about.html
<h1>{{ title }}</h1> <h1>{{ user.name}}</h1> <br/> <button ui-sref="home">Home</button>
about.js
(function(){ define([], function () { return ["$scope", function ($scope) { $scope.title = "This is About page"; $scope.user = "tudou"; }]; }); })();
about.js和html.js內容同樣,不作解釋了。
控制器按需加載了
訪問home頁面js資源加載狀況以下前面的文件是依賴模塊加載,home.js和home.html是當前頁面須要的模版和資源,沒有加載about頁面的內容,咱們點擊按鈕去about頁面看下。能夠看到在about頁面新加載了兩個文件about.js和about.html
咱們controller的按需加載實現了,接下來咱們要實現服務的按需加載
服務按需加載
建立服務UserRepository.js文件
(function(){ define(["app"],function(app){ app.factory("UserRepository", function(){ return {name:"tudou"}; }); }); })();
服務的建立依賴angularJS模塊,上文是在controller/controller.js中建立了一個專門針對控制器的angularJS模塊,此次咱們直接調用app.js返回的angularJS模塊,原理和上文說到的同樣。 執行模塊的factory方法申明一個服務,這個服務就生成了。
調用服務
其實在建立about.js和home.js時咱們已經調用了$scope服務,不過$scope服務是angularJS內置的服務,因此咱們要調用自定義服務須要先加載文件,而後在申明調用的服務傳遞到控制器裏面就能夠直接使用了。 在define方法的第一個參數中聲明依賴的js文件,在回調函數返回的數組中申明服務,about.js修改爲下方代碼
(function(){ define(["UserRepository"], function (UserRepository) { return ["$scope", "UserRepository",function ($scope,UserRepository) { $scope.title = "This is About page"; console.log(UserRepository); $scope.user = UserRepository; }]; }); })();
這樣咱們angularJS控制器和服務的按需加載就完成了。還有過濾器的模塊化應該和服務是同樣的。