大型 AngularJS 和 JavaScript 应用程序中的代码组织
许多开发人员都在纠结如何组织应用程序的代码
一旦它的大小增长了基础。我最近在 AngularJS 和
JavaScript 应用程序,但从历史上看,它一直是一个问题
所有技术,包括我从事过的许多 Java 和 Flex 应用程序
过去。
总的趋势是痴迷于按类型组织事物。它
与人们组织他们的方式有着惊人的相似之处
衣服。
地板上的桩
我们来看看angular-seed,官方的起点
AngularJS 应用程序。 “app”目录包含以下结构:
css/img/js/app.js controllers.js directives.js filters.js
services.js lib/ partials/ JavaScript 目录有一个文件用于
我们编写的每种类型的对象。这很像组织你的
衣服在地板上分成不同的堆。你有一堆袜子,
内衣、衬衫、裤子等。你知道你的黑色羊毛袜在
角落里的那堆东西,但要花一些时间才能挖出来
出去。
这是一团糟。人们不应该这样生活和开发人员
不应该这样编码。一旦你超过六打左右
控制器或服务这些文件变得笨拙:你的对象
很难找到,源代码管理中的文件变更集成为
不透明等。
袜子抽屉
组织 JavaScript 的下一个逻辑步骤是创建一个
一些原型的目录并将对象拆分为它们的
自己的文件。为了延续服装比喻,我们现在投资了一个
漂亮的莫哈格尼梳妆台,打算把袜子放在一个抽屉里,内衣
另一个,整齐地折叠我们的裤子和衬衫。
假设我们正在构建一个带有登录名的简单电子商务网站
流程、产品目录和购物车 UI。我们还定义了新的
模型(业务逻辑和状态)和服务(代理)的原型
到 HTTP/JSON 端点)而不是将它们归为 Angular 的单一
“服务”原型。我们的 JavaScript 目录现在可以如下所示:
controllers/ LoginController.js RegistrationController.js
ProductDetailController.js SearchResultsController.js directives.js
filters.js 模型/ CartModel.js ProductModel.js SearchResultsModel.js
UserModel.js 服务/ CartService.js UserService.js ProductService.js
好的!现在可以通过浏览文件树或
使用 IDE 快捷方式,源代码管理中的变更集现在清楚地表明
修改了什么等。这是一个重大改进,但仍然受到影响
受到一些限制。
假设您在办公室并意识到您需要几套服装
干洗明天早上出差。你打电话回家
让你的另一半拿走你的黑炭和蓝
细条纹适合清洁工。别忘了那件灰色衬衫
黑色佩斯利领带和白色衬衫搭配纯黄色领带。
想象一下,你的另一半完全不熟悉
你的梳妆台和衣柜。当他们筛选你的领带抽屉时,他们
看到三个黄色领带。选哪个?
如果你的衣服按着装来组织不是很好吗?尽管
有成本和空间等实际限制
在现实世界中穿衣服很困难,类似的东西可以是
以零成本完成代码。
模块化
希望这些陈词滥调的比喻不会太乏味,但这里是
回顾:
您的另一半是团队中的新开发人员
要求修复应用程序中众多屏幕之一上的错误。这
开发人员筛选目录结构并查看所有
控制器、模型和服务井井有条。不幸的是
不告诉他/她哪些对象相关或具有
相互依赖。如果在某个时候开发人员想要
重用一些代码,他们需要从一堆文件中收集文件
不同的文件夹,并且总是会忘记另一个文件夹中的代码
别的地方。信不信由你,你很少需要重用所有
新报告应用程序中电子商务应用程序的控制器
你正在建造。但是,您可能需要重用一些
身份验证逻辑。如果这一切合二为一,那不是很好吗
地方?让我们根据功能区域重新组织应用程序:
cart/ CartModel.js CartService.js common/directives.js filters.js
产品/搜索/ SearchResultsController.js SearchResultsModel.js
ProductDetailController.js ProductModel.js ProductService.js 用户/
LoginController.js RegistrationController.js UserModel.js
UserService.js 任何随机的开发者现在都可以打开顶级文件夹
并立即深入了解应用程序的功能。对象
在同一个文件夹中有关系,有些会有依赖关系
在别人身上。了解登录和注册过程的工作原理
就像浏览该文件夹中的文件一样简单。通过原始重用
复制/粘贴至少可以通过将文件夹复制到
另一个项目。
使用 AngularJS,我们可以更进一步,创建一个模块
此相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 var userModule =
angular.module('userModule',[]); userModule.factory('userService',
['$http', function($http) { return new UserService($http); }]);
userModule.factory('userModel', ['userService', function(userService)
{ 返回新的用户模型(用户服务); }]);
userModule.controller('loginController', ['$scope', 'userModel',
登录控制器]); userModule.controller('registrationController',
['$scope', 'userModel', RegistrationController]);看法
rawUserModule.js 由 GitHub 托管
UserModule.js 进入用户文件夹它就变成了一个“manifest”
该模块中使用的对象。这也是一个合理的地方
为 RequireJS 或 Browserify 添加一些加载器指令。
通用代码提示
每个应用程序都有许多模块使用的通用代码。我们
只需要一个地方,它可以是一个名为“common”的文件夹或
“共享”或任何你喜欢的。在非常大的应用程序中,往往
是功能和横切关注点的很多重叠。
这可以通过一些技术来管理:
如果您的模块对象需要直接访问几个“公共”
对象,为它们编写一个或多个 Facades。这可以帮助减少
每个对象的协作者数量,因为有太多
协作者通常是代码味道。如果您的“通用”模块
变大将其细分为解决特定问题的子模块
功能区或关注点。确保您的应用程序模块仅使用
他们需要的“通用”模块。这是“接口”的变体
隔离原则”来自 SOLID。将实用程序方法添加到 $rootScope
所以它们可以被子作用域使用。这可以帮助防止不得不
将相同的依赖项(例如“PermissionsModel”)连接到每个
应用程序中的控制器。请注意,这应该谨慎进行
避免弄乱全局范围并产生依赖关系
不明显。使用事件解耦两个不需要的组件
对彼此的明确引用。 AngularJS 使这成为可能
通过 Scope 对象上的 $emit、$broadcast 和 $on 方法。一种
控制器可以触发一个事件来执行一些动作,然后接收一个
动作完成的通知。资产和测试快速说明
我认为在组织方面有更大的灵活性空间
HTML、CSS 和图像。将它们放在
模块可能在封装之间取得了最佳平衡
模块的资产依赖关系,而不是把东西弄得乱七八糟。
但是我认为这个内容有一个单独的顶级文件夹
包含反映应用程序包结构的文件夹结构
也很合理。我认为它也适用于测试。