小编典典

AngularJS:了解设计模式

angularjs

在AngularJS负责人Igor Minar
这篇文章中:

MVC vs MVVM vs MVP 。许多开发人员可能花费数小时来辩论和争论,这是一个有争议的话题。

几年来,AngularJS更加接近于MVC(或其客户端变体之一),但是随着时间的流逝,由于许多重构和api的改进,现在它更加接近 MVVM
$ scope 对象可以被认为是 ViewModel 。由我们称为 Controller 的函数修饰。

能够对框架进行分类并将其放入MV
*存储桶之一具有一些优势。通过使创建代表使用框架构建的应用程序的思维模型更容易,它可以帮助开发人员更熟悉其api。它还可以帮助建立开发人员使用的术语。

话虽如此,我宁愿看到开发人员构建经过精心设计并遵循关注点分离的踢屁股应用程序,也不愿看到他们浪费时间争论MV 废话。基于这个原因,我特此声明
AngularJSMVW框架-Model-View-Whatever 。凡代表什么“ 对您有用* ”。

Angular为您提供了很大的灵活性,可以很好地将表示逻辑与业务逻辑和表示状态分开。请使用它来提高您的生产力和应用程序可维护性,而不是热烈讨论最后没有什么关系的事情。

在客户端应用程序中实现AngularJS MVW(模型-视图-任何)设计模式是否有任何建议或准则?


阅读 290

收藏
2020-07-04

共1个答案

小编典典

感谢大量有价值的资源,我为在AngularJS应用程序中实现组件提供了一些一般性建议:


控制者

  • 控制器应该只是模型和视图之间的 中间层 。尝试使其尽可能

  • 强烈建议 避免 在控制器中使用 业务逻辑 。应该将其移至模型。

  • 控制器可以使用方法调用(在孩子想与父母进行通信时)或 $ emit$ broadcast$ on 方法与其他控制器通信。发出和广播的消息应保持最少。

  • 控制器不应该 关心表示 或DOM操作。

  • 尽量 避免嵌套控制器 。在这种情况下,父控制器被解释为模型。而是将模型作为共享服务注入。

  • *控制器的 *作用域 应用于与视图 绑定* 模型,并像 表示模型 设计模式那样
    封装 视图模型
    *


范围

如治疗范围 只读模板只写在控制器 。范围的目的是引用模型,而不是模型。

在进行双向绑定(ng-model)时,请确保您不直接绑定到范围属性。


模型

AngularJS中的模型是 service* 定义的 单例*

模型提供了分离数据和显示的绝佳方法。

模型是单元测试的主要候选者,因为它们通常仅具有一种依赖关系(某种形式的事件发射器,在通常情况下为 $ rootScope )并且包含高度可测试的
域逻辑

  • 模型应被视为特定单元的实现。它基于单一职责原则。单元是一个实例,负责其自身的相关逻辑范围,该逻辑可以表示现实世界中的一个实体,并在编程世界中根据 数据和状态对其进行描述

  • 模型应封装您的应用程序数据,并提供 API 以访问和操纵该数据。

  • 模型应该是 便携式的, 以便可以轻松地运输到类似的应用程序。

  • 通过隔离模型中的单元逻辑,您可以更轻松地查找,更新和维护。

  • 模型可以使用整个应用程序通用的更通用的全局模型的方法。

  • 如果不是真正依赖于减少组件耦合并增加单元 可测试性可用性, 请尝试避免使用依赖注入将其他模型组合到模型中。

  • 尝试避免在模型中使用事件侦听器。它使它们更难测试,并且通常会按照单一职责原则杀死模型。

模型实施

由于模型应在数据和状态方面封装一些逻辑,因此在结构上应限制对其成员的访问,因此我们可以保证松散耦合。

在AngularJS应用程序中执行此操作的方法是使用 工厂
服务类型进行定义。这将使我们能够非常轻松地定义私有属性和方法,并在单个位置返回可公共访问的属性和方法,这将使其对开发人员真正可读。

一个例子

angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {

  var itemsPerPage = 10,
  currentPage = 1,
  totalPages = 0,
  allLoaded = false,
  searchQuery;

  function init(params) {
    itemsPerPage = params.itemsPerPage || itemsPerPage;
    searchQuery = params.substring || searchQuery;
  }

  function findItems(page, queryParams) {
    searchQuery = queryParams.substring || searchQuery;

    return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
      totalPages = results.totalPages;
      currentPage = results.currentPage;
      allLoaded = totalPages <= currentPage;

      return results.list
    });
  }

  function findNext() {
    return findItems(currentPage + 1);
  }

  function isAllLoaded() {
    return allLoaded;
  }

  // return public model API  
  return {
    /**
     * @param {Object} params
     */
    init: init,

    /**
     * @param {Number} page
     * @param {Object} queryParams
     * @return {Object} promise
     */
    find: findItems,

    /**
     * @return {Boolean}
     */
    allLoaded: isAllLoaded,

    /**
     * @return {Object} promise
     */
    findNext: findNext
  };
});

