基於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。

發佈了32 篇原創文章 · 獲贊 27 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章