国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

探索angularjs+requirejs全面實現按需加載的套路

2019-11-20 10:29:57
字體:
來源:轉載
供稿:網友

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

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

一、一步一步實現
基本思路:1、先開發一個框架頁面,它可以完成一些基本的業務邏輯,并且支持擴展的機制;2、業務邏輯變復雜,需要把部分邏輯拆分到子頁面中,子頁面按需加載;3、子頁面中的展現內容也變了復雜,又需要進行拆分,按需加載;4、子頁面的內容復雜到依賴外部模塊,需要按需加載angular模塊。

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

index.html

<script src="static/js/require.js" defer async data-main="/test/lazyspa/spa-loader.js"></script>

注意:采用手動啟動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已經提供完整的子頁面加載的方法,直接用。
注意必須設置html5Mode,否則url變化以后,routeProvider不截獲。

index.html

<div>  <a href="/test/lazyspa/page1">page1</a>  <a href="/test/lazyspa/page2">page2</a>  <a href="/test/lazyspa/">main</a></div><div ng-view></div>

spa.js

app.config(['$locationProvider', '$routeProvider', function($locationProvider, $routeProvider) {  /* 必須設置生效,否則下面的設置不生效 */  $locationProvider.html5Mode(true);  /* 根據url的變化加載內容 */  $routeProvider.when('/test/lazyspa/page1', {    template: '<div>page1</div>',  }).when('/test/lazyspa/page2', {    template: '<div>page2</div>',  }).otherwise({    template: '<div>main</div>',  });}]);

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

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

spa.js

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

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

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

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

routeProvider中動態加載模塊文件。

template: '<div ng-controller="ctrlModule1"><div>page2</div><div><button ng-click="openDialog()">open dialog</button></div></div>',resolve: {  load: ['$q', function($q) {    var defer = $q.defer();    /* 動態加載angular模塊 */    require(['/test/lazyspa/module1.js'], function(loader) {      loader.onload && loader.onload(function() {        defer.resolve();      });    });    return defer.promise;  }]}

動態加載angular模塊

angular._lazyLoadModule = function(moduleName) {  var m = angular.module(moduleName);  console.log('register module:' + moduleName);  /* 應用的injector,和config中的injector不是同一個,是instanceInject,返回的是通過provider.$get創建的實例 */  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);    }  });  /* 用應用的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('//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css');  /* !!! 動態定義requirejs !!!*/  require.config({    paths: {      'ui-bootstrap-tpls': '//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 = '<div class="modal-header">';      dlg += '<h3 class="modal-title">I/'m a modal!</h3>';      dlg += '</div>';      dlg += '<div class="modal-body">content</div>';      dlg += '<div class="modal-footer">';      dlg += '<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>';      dlg += '<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>';      dlg += '</div>';      $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);    }  };});

二、完整的代碼
index.html

<!DOCTYPE html><html>  <head>    <meta charset="utf-8">    <meta content="width=device-width,user-scalable=no,initial-scale=1.0" name="viewport">    <base href='/'>    <title>SPA</title>  </head>  <body>    <div ng-controller='ctrlMain'>      <div>        <a href="/test/lazyspa/page1">page1</a>        <a href="/test/lazyspa/page2">page2</a>        <a href="/test/lazyspa/">main</a>      </div>      <div ng-view></div>    </div>    <div class="loading"><div class='loading-indicator'><i></i></div></div>    <script src="static/js/require.js" defer async data-main="/test/lazyspa/spa-loader.js?_=3"></script>  </body></html>

spa-loader.js

window.loading = {  finish: function() {    /* 保留個方法做一些加載完成后的處理,我實際的項目中會在這里結束加載動畫 */  },  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);    /* 應用的injector,和config中的injector不是同一個,是instanceInject,返回的是通過provider.$get創建的實例 */    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);      }    });    /* 用應用的injector運行模塊的run */    angular.forEach(m._runBlocks, function(fn) {      $injector.invoke(fn);    });  };  app.config(['$injector', '$locationProvider', '$routeProvider', '$controllerProvider', function($injector, $locationProvider, $routeProvider, $controllerProvider) {    /**     * config中的injector和應用的injector不是同一個,是providerInjector,獲得的是provider,而不是通過provider創建的實例     * 這個injector通過angular無法獲得,所以在執行config的時候把它保存下來    */    app.providers = {      $injector: $injector,      $controllerProvider: $controllerProvider    };    /* 必須設置生效,否則下面的設置不生效 */    $locationProvider.html5Mode(true);    /* 根據url的變化加載內容 */    $routeProvider.when('/test/lazyspa/page1', {      template: '<div>page1</div><div ng-include="/'page1.html/'"></div>',      controller: 'ctrlPage1'    }).when('/test/lazyspa/page2', {      template: '<div ng-controller="ctrlModule1"><div>page2</div><div><button ng-click="openDialog()">open dialog</button></div></div>',      resolve: {        load: ['$q', function($q) {          var defer = $q.defer();          /* 動態加載angular模塊 */          require(['/test/lazyspa/module1.js'], function(loader) {            loader.onload && loader.onload(function() {              defer.resolve();            });          });          return defer.promise;        }]      }    }).otherwise({      template: '<div>main</div>',    });  }]);  app.controller('ctrlMain', ['$scope', '$location', function($scope, $location) {    console.log('main controller');    /* 根據業務邏輯自動到缺省的視圖 */    $location.url('/test/lazyspa/page1');  }]);  app.controller('ctrlPage1', ['$scope', '$templateCache', function($scope, $templateCache) {    /* 用這種方式,ng-include配合,根據業務邏輯動態獲取頁面內容 */    /* 動態的定義controller */    app.providers.$controllerProvider.register('ctrlPage1Dyna', ['$scope', function($scope) {      $scope.openAlert = function() {        alert('page1 alert');      };    }]);    /* 動態定義頁面內容 */    $templateCache.put('page1.html', '<div ng-controller="ctrlPage1Dyna"><button ng-click="openAlert()">alert</button></div>');  }]);  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('//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css');  require.config({    paths: {      'ui-bootstrap-tpls': '//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 = '<div class="modal-header">';      dlg += '<h3 class="modal-title">I/'m a modal!</h3>';      dlg += '</div>';      dlg += '<div class="modal-body">content</div>';      dlg += '<div class="modal-footer">';      dlg += '<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>';      dlg += '<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>';      dlg += '</div>';      $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);    }  };});

以上就是本文的全部內容,希望對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 澄迈县| 扶风县| 新乐市| 山阴县| 泰安市| 阿拉尔市| 留坝县| 尉犁县| 叶城县| 邹平县| 武隆县| 甘南县| 襄樊市| 乐陵市| 阿勒泰市| 梨树县| 高清| 大洼县| 无极县| 商南县| 安陆市| 东丽区| 清远市| 固安县| 鲁甸县| 巴东县| 贵德县| 安吉县| 淮滨县| 玉溪市| 吉林市| 田东县| 安宁市| 防城港市| 大竹县| 定陶县| 醴陵市| 河池市| 东乡族自治县| 铁力市| 鹤山市|