探索angularjs+requirejs全面实现按需加载的套路

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

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

一、一步一步实现

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

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

index.html

rush:js;">

spa-loader.js

rush:js;"> window.loading = { 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

rush:js;"> 'use 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: '
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"]); }); });

module1.js

rush:js;"> 'use 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 += '

相关文章

什么是深拷贝与浅拷贝?深拷贝与浅拷贝是js中处理对象或数据...
前言 今天复习了一些前端算法题,写到一两道比较有意思的题:...
最近在看回JavaScript的面试题,this 指向问题是入坑前端必须...
js如何实现弹出form提交表单?(图文+视频)
js怎么获取复选框选中的值
js如何实现倒计时跳转页面