繁体   English   中英

需要模式Browserify / Angular

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


编辑(两年后):其他一些人(在这里和其他地方)建议了替代方法,所以我应该解决它们以及权衡取舍:

  1. 为您的整个应用程序提供一个全局AngularJS模块,并且只需要副作用(即,除了操纵全局角度对象之外,不要让子模块导出任何内容)。

    这似乎是最常见的解决方案,但在面对模块时却有点苍蝇。 这似乎是最务实的方法,但是如果你使用AngularJS,你已经在污染全局变量,所以我认为纯粹的基于副作用的模块是你的架构问题中最少的。

  2. 将AngularJS应用程序代码传递给Browserify 之前,将其连接起来。

    这是“让我们结合AngularJS和Browserify”的最直接的解决方案。 如果您从AngularJS的传统“盲目连接您的应用程序文件”位置开始并想要为第三方库添加Browserify,这是一种有效的方法,所以我想这使它有效。

    至于你的应用程序结构,通过添加Browserify,这并没有真正改善任何事情。

  3. 像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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM