2014-10-09 71 views
6

我正在使用angular和browserify进行项目工作,这是我第一次将这两个工具结合使用,所以我想请教一些关于require文件与browserify 。需要图案Browserify/Angular

我们可以导入不同的方式对这些文件,到现在为止我尝试这样:

角应用:

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

问:

这样要求的文件可以是一个很好的结构,或者当我需要测试时会遇到一些问题?我想看看你的方式来实现良好的结构与角度和browserify

回答

5

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,并且导入两个定义相同名称的模块将相互覆盖。


EDIT(两年后):其他一些人(在这里和其他地方)建议的替代方法,所以我也许应该解决这些问题,什么权衡取舍:

  1. 有一个全局AngularJS模块为你的整个应用程序,只是做副作用需要(即没有子模块导出任何东西,但操纵全局角对象)。

    这似乎是最常见的解决方案,但有种苍蝇在面对有模块。这似乎是最实用的方法,但是如果你使用的是AngularJS,那么你已经对全局造成了污染,所以我猜测纯粹以副作用为基础的模块是你的体系结构问题中最少的。

  2. 在之前将您的AngularJS应用代码连接到Browserify。

    这是“让我们结合AngularJS和Browserify”的最直接的解决方案。如果你从传统的“只是盲目地连接你的应用程序文件”的位置开始,并且希望为第三方库添加Browserify,那么这是一个有效的方法,所以我想这是有效的。

    就你的应用结构而言,尽管增加了Browserify,但这并不能真正改善任何事情。

  3. 与1一样,但每个index.js都定义了自己的AngularJS子模块。

    这是Brian Ogden建议的样板方法。这受到1的所有缺点,但在AngularJS中创建了一些类似的层次结构,至少你有多个AngularJS模块,并且AngularJS模块名称实际上与你的目录结构相对应。

    但是,主要的缺点是您现在有两组名称空间来担心(您的实际模块和您的AngularJS模块),但没有强化它们之间的一致性。您不仅需要记住导入正确的模块(这又是纯粹依赖副作用),但您还必须记住将它们添加到所有正确的列表中,并将相同的样板添加到每个新文件。这使得重构变得难以置信,这使得我认为这是最糟糕的选择。

如果我今天不得不选择,我会去2,因为它给了AngularJS和Browserify能够统一所有的伪装,只是留下既做自己的事情。此外,如果您已经拥有一个AngularJS构建系统,它实际上意味着为Browserify添加额外步骤。

如果您没有继承AngularJS代码库,并想知道哪种方法最适合启动新项目:请勿在AngularJS中启动新项目。选择支持真实模块系统的Angular2,或切换到不受此问题影响的React或Ember。

+0

这工作完全 - 感谢经历的时间,使这一点。我设法做了一些非常类似于require.js的事情,并且帮助我从AMD的疯狂中走出来:D – marksyzm 2015-02-06 00:07:51

+0

@AlanPlum哇,难怪你不喜欢Browserify如何与Angular混合。这是一些疯狂的代码,不是一个好的模式。不需要module.exports,也不需要在角度模块实例化中...使用AngularJS和browserify https:// github查看更好的模式。com/Sweetog /另一种角度样板 – 2016-08-22 07:13:23

+0

一旦你将它们添加到正确的列表中,你不必记得再次添加它们,样板不是每个文件,只是模块和index.js每个目录,因为每个目录都是一个Angular模块 – 2016-08-22 17:36:03

0

我正在尝试使用与Angular的browserify,但发现它有点混乱。我不喜欢创建命名服务/控制器的模式,然后从其他位置请求它,例如,

angular.module('myApp').controller('charts', require('./charts')); 

控制器名称/定义在一个文件中,但函数本身在另一个文件中。如果您在IDE中打开大量文件,还会有大量的index.js文件,这让您感到非常困惑。

所以我整理了这篇一饮而尽插件,gulp-require-angular它允许您使用标准的角语法写的角度,所有的JS包含角模块是require()它出现在你的主模块的依赖关系树倒是成生成角模块的依赖关系文件条目文件,然后将其用作您的browserify条目文件。

你仍然可以使用require()你的代码库中的外部库拉(例如lodash)到服务/过滤器/指令的需要。

Here's the latest Angular seed forked and updated使用gulp-require-angular

+0

我投票给你,因为你做了一个很好的工作与该插件(我没有尝试,但看起来不错),但我会留在browserify 。 – Fabrizio 2014-10-30 13:49:34

0

我使用一种混合的方法很像pluma。我创建了NG-模块,像这样:

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模块作为依赖于主NG模块,在这种情况下,我不会定义一个服务为ng模块,然后将其列为ng模块的dep。我尽量保持它尽可能平坦:

//srvc.js - see below 
module.exports = function($rootScope, $http) 
{ 
    var api = {}; 
    api.getAppData = function(){ ... } 
    api.doSomething = function(){ ... } 
    return api; 
} 

关于代码气味的评论,我不同意。虽然这是一个额外的步骤,但它在针对模拟服务的测试方面提供了很好的可配置性。比如我用这个颇有几分测试agains服务,可能没有一个现有的服务器API准备:

angular.module(name, []) 
// .service('srvc', ['$rootScope', '$http', require('./path/to/srvc')) 
    .service('srvc', ['$rootScope', '$http', require('./path/to/mockSrvc')) 

因此,任何控制器或对象依赖srvc不知道哪个是越来越。我可以看到这在服务依赖于其他服务方面变得有些复杂,但对我而言这是糟糕的设计。我更喜欢使用ng的事件系统来进行通信。服务,以便您保持联系。

0

艾伦·梅的回答是不是个很棒的答案,或者至少不是CommonJS的模块和Browserify有角有很大的示范。与React相比,Browserify与Angular不能很好地融合的说法并不正确。

Browserify和CommonJS的模块模式的工作原理伟大的角度,让您的功能,而不是类型的组织,保持瓦尔了全球范围内的地共享跨应用角度模块。更何况,您不需要再次向您的HTML添加一个<script>,这要归功于Browserify查找所有依赖关系。

Alan Plum的答案中有什么特别的缺陷是不允许在每个index.js中为每个文件夹指定Angular模块,控制器,服务,配置,路由等的依赖关系。在Angular中不需要单个需求.module实例化,也不是单个模块。在Alan Plum的答案中提到的上下文中引用。

在这里看到一个更好的模块模式的角度使用Browserify:https://github.com/Sweetog/yet-another-angular-boilerplate

+0

我删除了我的意见,赞成编辑我的答案以解决您的建议(以及其他地方提出的问题)。 – 2016-08-22 08:33:38