Rumah >hujung hadapan web >tutorial js >Terokai angularjs+requirejs untuk melaksanakan sepenuhnya rutin memuatkan atas permintaan_AngularJS
Apabila mengerjakan projek dalam skala tertentu, anda biasanya ingin mencapai matlamat berikut: 1. Menyokong logik halaman yang kompleks (paparan kandungan dinamik berdasarkan peraturan perniagaan, seperti kebenaran, status data, dll.); . Mematuhi prinsip asas pemisahan bahagian depan dan belakang Prinsip (apabila tidak dipisahkan, anda boleh menggunakan enjin templat untuk terus menjana halaman yang baik pada bahagian belakang 3. Masa memuatkan halaman adalah singkat (jika logik perniagaan adalah kompleks , anda perlu merujuk perpustakaan pihak ketiga, tetapi kemungkinan besar perpustakaan yang dimuatkan tidak ada kaitan dengan operasi semasa pengguna) 4. Kod mesti mudah untuk dikekalkan (apabila menambah logik baru, menjejaskan sedikit sahaja fail yang mungkin).
Untuk mencapai matlamat ini pada masa yang sama, mesti ada mekanisme pemuatan atas permintaan Kandungan yang dipaparkan pada halaman dan semua fail bergantung boleh dimuatkan atas permintaan mengikut keperluan logik perniagaan. Baru-baru ini, semua pembangunan adalah berdasarkan angularjs, jadi artikel ini tertumpu terutamanya pada pelbagai mekanisme yang disediakan oleh angularjs untuk meneroka cara untuk melaksanakan pemuatan atas permintaan sepenuhnya.
1. Pelaksanaan langkah demi langkah
Idea asas: 1. Mula-mula bangunkan halaman rangka kerja, yang boleh melengkapkan beberapa logik perniagaan asas dan mekanisme pengembangan sokongan 2. Logik perniagaan menjadi rumit , dan beberapa logik perlu dibahagikan kepada sub-halaman, yang dimuatkan atas permintaan 3. Kandungan paparan dalam sub-halaman juga telah menjadi kompleks, dan perlu dipecah dan dimuatkan atas permintaan; sub-halaman adalah kompleks Untuk bergantung pada modul luaran, anda perlu memuatkan modul sudut atas permintaan.
1. Halaman Bingkai
Apabila bercakap tentang pemuatan atas permintaan pada bahagian hadapan, AMD (Definisi Modul Asynchronous) terlintas di fikiran pada masa kini. banyak requirejs digunakan. Jadi pertimbangkan dahulu untuk memperkenalkan keperluan.
index.html
<script src="static/js/require.js" defer async data-main="/test/lazyspa/spa-loader.js"></script>
Nota: Sudut dimulakan secara manual, jadi tiada ng-app dalam html.
spa-loader.js
require.config({ paths: { "domReady": '/static/js/domReady', "angular": "//cdn.bootcss.com/angular.js/1.4.8/angular.min", "angular-route": "//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(); }); });
Muatkan subhalaman atas permintaan
Angular's routeProvider+ng-view sudah menyediakan kaedah pemuatan sub-halaman yang lengkap, yang boleh digunakan secara langsung.
Perhatikan bahawa html5Mode mesti ditetapkan, jika tidak, routeProvider tidak akan memintas url selepas ia berubah.
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 Muatkan kandungan dalam sub-halaman atas permintaan
digunakan Premis routeProvider ialah URL perlu diubah, tetapi kadangkala hanya sebahagian daripada subhalaman yang perlu diubah. Jika perubahan ini terutamanya berkaitan dengan data terikat dan tidak menjejaskan reka letak halaman, atau kesannya sangat kecil, maka teg seperti ng-if pada asasnya boleh menyelesaikan masalah. Walau bagaimanapun, kadangkala kandungan tempatan perlu diubah sepenuhnya berdasarkan status halaman, seperti perubahan setempat sebelum dan selepas pengguna log masuk, dsb. Ini bermakna susun atur setempat mungkin agak rumit dan perlu dianggap sebagai unit bebas .
Gunakan ng-include untuk menyelesaikan masalah memuatkan sebahagian kandungan pada halaman. Walau bagaimanapun, kita boleh mempertimbangkan situasi yang lebih kompleks. Kod yang sepadan dengan serpihan halaman ini dijana secara dinamik oleh bahagian belakang, dan ia bukan sahaja html tetapi juga js Pengawal yang sepadan dengan serpihan kod ditakrifkan dalam js. Dalam kes ini, bukan sahaja isu memuatkan HTML secara dinamik mesti dipertimbangkan, tetapi juga isu mentakrifkan pengawal secara dinamik mesti dipertimbangkan. Pengawal didaftarkan melalui kaedah daftar controllerProvider sudut, jadi adalah perlu untuk mendapatkan contoh 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. Modul pemuatan dinamik
menggunakan sub-halaman di atas serpihan Terdapat had dalam kaedah pemuatan, iaitu, pelbagai logik (js) mesti ditambah pada modul permulaan, yang masih mengehadkan enkapsulasi bebas serpihan sub-halaman. Khususnya, jika serpihan subhalaman memerlukan penggunaan modul pihak ketiga, dan modul ini tidak dimuatkan terlebih dahulu dalam modul permulaan, tiada penyelesaian. Oleh itu, adalah perlu untuk dapat memuatkan modul secara dinamik. Untuk melaksanakan pemuatan dinamik modul adalah dengan mengekstrak kaedah memuatkan modul semasa permulaan Sudut dan kemudian mengendalikan beberapa situasi khas.
Namun, apabila saya benar-benar menjalankannya, saya mendapati terdapat masalah dengan kod dalam artikel itu. Apakah sebenarnya "$injector"? Selepas mengkaji kod sumber penyuntik sudut.js, saya kira-kira apa yang sedang berlaku.
Sesuatu aplikasi mempunyai dua $injector, providerInjector dan instanceInjector. invokeQueue menggunakan providerInjector, dan runBlocks menggunakan instanceProvider. Jika $injector digunakan secara tidak betul, perkhidmatan yang diperlukan akan ditemui.
Pemuatan dinamik fail modul dalam 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; }] }
Pemuatan dinamik modul sudut
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); }); };
Tentukan modul
modul1.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); } }; });
2. Lengkapkan kod
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": "//cdn.bootcss.com/angular.js/1.4.8/angular.min", "angular-route": "//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); } }; });
Di atas ialah kandungan meneroka angularjs+requirejs untuk melaksanakan sepenuhnya rutin pemuatan atas permintaan_AngularJS, dan lagi Untuk kandungan yang berkaitan, sila perhatikan laman web PHP Cina (www.php.cn)!