AngularJS 服务(Service)(实战总结)

AngularJS 服务(Service):构建可复用逻辑的核心机制

在开发大型前端应用时,你是否遇到过这样的场景:多个控制器需要调用相同的 API 接口?或是多个视图需要共享同一份数据处理逻辑?如果每个地方都重复写一遍代码,不仅效率低下,后期维护起来更是噩梦。这时候,AngularJS 提供的 AngularJS 服务(Service) 就像一个“中央调度室”,专门负责封装那些需要跨组件复用的逻辑,让代码更清晰、更健壮。

服务不是简单的函数集合,而是一种设计模式。它强调单例性(在整个应用中只存在一个实例)和解耦(控制器不直接处理数据,而是通过服务来完成)。你可以把它想象成一个“公共工具箱”——无论哪个页面需要拧螺丝、锯木头,都从这个工具箱里取工具,而不是每个人都自己带一套。


什么是 AngularJS 服务?它的核心价值

在 AngularJS 中,服务是一个可注入的、可复用的、独立于视图的 JavaScript 对象。它通常用来封装数据获取、业务逻辑、工具函数等。服务的真正魅力在于它的“依赖注入”(Dependency Injection)机制——你只需要声明需要什么服务,AngularJS 会自动帮你创建并传入。

举个例子:假设你有一个电商应用,需要从服务器获取商品列表。如果你在每个控制器里都写一遍 http.get('/api/products'),那当接口地址变了,你得改十几个地方。但如果把这段代码封装进一个服务,只需要改一处,所有用到的地方都会自动更新。

服务的生命周期与应用相同,一旦创建,就一直存在于内存中,直到页面刷新。这使得它非常适合存储状态、缓存数据或管理全局行为。


创建与注册服务:三种方式详解

AngularJS 支持多种方式创建服务,最常用的是 factoryservice。虽然功能类似,但底层实现略有不同。

使用 factory 创建服务

factory 是最灵活的方式,它返回一个对象,你可以自由定义它的属性和方法。

// app.js
angular.module('myApp', [])
  .factory('ProductService', function($http) {
    // 1. 这个函数会返回一个对象,这个对象就是服务实例
    // 2. $http 是 AngularJS 内置服务,通过依赖注入传入
    // 3. 你可以在这里定义任何逻辑,比如调用 API、处理数据等

    return {
      // 获取商品列表的方法
      getProducts: function() {
        return $http.get('/api/products'); // 返回一个 Promise
      },

      // 根据 ID 获取单个商品
      getProductById: function(id) {
        return $http.get('/api/products/' + id);
      },

      // 添加商品(可选:验证逻辑也放这里)
      addProduct: function(product) {
        return $http.post('/api/products', product);
      }
    };
  });

✅ 注释说明:

  • angular.module('myApp', []):创建一个名为 myApp 的模块,[] 表示没有依赖模块。
  • .factory('ProductService', function($http) { ... }):注册一个名为 ProductService 的服务,依赖 $http 服务。
  • return { ... }:返回一个包含多个方法的对象,这些方法就是服务对外提供的接口。
  • 所有方法都返回 Promise,这是异步操作的标准做法。

使用 service 创建服务

service 是通过构造函数的方式来创建服务,适合需要实例化、有状态的场景。

// app.js
angular.module('myApp', [])
  .service('CartService', function($http) {
    // 1. this 是服务的实例对象
    // 2. 所有属性和方法都挂载在 this 上
    // 3. 适合需要保存状态(如购物车中的商品列表)

    this.items = []; // 存储购物车商品

    // 添加商品到购物车
    this.addItem = function(product) {
      this.items.push(product);
    };

    // 获取购物车总数
    this.getTotalCount = function() {
      return this.items.length;
    };

    // 清空购物车
    this.clear = function() {
      this.items = [];
    };

    // 从服务器同步购物车数据(示例)
    this.syncWithServer = function() {
      return $http.post('/api/cart/sync', this.items);
    };
  });

✅ 注释说明:

  • this 指向服务的实例,你可以在服务内部通过 this.xxx 定义属性和方法。
  • this.items 是一个数组,保存了购物车中的商品,这就是“状态”的体现。
  • 方法如 addItemgetTotalCount 都是实例方法,每个控制器调用时都操作同一个实例。
  • 服务实例是单例的,所以所有控制器共享同一个购物车。

服务的依赖注入:如何使用服务

一旦注册了服务,你就可以在控制器中通过依赖注入的方式使用它。

