angularjs + ionic 实现项目的按需加载
参考:
ionic入门教程第十六课-在微信中使用ionic的解决方案(按需加载加强版)作者有一系列的ionic教程,很不错!
自己的demo:
angularjs-ionic-demo
demo中的READE.md写的比较混乱,可以忽略不看
ionic自带使用gulp压缩文件可以查看gulpfile.js文件(里面有我添加的基本task,要使用gulp还要安装相应依赖项哦!),至于项目只看www文件就可以了
背景:
一开始使用angularjs+ionic做项目,并没有什么经验,只能是把东西拼凑在一起,能完成任务就行。但是做到后面发现,在首页一进去加载的时候所有的东西都会被一次性加载出来,这就导致加载时间很长。
当项目需求开始趋于稳定的时候,决定重做项目:按需加载页面,样式和js
开始按需加载:
这里就不赘述项目的生成等过程了,简单的使用Ionic Lab就可以创建3种不同的项目
1、在app.js中定义懒加载服务
当然要在首页引入ocLazyLoad.min.js,这一点儿别忘记
var app = angular.module('starter', ['ionic', 'oc.lazyLoad'])
2、设置所有的js文件(控制器,自定义指令,过滤器等)都支持按需加载
.config(['$stateProvider', '$ionicConfigProvider','$urlRouterProvider', '$controllerProvider', '$compileProvider', '$filterProvider', '$provide', '$ocLazyLoadProvider', "$locationProvider", 'JS_REQUIRES', function($stateProvider,$ionicConfigProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide, $ocLazyLoadProvider, $locationProvider, jsRequires) {
// 主要代码(框架自带的懒加载的解决方案)
app.controller = $controllerProvider.register; app.directive = $compileProvider.directive; app.filter = $filterProvider.register; app.factory = $provide.factory; app.service = $provide.service; app.constant = $provide.constant; app.value = $provide.value; $stateProvider ...// 路由等代码 }]);
// JS_REQUIRES是自定义的在配置表里面定义的,算是静态变量
3、配置表文件(实现js和css的按需加载),在项目中我命名为config放在js文件夹下,记得在首页中引用该文件
'use strict'; // 使用$provide.constant来定义了一个静态变量(见app.js中的config的配置) app.constant('JS_REQUIRES', { //*** Scripts scripts: { //*** Controllers 'TestCtrl':"js/controllers/TestCtrl.js", //*** Services 'HttpService':"js/services/HttpService.js" //*** Filter 'SetDataTimeFilter': "js/filter/SetDataTimeFilter.js", //*** Directive 'StarDirective': "js/directive/Star.js", //*** 第三方 // 复制文本到剪贴板 'clipboard': "js/other/clipboard.min.js", }, CssArg:{ TestStyle: 'css/test.css', },
// 把业务员相关联的文件按照界面写成分组,这样在代码中就不用把太多的精力放在文件关联上 ViewArgs: { TestArgs: ['TestCtrl', 'HttpService', 'SetDataTimeFilter', 'TestStyle'] } });
4、拆分js文件
在www/js下新建controllers(控制器)、services(服务)、directive(指令)、filter(过滤器)等文件
以TestCtrl文件(controller)为例:
‘use strict’; // 使用严格模式 app.controller('TestCtrl', ["$scope", function(){ ... }]); // 这里使用的app.controller其实是使用了$controllerProvider.register
相应的:
services文件使用app.factory
directive文件使用app.directive
filter文件使用app.filter
4、使用oclazyload和$q的组合实现动态加载文件
.config(['$stateProvider', '$ionicConfigProvider','$urlRouterProvider', '$controllerProvider', '$compileProvider', '$filterProvider', '$provide', '$ocLazyLoadProvider', "$locationProvider", 'JS_REQUIRES', function($stateProvider,$ionicConfigProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide, $ocLazyLoadProvider, $locationProvider, jsRequires) { //第一步的配置代码
//配置路由 $stateProvider .state('test', { url: '/test', templateUrl: 'templates/test.html', prefetchTemplate:false, // 不会提前加载html(实现html页面的按需加载) controller: 'TestCtrl', resolve: loadSequence('TestArgs') /* 只关联少量文件时,可以写上所有的文件【loadSequence('ChatsCtrl','ChatsService')】; 或者文件比较多的时候,写上关联的模块名称 */ $urlRouterProvider.otherwise('/home'); function loadSequence() { var _args = arguments; // 传不传参数都能匹配到 var viewArgs = repeatArgs(_args[0]); //先匹配模块的,没有再匹配单文件的 if(viewArgs){ // 先匹配模块,找到就把模块对应的单文件列表返回给它,找不到就当做是单文件列表 _args = viewArgs }else{ //console.log("没有找到模块?") } function repeatArgs(name){ return jsRequires.ViewArgs[name]; } return { // 使用oclazyload和$q的组合实现懒加载 deps: ['$ocLazyLoad', '$q', function ($ocLL, $q) { var promise = $q.when(1); for (var i = 0, len = _args.length; i < len; i++) { promise = promiseThen(_args[i]); } return promise; function promiseThen(_arg) { if (typeof _arg == 'function') return promise.then(_arg); else return promise.then(function () { var nowLoad = requiredData(_arg); if (!nowLoad) return console.log('找不到文件 [' + _arg + ']'); return $ocLL.load(nowLoad); }); } function requiredData(name) { if (jsRequires.modules) for (var m in jsRequires.modules) if (jsRequires.modules[m].name && jsRequires.modules[m].name === name) return jsRequires.modules[m]; if(jsRequires.scripts && jsRequires.scripts[name]){ return jsRequires.scripts[name]; }else if(jsRequires.CssArg && jsRequires.CssArg[name]){ return jsRequires.CssArg[name]; } // return jsRequires.scripts && jsRequires.scripts[name]; } }]};} })
到此,我们想要的基本实现了。首次加载的时候只加载需要的文件,切换页面再加载其他文件。后面自行添加具体的功能就可以了
5、压缩
可以使用gulp压缩文件到一个单独的文件夹,记得把lib和index.html文件放进去。直接把访问地址中的www改成相应的名称就可以了。
前端构建工具gulpjs的使用介绍及技巧
了解angularjs,主要针对上面的第二步进行讲解,如果对代码没有问题可忽略
1、模块
angular模块通过angular.module(name, requires, configFn)方法生成:
- 参数name是模块名称;
- 参数requires标识依赖模块数组。如果不设置requires参数,调用angular.module(name)方法表示获取这个模块;因此,如果确定新模块没有依赖关系,必须设置requires为空数组[];
- 参数configFn是方法或数组,负责在模块初始化时做一些配置,如果是数组,最后一个元素必须是方法
已经初始化的angular模块保存在一个叫modules的缓存对象中,key是模块名,value是模块对象。所以,定义一个同名的模块,等于覆盖之前的模块
2、服务注入
angular模块只保留服务的定义。
服务提供商,在Angular中称为Provider,几乎所有的服务(除了$injector)都是由服务提供商供应。无论是服务还是服务提供商,他们在Angular中都是唯一的,服务和服务提供商是一个一对一的关系。
所以整个过程:
- 模块定义服务、服务提供商;
- 注入器根据模块依赖关系加载模块,实例化所有服务提供商;
- 应用需要服务,注入器根据服务名寻找服务提供商,服务提供商实例化服务;
每个angular模块内置有三个数组:
- invokeQueue保存如何注入服务提供商和值的信息;
- configBlocks保存模块的配置信息;
- runBlocks保存这个模块的执行信息
模块被使用的时候,注入器根据invokeQueue中的信息,实例化服务提供商;根据configBlocks中的信息对服务提供商做一些额外的处理;根据runBlocks中提供的信息,调用前面的服务提供商提供的服务执行模块需要完成的工作。
angular模块提供了很多方法来填充这三个数组,比如config()、run()等。
例如:添加一个Controller:
angular.module('ngAppDemo',[])
.controller('ngAppDemoController',function($scope) {
$scope.a= 1;
$scope.b = 2;
});
这段代码等于:
invokeQueue.push(['$controllerProvider','register', ['ngAppDemoController', function(){}]]);
注入器根据这个信息,就会调用$controllerProvider的register方法注册一个ngAppDemoController.
同理:
constant()
给默认的$provider注册一个常量:
原理:在invokeQueue首部插入['$provide', 'constant', arguments]。
controller()
在$controllerProvider中注册一个控制器:
原理:在invokeQueue尾部插入['$controllerProvider', 'register', arguments]。
directive()
在$compileProvider中注册一个指令:
原理:在invokeQueue尾部插入['$compileProvider', 'directive', arguments]
factory()
生成一个服务工厂(隐式创建一个了服务提供商):
原理:在invokeQueue尾部插入['$provide', 'factory', arguments]
filter()
在$filterProvider中注册一个过滤器:
原理:在invokeQueue尾部插入['$filterProvider', 'register', arguments]
service()
注册一个服务(隐式创建了一个服务提供商):
原理:在invokeQueue尾部插入['$provide', 'service', arguments]
value()
注册一个变量(隐式创建了一个服务提供商):
原理:在invokeQueue尾部插入['$provide', 'value', arguments]