我的AngularJS应用程序中有控制器,目前编码如下:
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,
$state,
$timeout,
enumService,
userService,
utilityService
) {
...
});
});
有人可以向我解释app.controller如何获得对服务的引用?我还需要在require.js端做任何其他事情吗?我是否按照我编写appController的方式走上正轨?
答案 0 :(得分:28)
<强> TL;博士;最终解决方案在最后一部分,或者只看this plunk
$injector
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', function($scope, $injector) {
require(['controllers/myctrl2'], function(myctrl2) {
$injector.invoke(myctrl2, this, {'$scope': $scope});
});
...
请注意,延迟加载的函数不是控制器。它只是一个使用$injector
调用的函数,可以访问实际控制器的$scope
和this
,并允许它访问任何。
您可以将同样的技术应用于服务,工厂或指令。
在大多数情况下,延迟加载可能是弄巧成拙的。如果你的目标是给你的用户一个活泼的网站,那么懒洋洋地加载每个控制器是一个坏主意。建立HTTP连接后,大多数互联网连接允许大量数据在短时间内通过线路流动。然而,延迟可能是真正的杀手。这就是为什么现在大多数网站使用连接和缩小来打包他们的javascript并减少网络请求的数量,而不是依赖延迟加载来增加请求的数量。
考虑您的应用的架构。您将创建多少个可重复使用的指令?应用程序的各个部分之间将共享多少代码,不适合延迟加载?对于许多应用程序,大部分代码都将由常见组件组成,使得延迟加载毫无意义。
延迟加载在具有非常独特且独立的部分的应用程序中是有意义的。如此独特且独立的碎片可被视为单独的应用程序。但是,即使在这种情况下,您也可以考虑实际创建单独的应用程序而不是组合它们。
,即使你不是懒惰加载,require.js仍然有用 即使您没有延迟加载, require.js也非常有用 依赖管理。与require.js一起使用 优化器它是一种跟踪依赖关系的优雅方式 压缩+缩小你的应用程序。
您还可以使用require.js加载运行Jasmine的依赖项 单元测试有助于保持组件模块化,并加快速度 只需加载所需的依赖项即可进行测试。对于单位 测试时,我创建了一个单独的 main-test.js 文件来调用
require.config(...)
加载应用依赖项以及 特定于测试的依赖项。
带角度的延迟加载相当复杂,因为角度不是为了支持延迟加载而设计的。在本节中,我将尝试带您探索如何强制角度来支持延迟加载。这不是一个成熟的解决方案,但我希望提出在构建此类应用程序时理解的重要概念。
让我们从路由器开始,而不是我在第一部分中提到的 angular-requirejs-seed ,它实际上对延迟加载更有意义。您的应用程序的路由器。使用 ui-router ,我们可以通过这种方式实现延迟加载:
...
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 加载视图partial, lazy.html 。也就是说,当我们加载lazy.js时,部分本身包含在lazy.js中。从理论上讲,我们可以将lazy.html与lazy.js分开加载,但为了获得最佳性能,我们应该将partials编译成我们的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文件自动生成的。
现在,您可以使用迄今为止提供的方法构建整个应用程序。但是,它有点......笨拙。我们宁愿在lazy.js中做的是实例化一个新模块,比如appLazy = angular.module('app.lazy')
,然后实例化我们的控制器,指令,服务等,如appLazy.directive(...)
。
然而,我们无法做到这一点的原因是因为所有这些内容都已在angular.bootstrap
方法中初始化(并可供我们的应用使用),该方法已经被时间调用lazy.js 已加载。我们无法再次致电angular.bootstrap(...)
。
,内部角度正在执行此操作以引导模块: var injector = createInjector(modules, config.strictDi);
createInjector
是角度循环的内部函数 所有模块和寄存器都是各种构建块。在 lazy.js 中,我们将
$controllerProvider.register(..)
称为 懒洋洋地注册我们的控制器。createInjector
也触发了一个 在应用程序引导时调用相同的函数。这是一个清单 各种角度构建块及其形式 由angular注册:provider: $provide.provider factory: $provide.factory service: $provide.service value: $provide.value constant: $provide.constant.unshift animation: $animateProvider.register filter: $filterProvider.register controller: $controllerProvider.register directive: $compileProvider.directive
那么,有没有办法懒惰地实例化模块?是的, you can register a module and it's sub-modules by iterating through various nested properties of the module object (requires
and _invokeQueue
) ,这个操作在一个名为 ocLazyLoad 的库中被简化了。
本节中提供的大部分代码均可在 this plunker 中找到。
(以上未提及的灵感来源:Couch Potato,AngularAMD)
ocLazyLoad
+ ui-router
+ requirejs
] - plunk 因为 ui-router 允许我们推迟加载模板和控制器,我们可以将它与 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; },
controller: 'lazyCtrl',
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', 'lazy-partials'], function (angular) {
var appLazy = angular.module('app.lazy', ['app.lazy.partials']);
appLazy.controller('lazyCtrl', function ($scope, $compile, $templateCache) {
$scope.data = 'my data';
});
});
请注意,在延迟加载方面,此文件不再有任何特殊之处。您可以以非懒惰的方式轻松加载此文件,但它不会知道其中的差异。同样的原则适用于 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) {
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>');
});
});
在部署方面,这个难题的最后一部分是使用requirejs优化器来连接和最小化我们的js文件。理想情况下,我们希望优化器跳过已包含在主应用程序中的依赖项的连接(即:公共文件)。为实现此目标,see this repo及其随附的build.js file。
我们可以通过为 a very elegant solution 添加ui-router装饰器来改进之前的插件。