【精选】ui router & ocLazyLoad & angularjs 实现路由组件异步加载 ( 切实能用 )

35 阅读3分钟

ui router 实现路由组件异步加载/controller依赖注入/首屏优化

适合人群:

  • 对ui router毫无基础但是希望实现组件异步加载 / 首屏加载优化的懵逼人群
  • 对ui router有点基础但是实现过程中遇到了问题的懵逼人群

本文优点:保姆式指导,百分百还原项目环境,照着做就行了,脑子不重要,看懂关注,么么哒。

技术栈: angular 2 / ui router

一 为什么要这么做 / 确定方案

近日对公司项目(单页面应用)进行优化,通过对页面开启时,开发者调试工具中展示时间消耗进程的判断,对js文件的加载占用了大部分时间,经过查询,该项目的js文件(大部分为装载着js的文件),是从index.html中一次性引入的。

在这里插入图片描述
第一个优化方案浮出脑海:路由懒加载。

这对我来说是个挑战,因为我对angular2与ui router都一知半解。

二 了解我的项目 / 方案选型

首先观察控制器与路由之间的关系,通过对js控制器文件群的观察,项目中共有N个控制器文件(示例如下),都附给同一个模块名称,路由中指定本次路由的html与控制器名称即可。

var app=angular.module("myApp",[]);

app.controller('myCtrl', function ($scope) {  
    $scope.name = "summer";  
});  
 
app.controller('myCtrl2', function ($scope) {  
    $scope.name="zeoy";  
}); 

在整个项目的路由分配中中,module名称始终不变(在index.html中指定),变的是控制器名。

 <html   ng-app="myApp"> 

现任务是不在index.html中一次性加载大量的控制器,而在路由中,分批次的在需要的时候注入控制器。

首先找到路由文件,一般为名含有router的js文件,从文件中判断,本项目使用的是ui router。

首先想到的是常用的import,但是在此报环境错误。

import is not defind

经查询为环境兼容问题,此方式放弃。

最终决定使用oclazyload来引入控制器文件。

三 只需要做三步 / 方案实现

3.1 引入ocLazyLoad.js

在index.html文件中引用ocLazyLoad.js。

ocLazyLoad.js一定要在angular与angular-router后引入。

<script src="lib/angular-1.3.0.js"></script>
<script src="lib/angular-ui-router.js"></script>
<script src="lib/ocLazyLoad.min.js"></script>

在路由配置文件中(router.js),路由控制模块引入oclazyLoad。

angular.module('myApp',['ui.router','oc.lazyLoad'])
.config(function ($stateProvider, $urlRouterProvider) {
		...
	    $stateProvider
        .state('a',{
            url : '/a',
            controller : 'ctrla',
            templateUrl : './tpl/pagea.html',
        })
        .state('b',{
            url : '/b',
            templateUrl : './tpl/pageb.html',
            controller : 'ctrlb',
        })
        ...
}

接下来要实现两个功能,在路由转换后,控制器加载前引入控制器文件,并将其注册到主模块的控制器列表中。

3.2 设置异步路由

在这一步我们在需要异步加载的路由中更改配置。

// 更改前
.state('index',{
	url: '',
	templateUrl: 'views/login/login.html',
	controller: 'loginCtrl'
})
// 更改后 
.state('index',{
	url: '',
	templateUrl: 'views/login/login.html',
	controller: 'loginCtrl',
	resolve: {
		loadMyCtrl: ['$ocLazyLoad', function($ocLazyLoad){
			return $ocLazyLoad.load('views/login/loginCtrl.js')
		}]
	}
})

如上文,这一步需要在路由文件中的对应路由配置中添加resolve,resolve是一个对象,它会在控制器加载前执行,所以我们可以在这里获取控制器。

这时执行项目,跳转路由,可以看到ocLazyLoad成功的获取了文件。

img

这时浏览器会报错,因为我们还需要第三步。

3.3 将异步加载的控制器注册到主模块中

这里涉及到一个隐蔽的知识点,我们此刻处于路由控制模块,需要获得主模块,更改主模块配置,以成功注入动态控制器。

如下两行代码的区别在于第一则存在requires参数数组,第二则不包含此参数

如果你不指定requires参数,那么不会创建一个Module对象,而是返回已经创建的实例。

我们以此来获得主模块。

var myModule2 = angular.module('myModule',[]);
var myModule2 = angular.module('myModule');

首先我们先找到主模块,也就是控制器绑定的模块,可以通过进入控制器文件,查看控制器对应的模块名称。

然后我们在路由文件中,.config的function参数中添加$controllerProvider,在方法开头,将主模块与本地控制器注册表绑定。

... ...
.config(function(... ... , $controllerProvider){
	var app  = angular.module('myModule');
	app.controller = $controllerProvider.register;
	... ...
})

如上,项目可以正常运行了。

四 过程中的错误

4.1 Error: [ng:areq]
Error: [ng:areq] http://errors.angularjs.org/1.4.8/ng/areq?p0=HelloCtrl&p1=not%20a%20function%2C%20got%20undefined

控制器未正确引入造成,见于未做上述第三步导致,异步加载的文件未与主模块绑定,导致控制器文件虽导入成功但不能正常使用该控制器。

参考文献

AngularJS-控制器篇(controller)

AngularJS中的模块和依赖注入