基于Taro封装的微信小程序框架

1、基本概述

  由京东凹凸团队开源的小程序框架Taro (https://taro.aotu.io/),以及对应UI框架Taro-UI (https://taro-ui.jd.com/#/),是一套遵循 React 语法规范的 多端开发 解决方案。根据官方介绍:

   现如今市面上端的形态多种多样,Web、React-Native、微信小程序等各种端大行其道,当业务要求同时在不同的端都要求有所表现的时候,针对不同的端去编写多套代码的成本显然非常高,这时候只编写一套代码就能够适配到多端的能力就显得极为需要。

  使用 Taro,我们可以只书写一套代码,再通过 Taro 的编译工具,将源代码分别编译出可以在不同端(微信/百度/支付宝/字节跳动/QQ小程序、快应用、H5、React-Native 等)运行的代码。

   官方提供了对应的脚手架生成方法,本文基于官方脚手架进一步封装,帮助大家更好的利用Taro进行日常开发。github代码地址:https://github.com/zhengchangshun/myTaro

2、模块介绍


├── dist                                编译结果目录
├── config                              配置目录
|   ├── dev.js                          开发时配置
|   ├── index.js                        默认配置
|   └── prod.js                         打包时配置
├── src                                 源码目录
|   ├── components                      全局组件
|   |   |   ├── DateTimePicker          日期时间组件
|   |   |   ├── GenerateDetail          通过配置生成详情的组件
|   |   |   ├── PickerSelector          下拉组件的封装
|   ├── lib                             公共类
|   |   |   ├── constant                常量
|   |   |   ├── envUtil                 依赖于测试、生产环境的配置和方法
|   |   |   ├── request                 http请求的封装
|   |   |   ├── interceptors            针对http请求的response的处理
|   |   |   ├── utils                   常用公共方法的封装
|   ├── pages 
|   |   ├── index                       index 页面目录
|   |   |   ├── index.js                index 页面逻辑
|   |   |   └── index.css               index 页面样式
|   ├── services                        用于存放http请求的api
|   ├── app.css                         项目总通用样式
|   └── app.js                          项目入口文件
└── package.json

  1、dist:编译后生成的代码,具体参考官方文档。

  2、config:脚手架生成,这个配置是整个应用的全局的配置,具体参考官方文档。 这里用到了别名的配置alias,可以避免书写多级相对路径。配置别名后,为了让编辑器(VS Code)不报错,并继续使用自动路径补全的功能,需要添加jsconfig.json文件。webstorm中添加自定义配置文件webstrom.config.path.js,并在 setting - languages & Frameworks - JavaScript - Webpack中添加该自定义配置文件;

  3、src:源码。

    3.1、componnet 自定义的全局组件

      DateTimePicker - 日期时间组件(官方组件库中没有时间日期组件)

      GenerateDetail - 可以通过配置生成详情

      PickerSelector - 下拉选择组件

    3.2、lib 公共类封装

      constant - 存放常量,枚举

      envUtil - 与env相关的配置和方法

      request - http请求的封装

      interceptors - 可以通过配置生成详情

      utils - 常用方法的封装

    3.3、page页面:正常业务需求开发的页面。

    3.4、services:接口请求统一在services下管理,可以根据各个模块的不同,分成多个js文件。

3、详细介绍

   这部分重点介绍下lib下的envUtil、request和interceptors。

   3.1、envUtil.js

   envUtil.js主要存放的是与环境相关的配置和方法。Taro框架提供了变量: process.env.NODE_ENV,可以获取当前环境参数,可用于判断是测试、生产环境等。

   通常情况下,在前后端分离模式下进行开发,在代码中,前端请求url一般是相对路径,http请求会存在跨域,常见的解决方法是通过配置nginx服务器的反向代理。在nginx中通过url的关键字做正则匹配后,决定转发到具体的服务器,正常情况下,我们会分别部署测试环境和生产环境,测试环境和生产环境的转发域名会有不同。简单来讲如下:

graph LR

测试环境http请求 --> 测试服务器
graph LR
正式环境http请求 --> 正式服务器

  通过上述简单的介绍,我们了解到,微信小程序如果需要发送http请求成功,需要完成以下两点:

    1、接口服务器可访问

    2、当前环境下的接口请求能够转发到对应环境下的接口服务器

  对于第一个问题,解决方法:在微信公众平台小程序管理后台添加request请求的白名单。针对第二个问题,可以通过process.env.NODE_ENV参数判断当前环境,确定当前需要转发的接口服务器域名,并将相对路径转化为绝对路径,代码如下:

/**
 * 根据环境不同配置http请求的url域名
 * @param url :请求的相对路径
 * @returns {string} :请求的绝对路径
 */
export const getfulllUrl = (url) => {
  let BASE_URL = '';
  if (process.env.NODE_ENV === 'development') {
    //开发环境 - 根据请求不同返回不同的BASE_URL
      if (url.includes('/oilChainGasStation/')) {
      BASE_URL = 'https://xxtest.tf.com';
    } else if (url.includes('/passport')) { 
      BASE_URL = 'https://test.xxx.com';
    }
  } else {
    // 生产环境
      if (url.includes('/oilChainGasStation/')) {
      BASE_URL = 'https://xx.tf.com';
    } else if (url.includes('/passport')) { 
      BASE_URL = 'https://www.xxx.com';
    }
  }
  return BASE_URL + url;
};

  其中/oilChainGasStation/、/passport即为接口关键字,https://xxtest.tf.com与https://xx.tf.com即为对应关键转发对应环境下接口的域名服务器,根据自己的项目情况做更改即可。最后,通过BASE_URL + url将相对路径转化为绝对路径,即为接口请求的全路径。

   3.2、request.js

   request.js是对发送http请求接口的封装,其中Taro.request支持primise的使用。request的核心代码如下:

import interceptors from './interceptors';

interceptors.forEach(i => Taro.addInterceptor(i));
// 添加请求的拦截器
Taro.addInterceptor(Taro.interceptors.timeoutInterceptor);

......

/**
 * 根据环境不同配置http请求的url域名
 * @param url :请求的相对路径
 * @returns {string} :请求的绝对路径
 */
export function request(url, options) {
  //url根据环境参数配置域名;拼接appStoken参数
  url = stitchUrlParam(getfulllUrl(url), parseParamStr({ app_stoken: getAppStoken() }));
  options = Object.assign({}, defaultOpts, options);
  //添加自定义头
  const header = Object.assign({}, options.headers);
  const requestOptions = {
    url,
    header,
    data: options.body,
    method: (options.method ).toUpperCase(),
  };
  return Taro.request(requestOptions);
}

  interceptors也即第三部分要阐述的拦截器。可以使用拦截器在请求发出前或发出后做一些额外操作。

  request即为封装的网络请求方法。首先,对代码的网络请求的相对路径的url做处理变为绝对路径,并拼接当前登录的app_stoken(不同的项目视具体情况而定);其次对用户自定义的header、methods做处理,默认header和method存放在defaultOpts变量中;最后,调用Taro.request方法发起请求;

   实际开发中,修改header参数的情况并不多,最常见的POST请求下修改content-type为form表单提交或者json格式提交。针对上述情况,基于request方法分别封装了requestPost、requestPostJson、requestGet,代码如下:

//POST 请求的http接口 ,默认表单提交
export function requestPost(url, params = {}, type = 'form') {}

//POST 请求的http接口 json方式提交
export function requestPostJson(url, params = {},) {}

// GET 请求的http接口
export function requestGet(url, params = {}) {}

   上述三个方法与request稍有不同,request的参数option是一个对象,包含header部分和body部分(请求参数),而上述三个方法,header部分已经在方法内部实现,调用该方法时,只需关注接口url和接口参数。当然如果上述三个方法不能满足你的开发需求,可自行在request的基础的上封装,或者直接调用request方法。

   3.3、interceptors.js

   在上文中提到过interceptors提供了Taro.request的拦截器。官方解释如下:

在调用 Taro.request 发起请求之前,调用 Taro.addInterceptor 方法为请求添加拦截器,拦截器的调用顺序遵循洋葱模型。

拦截器是一个函数,接受 chain 对象作为参数。chain 对象中含有 requestParmas 属性,代表请求参数。拦截器内最后需要调用 chain.proceed(requestParams) 以调用下一个拦截器或发起请求。

Taro 提供了两个内置拦截器 logInterceptor 与 timeoutInterceptor,分别用于打印请求的相关信息和在请求超时时抛出错误。

   核心代码如下。其中statusCode可用于判断网络请求,此处只针对404、503做错误处理,如不满足需求,可自行根据项目特点添加。statusCode为200时,表示网路请求正常响应,对于具体业务而言,code为0或者200才表示业务接口请求正常,code值根据各自项目特点可配置在specialCode中。对于请求正常的接口,返回data,异常接口抛出Toast提示,并返回Promise.reject,如需对异常请求做额外处理,可在对于接口请求处,通过catch捕获异常。

function customInterceptor(chain) {
    const requestParams = chain.requestParams;
    return chain.proceed(requestParams).then(res => {
        const { statusCode, data } = res;
        const { code, msg, result } = data || {}; // "result" 字段兼容会员老接口

        // 404
        if (statusCode === HTTP_STATUS.NOT_FOUND) {
            _handelErrorByCode('接口请求404,请检查请求url');
            return Promise.reject(res);
        }

        // 503
        if (statusCode === HTTP_STATUS.SERVICE_UNAVAILABLE) {
            _handelErrorByCode('接口请求503, 服务不可访问');
            return Promise.reject(res);
        }

        // 200
        if (statusCode === HTTP_STATUS.SUCCESS) {
            // 特殊码处理了
            if ((specialCode.includes(code)) || result === 'success') {
                return data;
            } else {
                _handelErrorByCode(msg, data);
                return Promise.reject(data);
            }
        }
    });
}

   拦截器提供了对Taro.request请求的处理,针对上一部分中request请求章节,对于header和mothod的处理,也可以通过拦截器实现。

4、踩坑记录

   在使用Taro、Taro-ui过程中,遇到了一些坑,记录下:

   1、key :在列表渲染时,如果确定已经使用了唯一的值作为key,但是在微信小程序开发工具上运行是,仍旧有waring时,可强制转换key为字符串:String(item
.id);

   2、自定义组件的className:自定义组件如果需要通过props传递className到组件中。需要在组件中额外声明externalClasses(变量名不可变),其中my-class可以自行命名,在父组件中,可通过my-class属性传递class,my-class只能是 短横线命名法 (kebab-case),而不是 React 惯用的 驼峰命名法 (camelCase)。

export default class CustomComp extends Component {
  static externalClasses = ['my-class']

  render () {
    return <View className="my-class">这段文本的颜色由组件外的 class 决定</View>
  }
}

export default class MyPage extends Component {
  render () {
    return <CustomComp my-class="red-text" />
  }
}

   3、checkbox样式修改方法:可以通过Label组件包裹checkbox组件,并影藏checkbox,同时自定义选中时的样式,具体的实现可以https://developers.weixin.qq.com/miniprogram/dev/component/label.html。

你可能感兴趣的:(react,其他)