移动端服务构建指南

351 阅读4分钟

服务诞生的背景

我们在日常开发中,为了满足客户的需求,使产品更于接近客户,经常会在移动设备上开发多端的入口。目前在新零售开发组里面,以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的操作
}