AngularJS 服务(Service):构建可复用逻辑的核心机制
在开发大型前端应用时,你是否遇到过这样的场景:多个控制器需要调用相同的 API 接口?或是多个视图需要共享同一份数据处理逻辑?如果每个地方都重复写一遍代码,不仅效率低下,后期维护起来更是噩梦。这时候,AngularJS 提供的 AngularJS 服务(Service) 就像一个“中央调度室”,专门负责封装那些需要跨组件复用的逻辑,让代码更清晰、更健壮。
服务不是简单的函数集合,而是一种设计模式。它强调单例性(在整个应用中只存在一个实例)和解耦(控制器不直接处理数据,而是通过服务来完成)。你可以把它想象成一个“公共工具箱”——无论哪个页面需要拧螺丝、锯木头,都从这个工具箱里取工具,而不是每个人都自己带一套。
什么是 AngularJS 服务?它的核心价值
在 AngularJS 中,服务是一个可注入的、可复用的、独立于视图的 JavaScript 对象。它通常用来封装数据获取、业务逻辑、工具函数等。服务的真正魅力在于它的“依赖注入”(Dependency Injection)机制——你只需要声明需要什么服务,AngularJS 会自动帮你创建并传入。
举个例子:假设你有一个电商应用,需要从服务器获取商品列表。如果你在每个控制器里都写一遍 http.get('/api/products'),那当接口地址变了,你得改十几个地方。但如果把这段代码封装进一个服务,只需要改一处,所有用到的地方都会自动更新。
服务的生命周期与应用相同,一旦创建,就一直存在于内存中,直到页面刷新。这使得它非常适合存储状态、缓存数据或管理全局行为。
创建与注册服务:三种方式详解
AngularJS 支持多种方式创建服务,最常用的是 factory 和 service。虽然功能类似,但底层实现略有不同。
使用 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是一个数组,保存了购物车中的商品,这就是“状态”的体现。- 方法如
addItem、getTotalCount都是实例方法,每个控制器调用时都操作同一个实例。- 服务实例是单例的,所以所有控制器共享同一个购物车。
服务的依赖注入:如何使用服务
一旦注册了服务,你就可以在控制器中通过依赖注入的方式使用它。
// 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 服务,是你迈向专业前端开发的重要一步。它不仅是代码组织的工具,更是构建可扩展、可维护应用的基石。当你不再为“哪里该写逻辑”而纠结时,你就真正理解了服务的意义。