服务诞生的背景
我们在日常开发中,为了满足客户的需求,使产品更于接近客户,经常会在移动设备上开发多端的入口。目前在新零售开发组里面,以H5,小程序开发为主。
提供不同的应用入口,虽然为接近客户提供了便利,但是也增加了开发的成本。对客户而言,H5和小程序两端,在界面和功能上呈现的是相同的效果,但是背后却隐藏着不同。由于客户端在不同的框架上搭建,因此在具体实施上,会分两套过程。在原型和UI确定的情况下,H5和小程序客户端,会交由两人开发,在产品逻辑上存在重复开发,维护成本高的问题。
随着业务压力的日益增长,开发效率要求越来越高。在不增加人员的前提下,我们在移动端开发中,寻找提高效率的方式,构建移动端服务,成为选项之一。
目标:提高移动端开发效率。
过程:构建通用服务,提供给移动端(主要是H5和小程序)使用。
结果:移动端中相同的逻辑代码,由同一人开发,对应的另一端只需使用就行。将原来由两人完成的工作,优化到只需一人完成,效率提升50%。
服务的概念和目的
以下引自angular官方文档中《服务与依赖注入简介》一章
服务是一个广义的概念,它包括应用所需的任何值、函数或特性。狭义的服务是一个明确定义了用途的类。它应该做一些具体的事,并做好。
把组件和服务区分开,以提高模块性和复用性。 通过把组件中和视图有关的功能与其它类型的处理分离开,你可以让组件类更加精简、高效。
理想情况下,组件的工作只管用户体验,而不用顾及其它。 它应该提供用于数据绑定的属性和方法,以便作为视图(由模板渲染)和应用逻辑(通常包含一些模型的概念)的中介者。
组件应该把诸如从服务器获取数据、验证用户输入或直接往控制台中写日志等工作委托给各种服务。通过把各种处理任务定义到可注入的服务类中,你可以让它被任何组件使用。 通过在不同的环境中注入同一种服务的不同提供者,你还可以让你的应用更具适应性。
从以上引用文档中总结出来:
1.服务与组件,视图无直接依赖关系。本文所指的服务是一个独立的Javascript文件,只依赖语言的基础功能,不依赖任何视图与组件。
2.服务的内容可以包含服务器获取数据、验证用户输入或直接往控制台中写日志等。这些内容通常与视图,组件无直接联系。
3.服务能够使你的应用更具适应性。易于修改和维护,降低代码重复率,适应快速迭代的需求。
4.服务是更高级的抽象。通常将服务构建成一个对象,通过对象的属性和方法,映射视图的渲染和用户的操作。
创建服务
UserInfoService.js
class UserInfoService {
// 服务所包含的属性,方法
}
// 导出单例(是否导出单例取决于服务在应用中的定位和特性,如果不是一个单例,则只需要导出类)
const userInfo = new UserInfo();
export default userInfo;
// 导出类
//class UserInfoService {
// 服务所包含的属性,方法
//}
为服务创建属性和方法
class UserInfoService {
// 服务所包含的属性,方法
constructor(tokenAPI) {
this.tokenAPI = tokenAPI;
this.userToken = '';
this.records = [];
}
get userToken() {
return this.token;
}
setUserToken(token) {
if (!token || token === 'undefined') {
throw new Error('token为空');
}
this.token = token;
}
async fetchToken() {
const response = await this.tokenAPI();
// 服务本身的逻辑处理
if (response.success && response.data) {
this.setUserToken(response.data);
}
// 将接口的返回值返回出去
return response;
}
// 返回激活的记录
activeRecords() {
const activeRecords = [];
this.records.forEach(elem=>{
if (elem.active) {
activeRecords.push(elem)
}
});
return activeRecords;
}
// 根据id查询记录并返回
recordById(id) {
const record = null;
this.records.forEach(elem=>{
if (elem.id === id) {
record = elem;
}
});
return record;
}
}
组件(页面)调用服务
// minePage.js
// 引用用户服务
import UserInfoService from 'your-path/UserInfoService';
import tokenAPI from 'your-path/API.js';
Page({
data: {
},
// options(Object)
onLoad: async function (options) {
// 初始化服务
const userInfoService = new UserInfoService(tokenAPI);
// 调用服务的方法
const response = await userInfoService.fetchToken();
if (!response.success) {
response.msg ? toast(response.msg) : '' ;
return;
}
// 成功的情况下从服务取token
const token = userInfoService.userToken;
// 将token设入当前页面的 data
this.setData({
token
});
},
onReady: function () { },
onShow: function () { },
onHide: function () { },
onUnload: function () { },
onPullDownRefresh: function () { },
onReachBottom: function () { },
onShareAppMessage: function () { },
onPageScroll: function () { },
// item(index,pagePath,text)
onTabItemTap: function () { }
});
服务方法返回值规范
1.服务的属性可能为空(null,[],undefined,'')。调用服务的属性时需要做非空判断
const token = userInfoService.userToken;
if (token) {
// 使用token的操作
}
2.服务方法返回值可能为空。使用返回值之前需要做非空判断.
调用返回值为数组的方法,如果没有就会返回空数组:[ ];
调用返回值为对象的方法,如果没有就会返回空对象: null.
const activeRecords = userInfoService.activeRecords();
if (activeRecords.length > 0) {
// 使用activeRecords的操作
}
const record = userInfoService.recordById('111111');
if (record) {
// 使用record的操作
}