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

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

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

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

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

index.html

<script src="https://www.jb51.net/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": "//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(); }); });

2、按需加载子页面
angular的routeProvider+ng-view已经提供完整的子页面加载的方法,直接用。
注意必须设置html5Mode,否则url变化以后,routeProvider不截获。

index.html

<div> <a href="https://www.jb51.net/test/lazyspa/page1">page1</a> <a href="https://www.jb51.net/test/lazyspa/page2">page2</a> <a href="https://www.jb51.net/test/lazyspa/">main</a> </div> <div ng-view></div>

spa.js

app.config(['$locationProvider', '$routeProvider', function($locationProvider, $routeProvider) { /* 必须设置生效,否则下面的设置不生效 */ $locationProvider.html5Mode(true); /* 根据url的变化加载内容 */ $routeProvider.when('https://www.jb51.net/test/lazyspa/page1', { template: '<div>page1</div>', }).when('https://www.jb51.net/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('https://www.jb51.net/test/lazyspa/page1', { /*!!!页面中引入动态内容!!!*/ template: '<div>page1</div><div ng-include="\'page1.html\'"></div>', controller: 'ctrlPage1' }).when('https://www.jb51.net/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>'); }]); }]);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgysgp.html