[英]Require pattern Browserify / Angular
我正在使用angular和browserify开展一个项目,这是我第一次将这两个工具结合使用,所以我想要一些建议,这就是require
使用browserify文件的方法。
我们可能以不同的方式导入这些文件,到现在为止我用这种方式进行了实验:
角度应用:
app
_follow
- followController.js
- followDirective.js
- followService.js
- require.js
- app.js
对于插件文件中的每个文件夹,我创建了一个require.js
文件,在其中我需要该文件夹的所有文件。 像这样:
var mnm = require('angular').module('mnm');
mnm.factory('FollowService', ['Restangular',require('./followService')]);
mnm.controller('FollowController',['$scope','FollowService',require('./followController')])
mnm.directive('mnmFollowers', ['FollowService',require('./followDirective')]);
然后在名为app.js
的唯一文件中要求所有require.js
文件生成bundle.js
题:
这种方式要求文件可以是一个很好的结构,或者当我需要测试时会有一些问题吗? 我希望看到你通过angular和browserify实现良好结构的方法
遗憾的是,AngularJS和browserify并不是一个很好的匹配。 当然不喜欢React和browserify,但我离题了。
对我有用的是将每个文件作为AngularJS模块(因为每个文件已经是一个CommonJS模块)并让文件导出它们的AngularJS模块名称。
所以你的例子看起来像这样:
app/
app.js
follow/
controllers.js
directives.js
services.js
index.js
app.js
看起来像这样:
var angular = require('angular');
var app = angular.module('mnm', [
require('./follow')
]);
// more code here
angular.bootstrap(document.body, ['mnm']);
follow/index.js
看起来像这样:
var angular = require('angular');
var app = angular.module('mnm.follow', [
require('./controllers'),
require('./directives'),
require('./services')
]);
module.exports = app.name;
follow/controllers.js
看起来像这样:
var angular = require('angular');
var app = angular.module('mnm.follow.controllers', [
require('./services'), // internal dependency
'ui.router' // external dependency from earlier require or <script/>
// more dependencies ...
]);
app.controller('FollowController', ['$scope', 'FollowService', function ...]);
// more code here
module.exports = app.name;
等等。
这种方法的优点是可以使您的依赖关系尽可能明确(即在CommonJS模块中实际需要它们),并且CommonJS模块路径和AngularJS模块名称之间的一对一映射可以防止令人讨厌的意外。
您的方法最明显的问题是您将保留实际的依赖项,这些依赖项将与期望它们的函数分开注入,因此如果函数的依赖项发生更改,则必须触摸两个文件而不是一个文件。 这是代码气味(即坏事)。
对于可测试性,任何一种方法都应该起作用,因为Angular的模块系统本质上是一个巨大的blob,导入两个定义相同名称的模块将相互覆盖。
编辑(两年后):其他一些人(在这里和其他地方)建议了替代方法,所以我应该解决它们以及权衡取舍:
为您的整个应用程序提供一个全局AngularJS模块,并且只需要副作用(即,除了操纵全局角度对象之外,不要让子模块导出任何内容)。
这似乎是最常见的解决方案,但在面对模块时却有点苍蝇。 这似乎是最务实的方法,但是如果你使用AngularJS,你已经在污染全局变量,所以我认为纯粹的基于副作用的模块是你的架构问题中最少的。
在将AngularJS应用程序代码传递给Browserify 之前,将其连接起来。
这是“让我们结合AngularJS和Browserify”的最直接的解决方案。 如果您从AngularJS的传统“盲目连接您的应用程序文件”位置开始并想要为第三方库添加Browserify,这是一种有效的方法,所以我想这使它有效。
至于你的应用程序结构,通过添加Browserify,这并没有真正改善任何事情。
像1一样,但每个index.js定义自己的AngularJS子模块。
这是Brian Ogden建议的样板方法。 这有1的所有缺点,但在AngularJS中创建了一些层次结构,因为至少你有多个AngularJS模块,而AngularJS模块名称实际上对应于你的目录结构。
但是主要的缺点是你现在有两套命名空间需要担心(你的实际模块和你的AngularJS模块)但没有强制它们之间的一致性。 您不仅要记住导入正确的模块(这又是纯粹依赖于副作用),而且您还必须记住将它们添加到所有正确的列表中,并为每个新文件添加相同的样板。 这使得重构非常笨拙,并且在我看来这是最糟糕的选择。
如果我今天必须选择,我会选择2,因为它放弃了AngularJS和Browserify的所有伪装,能够统一并让两者都做自己的事情。 此外,如果您已经有一个AngularJS构建系统,它实际上只是意味着为Browserify添加额外的步骤。
如果您没有继承AngularJS代码库并想知道哪种方法最适合启动新项目:不要在AngularJS中启动新项目。 选择支持开箱即用的真实模块系统的Angular2,或切换到没有遇到此问题的React或Ember。
我试图在Angular中使用browserify,但发现它有点乱。 我不喜欢创建命名服务/控制器的模式,然后从另一个位置需要它,例如
angular.module('myApp').controller('charts', require('./charts'));
控制器名称/定义在一个文件中,但函数本身在另一个文件中。 如果你在IDE中打开了很多文件,那么拥有大量的index.js
文件会让人感到困惑。
所以我把这个gulp插件放在一起, gulp-require-angular允许你使用标准的Angular语法编写Angular,所有包含角度模块的js文件和出现在主模块依赖关系树中的角度模块的依赖关系都是require()
'd into生成的条目文件,然后将其用作browserify条目文件。
您仍然可以在代码库中使用require()
根据require()
将外部库(例如lodash)引入服务/过滤器/指令。
这是最新的Angular种子分叉并更新为使用gulp-require-angular
。
我使用了类似pluma的混合方法。 我创建了像这样的ng-modules:
var name = 'app.core'
angular.module(name, [])
.service('srvc', ['$rootScope', '$http', require( './path/to/srvc' ))
.service('srvc2', ['$rootScope', '$http', require( './path/to/srvc2' ))
.config...
.etc
module.exports = name
我认为不同之处在于我没有将单个ng-modules定义为主要ng-module的依赖项,在这种情况下,我不会将服务定义为ng-module,然后将其列为app.core
的dep app.core
ng模块。 我尽量保持平坦:
//srvc.js - see below
module.exports = function( $rootScope, $http )
{
var api = {};
api.getAppData = function(){ ... }
api.doSomething = function(){ ... }
return api;
}
关于代码嗅觉的评论,我不同意。 虽然这是一个额外的步骤,但它在针对模拟服务的测试方面允许一些很好的可配置性。 例如,我使用它来测试可能没有现成服务器API的再次服务:
angular.module(name, [])
// .service('srvc', ['$rootScope', '$http', require( './path/to/srvc' ))
.service('srvc', ['$rootScope', '$http', require( './path/to/mockSrvc' ))
所以依赖于srvc
任何控制器或对象都不知道它得到了什么。 我可以看到这在服务依赖于其他服务方面有点复杂,但对我来说这是糟糕的设计。 我更喜欢用ng的事件系统进行交流。 服务,以便你保持联系。
Alan Plum的答案不是一个很好的答案,或者至少不是一个很好的演示CommonJS模块和Browserify with Angular。 与React相比,Browserify与Angular不能很好地混合的说法是不正确的。
Browserify和CommonJS模块模式与Angular配合使用,允许您按功能而不是类型进行组织,使vars远离全局范围,并轻松地跨应用程序共享Angular模块。 更不用说由于Browserify找到了所有依赖项,您不需要再次向HTML添加单个<script>
。
Alan Plum的答案中有什么特别的缺陷,就是不要让每个index.js中的每个文件夹都要求Angular模块,控制器,服务,配置,路由等的依赖关系。在Angular.module实例化中不需要单个require,也不是一个单一的模块。出现在Alan Plum的回答所暗示的背景中。
使用Browserify查看Angular的更好模块模式: https : //github.com/Sweetog/yet-another-angular-boilerplate
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.