// controller.js
angular.module('myApp')
  .controller('ProductController', function($scope, ProductService, CartService) {
    // 1. $scope 是作用域对象,用于绑定视图
    // 2. ProductService 和 CartService 是注入的服务
    // 3. AngularJS 会自动创建实例并传入

    $scope.products = []; // 存放商品列表
    $scope.cartCount = 0;  // 显示购物车数量

    // 初始化时加载商品
    ProductService.getProducts()
      .then(function(response) {
        $scope.products = response.data; // 将返回的数据赋值给作用域
      })
      .catch(function(error) {
        console.error('获取商品失败:', error);
      });

    // 添加商品到购物车
    $scope.addToCart = function(product) {
      CartService.addItem(product);
      $scope.cartCount = CartService.getTotalCount(); // 更新显示
    };

    // 清空购物车
    $scope.clearCart = function() {
      CartService.clear();
      $scope.cartCount = 0;
    };
  });

✅ 注释说明:

  • function($scope, ProductService, CartService):控制器的参数列表,AngularJS 会按顺序注入对应的服务。
  • ProductService.getProducts():调用服务的 getProducts 方法,返回一个 Promise。
  • .then().catch():处理异步操作的成功与失败。
  • CartService.addItem(product):调用服务的方法,修改共享状态。
  • 所有操作都通过服务完成,控制器只负责“协调”和“绑定”。

服务的生命周期与单例机制

AngularJS 的服务是单例的,这意味着在整个应用运行期间,每个服务只被创建一次。无论你在多少个控制器中注入同一个服务,它们都指向同一个实例。

这个特性非常关键,它保证了:

  • 数据一致性:多个视图看到的是同一份数据。
  • 内存高效:不需要重复创建对象。
  • 状态共享:如购物车、用户登录状态等,可以跨页面共享。
// 示例:两个控制器使用同一个 CartService
angular.module('myApp')
  .controller('CartController', function($scope, CartService) {
    $scope.cart = CartService.items; // 直接访问共享数据
  })
  .controller('CheckoutController', function($scope, CartService) {
    $scope.totalItems = CartService.getTotalCount(); // 共享方法
  });

✅ 注意:由于服务是单例,所以你必须小心处理共享状态。避免在服务中使用可变的全局变量,除非你明确知道它的作用。


实际应用:构建一个完整的数据管理流程

我们来搭建一个完整的场景:用户浏览商品 → 加入购物车 → 查看购物车 → 提交订单。

1. 服务层设计

// services.js
angular.module('myApp')
  .factory('DataService', function($http) {
    return {
      // 获取商品列表
      getProducts: function() {
        return $http.get('/api/products');
      },

      // 提交订单
      submitOrder: function(orderData) {
        return $http.post('/api/orders', orderData);
      }
    };
  })
  .service('CartService', function() {
    this.items = [];

    this.add = function(item) {
      this.items.push(item);
    };

    this.remove = function(index) {
      this.items.splice(index, 1);
    };

    this.getTotal = function() {
      return this.items.length;
    };

    this.clear = function() {
      this.items = [];
    };
  });

2. 控制器使用

// controllers.js
angular.module('myApp')
  .controller('MainController', function($scope, DataService) {
    $scope.products = [];

    DataService.getProducts()
      .then(function(res) {
        $scope.products = res.data;
      });
  })
  .controller('CartController', function($scope, CartService) {
    $scope.cart = CartService.items;

    $scope.removeFromCart = function(index) {
      CartService.remove(index);
    };

    $scope.getTotal = function() {
      return CartService.getTotal();
    };
  });

3. 视图绑定(HTML)

<!-- index.html -->
<div ng-controller="MainController">
  <h2>商品列表</h2>
  <div ng-repeat="p in products">
    {{ p.name }} - {{ p.price }} 元
    <button ng-click="addToCart(p)">加入购物车</button>
  </div>
</div>

<div ng-controller="CartController">
  <h2>购物车 ({{ getTotal() }} 件)</h2>
  <ul>
    <li ng-repeat="item in cart">
      {{ item.name }} - {{ item.price }}
      <button ng-click="removeFromCart($index)">移除</button>
    </li>
  </ul>
</div>

✅ 小结:整个流程中,数据获取、状态管理、逻辑处理都被封装在服务中,控制器只负责“连接”和“展示”,完全符合单一职责原则。


总结:为什么你应该使用 AngularJS 服务?

  • 代码复用:避免重复逻辑,提升开发效率。
  • 逻辑解耦:控制器不再关心“怎么获取数据”,只关心“怎么展示”。
  • 易于测试:服务可以单独测试,不依赖 DOM 或视图。
  • 状态共享:跨控制器、跨视图共享数据,适合构建复杂应用。
  • 可维护性强:修改一处逻辑,影响全局,无需遍历所有控制器。

掌握 AngularJS 服务,是你迈向专业前端开发的重要一步。它不仅是代码组织的工具,更是构建可扩展、可维护应用的基石。当你不再为“哪里该写逻辑”而纠结时,你就真正理解了服务的意义。