JavaScript   发布时间:2022-04-16  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了探索angularjs+requirejs全面实现按需加载的套路大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

在进行有一定规模的项目时,通常希望实现以下目标:1、支持复杂的页面逻辑(根据业务规则动态展现内容,例如:权限,数据状态等);2、坚持前后端分离的基本原则(不分离的时候,可以在后端用模版引擎直接生成好页面);3、页面加载时间短(业务逻辑复杂就需要引用第三方的库,但很可能加载的库和用户本次操作没关系);4,还要代码好维护(加入新的逻辑时,影响的文件尽量少)。

想同时实现这些目标,就必须有一套按需加载的机制,页面上展现的内容和所有需要依赖的文件,都可以根据业务逻辑需要按需加载。最近都是基于angularjs做开发,所以本文主要围绕angularjs提供的各种机制,探索全面实现按需加载的套路。

一、一步一步实现

基本思路:1、先开发一个框架页面,它可以完成一些基本的业务逻辑,并且支持扩展的机制;2、业务逻辑变复杂,需要把部分逻辑拆分到子页面中,子页面按需加载;3、子页面中的展现内容也变了复杂,又需要进行拆分,按需加载;4、子页面的内容复杂到依赖外部模块,需要按需加载angular模块。

1、框架页 提到前端的按需加载,就会想到AMD( Asynchronous Module Definition),现在用requirejs的非常多,所以首先虑引入requires。

index.html

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

注意:采用手动启动angular的方式,因此html中没有ng-app。

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

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

spa.js

page1
',}).when('/test/lazyspa/page2',{ template: '
page2
',}).otherwise({ template: '
main
',}); }]);

3、按需加载子页面中的内容 用routeProvider的前提是url要发生变化,但是有的时候只是子页面中的局部要发生变化。如果这些变化主要是和绑定的数据相关,不影响页面布局,或者影响很小,那么通过ng-if一类的标签基本就解决了。但是有的时候要根据页面状态,完全改变局部的内容,例如:用户登录前和登录后局部要发生的变化等,这就意味着局部的布局可能也挺复杂,需要作为独立的单元来对待。

利用ng-include可以解决页面局部内容加载的问题。但是,我们可以再考虑更复杂一些的情况。这个页面片段对应的代码是后端动态生成的,而且不仅仅有html还有js,js中定义了代码片段对应的controller。这种情况下,不仅仅要考虑动态加载html的问题,还要考虑动态定义controller的问题。controller是通过angular的controllerProvider的register方法注册,因此需要获得controllerProvider的实例。

spa.js

page1
',controller: 'ctrlPage1' }).when('/test/lazyspa/page2',}); app.controller('ctrlPage1',['$scope','$templateCache',function($scope,$templateCache) { /* 用这种方式,ng-include配合,根据业务逻辑动态获取页面内容 */ /* !!!动态的定义controller!!! */ app.providers.$controllerProvider.register('ctrlPage1Dyna',function($scope) { $scope.openAlert = function() { alert('page1 alert'); }; }]); /* !!!动态定义页面的内容!!! */ $templateCache.put('page1.html','
'); }]); }]);

4、动态加载模块

采用上面子页面片段的加载方式存在一个局限,就是各种逻辑(js)要加入到启动模块中,这样还是限制子页面片段的独立封装。特别是,如果子页面片段需要使用第三方模块,且这个模块在启动模块中没有事先加载时,就没有办法了。所以,必须要能够实现模块的动态加载。实现模块的动态加载就是把angular启动过程中加载模块的方式提取出来,再处理一些特殊情况。

但是,实际跑起来发现文章中的代码有问题,就是“$injector”到底是什么?研究了angular的源代码injector.js才大概搞明白是怎么回事。

一个应用有两个$injector,providerInjector和instanceInjector。invokeQueue和用providerInjector,runBlocks用instanceProvider。如果$injector用错了,就会找到需要的服务。

routeProvider中动态加载模块文件。

page2
',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模块

定义模块

module1.js

'; dlg += '
'; dlg += '

spa-loader.js

{ finish: function() { /* 保留个方法做一些加载完成后的处理,我实际的项目中会在这里结束加载动画 */ },load: function() { require.config({ paths: { "domReady": '/static/js/domReady',shim: { "angular": { exports: "angular" },"angular-route": { deps: ["angular"] },urlArgs: "bust=" + (new Date()).getTime() }); } }; window.loading.load();

spa.js

Strict'; define(["require",['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',function($injector,$LOCATIOnProvider,$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: '
page1
',controller: 'ctrlPage1' }).when('/test/lazyspa/page2',{ template: '
page2
',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: '@H_26_33@main
',}); }]); app.controller('ctrlMain','$LOCATIOn',$LOCATIOn) { console.log('main controller'); /* 根据业务逻辑自动到缺省的视图 */ $LOCATIOn.url('/test/lazyspa/page1'); }]); app.controller('ctrlPage1',$templateCachE) { /* 用这种方式,ng-include配合,根据业务逻辑动态获取页面内容 */ /* 动态的定义controller */ app.providers.$controllerProvider.register('ctrlPage1Dyna',function($scopE) { $scope.openAlert = function() { alert('page1 alert'); }; }]); /* 动态定义页面内容 */ $templateCache.put('page1.html','
'); }]); require(['domReady!'],["app"]); }); });
@H_714_0@module1.js

Strict'; define(["angular"],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'],$uibModal) { console.log('module1 - ctrl begin'); var dlg = ''; dlg += '
标签:angularjsrequirejs按需加载
猜你在找的JavaScript相关文章
其他相关热搜词更多
phpJavaPython程序员load如何string使用参数jquery开发安装listlinuxiosandroid工具javascriptcap