app.controller('appController',[ '$state','$timeout','enumService','userService','utilityService',appController ]); function appController( $scope,$state,$timeout,enumService,userService,utilityService ) { ... }
我想开始做的是使用require.js来处理控制器的延迟加载.我知道我应该使用这样的东西:
require(["app"],function (app) { app.controller('appController',function appController( $scope,utilityService ) { ... }); });
有人可以向我解释一下app.controller如何获得对服务的引用?我需要在require.js方面做任何其他事情吗?我正在跟踪我正在编写appController的方式吗?
懒惰加载$注射器
angular-requirejs-seed项目说明如何通过设置如下这样的懒惰功能轻松实现延迟加载:
define([],function() { return ['$scope','$http','myInjectable',function($scope,$http,myInjectable) { $scope.welcomeMessage = 'hey this is myctrl2.js!'; // because this has happened async,we've missed the digest cycle $scope.$apply(); }]; });
…然后实例化控制器,如下所示:
.controller('MyCtrl2',['$scope','$injector',$injector) { require(['controllers/myctrl2'],function(myctrl2) { $injector.invoke(myctrl2,this,{'$scope': $scope}); }); ...
请注意,延迟加载的函数不是控制器.这只是一个使用$inject调用的函数,它允许它访问实际控制器的$范围,并允许它访问在你的应用程序中加载的任何注入.
您可以将相同的技术应用于服务,工厂或指令.
懒惰加载警告
在大多数情况下,延迟加载可能是自毁的.如果您的目标是为用户提供一个快速的网站,那么懒惰加载每个控制器是一个坏主意.一旦建立了HTTP连接,大多数互联网连接允许大量数据在短时间内流过电线.延迟,但是可以是真正的杀手.这就是为什么大多数网站这些天使用连接和缩小来打包他们的JavaScript并减少网络请求的数量,而不是依赖于延迟请求数量的延迟加载.
考虑你的应用程序的架构.您将创建多少个可重复使用的指令?在各种应用程序之间共享多少代码,不适合延迟加载?对于许多应用程序,大部分代码将由常见的组件组成,使得延迟加载毫无意义.
延迟加载在应用程序中是非常明确和分开的部分.那些非常独特和独立的片段可以被认为是单独的应用程序.然而,即使在这种情况下,您可能会考虑实际创建单独的应用程序,而不是组合它们
BTW,require.js is still useful even if you’re not Lazy-loading
Even if you’re not lazy-loading,require.js is extremely useful for
dependency management. Used in conjunction with the require.js
optimizer it’s an elegant way to keep track of dependencies and
compress+minify your app.You can also use require.js to load dependencies for running Jasmine
unit tests which helps keep your components modular,and speeds up
your tests by just loading the dependencies that you need. For unit
testing,I create a separate main-test.js file which calls
require.config(...)
to load app dependencies as well as
testing-specific dependencies.
延迟装载架构
具有角度的惰性载荷相当复杂,因为角度不是设计用于支撑惰性载荷.在本节中,我将尝试引导您探索如何强制角度来支持延迟加载.这不是一个完整的解决方案,但我希望介绍在构建这样的应用程序时要了解的重要概念.
我们从路由器开始,而不是我在第一部分提出的angular-requirejs-seed,它实际上比延迟加载在你的应用程序的路由器中更有意义.使用ui-router,我们可以这样实现lazy-load:
... app.$controllerProvider = $controllerProvider; var lazyPartialDeferred; $stateProvider ... .state('lazy',{ url: "/lazy",templateProvider: function() { return lazyPartialDeferred.promise; },controller: 'lazyCtrl',resolve: { load: function($q,$templateCache) { var lazyCtrlDeferred = $q.defer(); lazyPartialDeferred = $q.defer(); require(['lazy'],function (lazy) { lazyCtrlDeferred.resolve(); lazyPartialDeferred.resolve($templateCache.get('lazy.html')); }); return lazyCtrlDeferred.promise; } } }); ...
我们在这里做的是推迟部分(lazy.html)和控制器(lazyCtrl)的实例化,直到我们的requirejs模块(lazy.js)被加载为止.另外,请注意,我们直接从$templateCache加载我们的view,partial,lazy.html.也就是说,当我们加载lazy.js时,部分本身就包含在lazy.js中.理论上,我们可以从lazy.js中单独加载lazy.html,但为了获得最佳性能,我们应该将部分文件编译成我们的js文件.
我们来看看lazy.js:
define(['angular','lazy-partials'],function (angular) { var app = angular.module('app'); var lazyCtrl = ['$scope','$compile','$templateCache',function ($scope,$compile,$templateCache) { $scope.data = 'my data'; }]; app.$controllerProvider.register('lazyCtrl',lazyCtrl); });
请记住,上述代码代表未编译的lazy.js.在生产中,lazy-partials.js(在上面的第一行引用)实际上将被编译成同一个文件.
现在我们来看看lazy-partials.js:
// Imagine that this file was actually compiled with something like grunt-html2js // So,what you actually started with was a bunch of .html files which were compiled into this one .js file... define(['angular'],function (angular) { var $injector = angular.element(document).injector(),$templateCache = $injector.get('$templateCache'); $templateCache.put('lazy.html','<p>This is lazy content! and <strong>{{data}}</strong> <a href="#">go back</a></p>'); });
再次,上述代码并不完全是这样的文件真正的样子. lazy-partials.js实际上将使用像grunt-html2js这样的构建工具插件从您的html文件自动生成.
现在,您可以在理论上使用迄今提出的方法构建整个应用程序.但是,这有点… janky.我们更喜欢在lazy.js中实例化一个新的模块,例如appLazy = angular.module(‘app.lazy’),然后实例化我们的控制器,指令,服务等,如appLazy.directive(…) .
然而,我们不能这样做的原因是因为所有这些东西都是在已经在lazy.js加载的时候已被调用的angular.bootstrap方法中初始化(并且提供给我们的应用程序).而且我们不能再重新调用angular.bootstrap(…).
BTW,internally angular is doing this to bootstrap modules:
06005
createInjector
is an internal function to angular that loops through
all of the modules and registers all of their varIoUs building blocks.In lazy.js,we called
$controllerProvider.register(..)
to
lazily register our controller.createInjector
also triggers a
call to the same function when the app is bootstrapped. Here is a list
of varIoUs angular building blocks and the way in which they are
registered by angular:06006
那么,有没有办法懒散地实例化模块?是的,you can register a module and it’s sub-modules by iterating through various nested properties of the module object (requires
and _invokeQueue
),这个操作在一个名为ocLazyLoad的lib中被简化了.
本节中提供的大部分代码可在this plunker.
(灵感来源未提及:Couch Potato,AngularAMD)
完整的懒惰加载解决方案:
[ocLazyLoad ui-router requirejs] – plunk
因为ui路由器允许我们延迟加载模板和控制器,所以我们可以使用它与ocLazyLoad一起在路由更改之间即时加载模块.这个例子构建了上一节的原理,但是通过使用ocLazyLoad,我们有一个解决方案,可以使我们的延迟加载模块的结构与非惰性加载的模块相同.
这里的关键是我们的app.config(..)块:
app.config(function($stateProvider,$locationProvider,$ocLazyLoadProvider) { var lazyDeferred; $ocLazyLoadProvider.config({ loadedModules: ['app'],asyncLoader: require }); $stateProvider ... .state('lazy',{ url: "/lazy",templateProvider: function() { return lazyDeferred.promise; },resolve: { load: function($templateCache,$ocLazyLoad,$q) { lazyDeferred = $q.defer(); return $ocLazyLoad.load({ name: 'app.lazy',files: ['lazy'] }).then(function() { lazyDeferred.resolve($templateCache.get('lazy.html')); }); } } }); ...
lazy.js现在看起来像这样:
define(['angular',function (angular) { var appLazy = angular.module('app.lazy',['app.lazy.partials']); appLazy.controller('lazyCtrl',$templateCache) { $scope.data = 'my data'; }); });
请注意,这个文件在延迟加载方面已经不复存在了.您可以轻松地以非懒惰的方式加载该文件,并不会知道这个区别.同样的原则适用于lazy-partials.js:
// Imagine that this file was actually compiled with something like grunt-html2js // So,function (angular) { angular.module('app.lazy.partials',[]) .run(function($templateCache) { $templateCache.put('lazy.html','<p>This is lazy content! and <strong>{{data}}</strong> <a href="#">go back</a></p>'); }); });
>>> check out the fully-functioning plunk< 部署 当涉及到部署,这个难题的最后一块是使用requirejs优化器来连接和最小化我们的js文件.理想情况下,我们希望优化器跳过已经包含在主应用程序中的依赖关系的连接(即:普通文件).为了完成这个,see this repo和build.js file.
一个更优雅的解决方案
我们可以通过为a very elegant solution添加一个ui路由器装饰器来改善以前的繁琐.