创建新实例

尽量避免让工厂返回新的有能力的函数,因为这会破坏依赖注入,并且库的行为会很尴尬,尤其是对于第三方。

完成同一件事的更好方法是使用工厂作为API,以返回带有附加的getter和setter方法的对象集合。

angular.module('car')
 .factory( 'carModel', ['carResource', function (carResource) {

  function Car(data) {
    angular.extend(this, data);
  }

  Car.prototype = {
    save: function () {
      // TODO: strip irrelevant fields
      var carData = //...
      return carResource.save(carData);
    }
  };

  function getCarById ( id ) {
    return carResource.getById(id).then(function (data) {
      return new Car(data);
    });
  }

  // the public API
  return {
    // ...
    findById: getCarById
    // ...
  };
});

全局模型

通常,尝试避免这种情况并正确设计模型,以便可以将其注入控制器并在视图中使用。

在特定情况下,某些方法需要应用程序内的全局可访问性。为了使之成为可能,您可以在 $ rootScope中 定义’ common‘属性,并在应用程序引导期间将其绑定到 commonModel

angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
  $rootScope.common = 'commonModel';
}]);

您所有的全局方法都将位于“ common ”属性内。这是某种 名称空间

但是不要直接在 $ rootScope中
定义任何方法。在视图范围内与ngModel指令一起使用时,这可能导致意外的行为,通常使您的范围乱七八糟,并导致范围方法覆盖问题。


资源资源

资源使您可以与不同的 数据源进行 交互。

应该使用 single-responsibility-principle实现

在特定情况下,它是HTTP / JSON端点的 可重用 代理。

资源被注入模型中,并提供了发送/检索数据的可能性。

资源实施

工厂创建一个资源对象,使您可以与RESTful服务器端数据源进行交互。

返回的资源对象具有可提供高级行为的操作方法,而无需与低级$ http服务进行交互。


服务

模型和资源都是服务

服务是独立的,独立的, 松散耦合 的功能单元。

服务是Angular从服务器端带到客户端Web应用程序的功能,在该服务器上,服务已被长期使用。

Angular应用程序中的服务是可替换对象,这些对象使用依赖项注入连接在一起。

Angular带有不同类型的服务。每个都有自己的用例。有关详细信息,请阅读了解服务类型

尝试考虑应用程序中服务体系结构的主要原理

通常根据Web服务词汇表

服务是一种抽象资源,代表从提供者实体和请求者实体的角度来看,执行形成一致功能的任务的能力。要使用该服务,必须由具体的提供者代理来实现服务。


客户端结构

通常,应用程序的客户端分为 模块 。每个模块都应作为一个单元进行 测试

尝试根据 功能/特性视图
(而不是类型)定义模块。有关详细信息,请参见Misko的演示

按照惯例,模块组件可以按类型分组,例如控制器,模型,视图,过滤器,指令等。

但是模块本身仍然是 可重用 转让可测试的

对于开发人员来说,查找代码的某些部分及其所有依赖性也要容易得多。

有关详细信息,请参阅LargeAngularJS和JavaScript应用程序中的代码组织

文件夹结构的示例

|-- src/
|   |-- app/
|   |   |-- app.js
|   |   |-- home/
|   |   |   |-- home.js
|   |   |   |-- homeCtrl.js
|   |   |   |-- home.spec.js
|   |   |   |-- home.tpl.html
|   |   |   |-- home.less
|   |   |-- user/
|   |   |   |-- user.js
|   |   |   |-- userCtrl.js
|   |   |   |-- userModel.js
|   |   |   |-- userResource.js
|   |   |   |-- user.spec.js
|   |   |   |-- user.tpl.html
|   |   |   |-- user.less
|   |   |   |-- create/
|   |   |   |   |-- create.js
|   |   |   |   |-- createCtrl.js
|   |   |   |   |-- create.tpl.html
|   |-- common/
|   |   |-- authentication/
|   |   |   |-- authentication.js
|   |   |   |-- authenticationModel.js
|   |   |   |-- authenticationService.js
|   |-- assets/
|   |   |-- images/
|   |   |   |-- logo.png
|   |   |   |-- user/
|   |   |   |   |-- user-icon.png
|   |   |   |   |-- user-default-avatar.png
|   |-- index.html

angular-app 实现了角度应用程序结构的一个很好的例子 -https ://github.com/angular-app/angular-app/tree/master/client/src

现代应用程序生成器也考虑了这一点-https: //github.com/yeoman/generator-angular/issues/109

2020-07-04