開發那點事(十)組件化開發微信小程序案例

開發背景
最近在優化一個兩三年的老項目,奈何UI小姐姐出的圖太高大上,用了好久的微信自帶網絡加載api,沒法滿足需求,但是在每個頁面上寫太多相同的setData太low,於是下定決心封裝一個。

插件實現功能

  • 1 網絡加載框
  • 2 選擇對話框
  • 3 吐司對話框

本文要點

  • 1 插件效果演示(amazing)
  • 2 插件集成使用(import)
  • 2 小程序插件的開發與使用(component)
  • 3 基於插件相關功能Util類封裝(loadToastUtil)
  • 4 總結(summary)

上乾貨

插件效果演示(amazing)

插件集成使用(import)

插件下載地址在文末,歡迎下載,歡迎star😄😄😄
在小程序頁面的json文件中引入該插件

home.json

{
  "usingComponents": {
    "jkDialog": "../../view/jk-dialog/jk-dialog"
  }
}

在js文件中引入jkDialogUtil,注意構造方法中的三個參數

home.js

import {
  jkDialogUtil
} from "../../view/jk-dialog/dialogUtil";
let dialogUtil;
//三個參數分別對應page對象,網絡請求基礎url,對應插件id
dialogUtil = new jkDialogUtil(this, 'https://www.baidu.com', '#loadToast');
//調用吐司對話框
dialogUtil.showToast('提示用戶\n換行');
//調用選擇對話框
dialogUtil.showSelectDialog({
      selectTip: '測試',
      selectContent: '測試選擇對話框\njust soso',
    }, (res) => {
      dialogUtil.showToast(res == 'confirm' ? '確認操作' : '取消操作');
    });
//顯示隱藏網絡加載框
dialogUtil.showLoading();
dialogUtil.hiddenLoading();

原理分析

小程序插件的開發與使用(component)
在項目文件中定義view文件夾,用來存放自定義組件
右鍵新建Component,以我的爲例取名爲jk-dialog
這個時候開發者工具就會生成四個相應的文件
話不多說,直接上代碼

jk-dialog.json
json文件,這裏需要注意的就是component字段,聲明這個模塊是一個插件

{
  "component": true, //聲明組件
  "usingComponents": {}
}

jk-dialog.wxml
wxml文件,插件頁面文件,這個分成三個部分,網絡加載框 選擇對話框 吐司對話框,分別用三個boolean變量控制顯隱

<view wx:if="{{showLoading}}" class="load-container">
    <image src="./img/loading.png"></image>
</view>

<view wx:if="{{showSelect}}" class="pop-container">
    <view class="delay-con column-center">
        <view class="delay-title">
            {{selectTip}}
        </view>
        <view class="delay-content column-center">
            <text>{{selectContent}}</text>
        </view>
        <view class="delay-bottom row-center">
            <view data-state="confirm" bindtap="hiddeenSelectDialog" style="border-right:1px solid #d6d6d6">{{confirmText}}</view>
            <view data-state="cancel" bindtap="hiddeenSelectDialog">{{cancelText}}</view>
        </view>
    </view>
</view>

<view wx:if="{{showToast}}" style="background: transparent;" class="load-container">
    <view class="toast-con">
        <text>{{toastContent}}</text>
    </view>
</view>

jk-dialog.wxss
wxss文件,頁面樣式,其中用到了一些動畫效果,有想法的可以看看,雖然我也是在網上找的代碼。😄😄😄

.load-container {
  pointer-events: auto;
  color: #000;
  font-size: 20px;
  z-index: 9999;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.load-container image {
  width: 50px;
  height: 50px;
  animation: loading-rotate 1s linear infinite;
}

@keyframes loading-rotate {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

.pop-container {
  pointer-events: auto;
  color: #000;
  font-size: 20px;
  z-index: 10;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.row-center {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

.delay-con {
  width: 80%;
  border-radius: 25rpx;
  background: white;
  font-size: 32rpx;
  margin-bottom: 200rpx;
  -webkit-animation: fadeleftIn .4s;
  animation: fadeleftIn .4s;
  -webkit-animation-name: popIn;
  animation-name: popIn;
}


.delay-title {
  padding-top: 30rpx;
  padding-bottom: 20rpx;
}

.column-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.delay-content {
  padding-bottom: 30rpx;
  line-height: 45rpx;
  text-align: center;
}

.delay-bottom {
  width: 100%;
  border-top: 1px solid #d6d6d6;
  color: #027bfe;
}

.delay-bottom view {
  width: 50%;
  padding-top: 25rpx;
  padding-bottom: 25rpx;
  text-align: center;
}

.toast-con {
  width: calc(85% - 60px);
  padding-top: 20px;
  padding-bottom: 20px;
  padding-left: 30px;
  padding-right: 30px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.8);
  margin-bottom: 200px;
  color: white;
  text-align: center;
  font-size: 17px;
  -webkit-animation: fadelogIn .2s;
  animation: fadelogIn .2s;
}

@-webkit-keyframes popIn {
  0% {
    -webkit-transform: scale3d(0, 0, 0);
    transform: scale3d(0.5, 0.5, 0.5);
    opacity: 0;
  }

  50% {
    -webkit-animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
    animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
  }

  100% {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
    -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    opacity: 1;
  }
}

@keyframes popIn {
  0% {
    -webkit-transform: scale3d(0, 0, 0);
    transform: scale3d(0.5, 0.5, 0.5);
    opacity: 0;
  }

  50% {
    -webkit-animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
    animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
  }

  100% {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
    -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    opacity: 1;
  }
}

@keyframes fadelogIn {
  0% {
    -webkit-transform: translate3d(0, 100%, 0);
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }

  100% {
    -webkit-transform: none;
    transform: none;
  }
}

@-webkit-keyframes fadelogIn {
  0% {
    -webkit-transform: translate3d(0, 100%, 0);
  }

  100% {
    -webkit-transform: none;
  }
}

jk-dialog.js
js文件,封裝了三個對話框的顯示隱藏

let systemInfo, selectCallBack;
import {
  selectLabel
} from './config'
Component({
  /**
   * 組件的屬性列表
   */
  properties: {
    toastText: {
      type: String,
    }
  },

  /**
   * 組件的初始數據
   */
  data: {
    statusBarHeight: 0,
    showLoading: false,
    showToast: false,
    toastContent: '',
    showSelect: false,
    selectTip: '提示',
    selectContent: '該操作不可逆\n是否繼續',
    confirmText: '確認',
    cancelText: '取消'
  },
  ready: function () {
    let that = this;
    that.initData();
  },
  /**
   * 組件的方法列表
   */
  methods: {
    /*用戶提示對話框
    \n表示換行
     */
    showToast: function (content, duration) {
      let that = this;
      that.setData({
        showLoading: false,
        showSelect: false,
        showToast: true,
        toastContent: content
      });
      let timer = setTimeout((res) => {
        that.setData({
          showToast: false,
        });
        clearTimeout(timer);
      }, duration == undefined ? 1500 : duration)
    },
    /* 展示自定義加載框
     */
    showLoading: function () {
      this.setData({
        showSelect: false,
        showToast: false,
        showLoading: true,
      })
    },
    /* 隱藏自定義加載框
     */
    hiddenLoading: function () {
      this.setData({
        showLoading: false,
      })
    },
    /* 初始化數據
     */
    initData: function () {
      let that = this;
      wx.getSystemInfo({
        success: function (res) {
          console.log(res, 'system');
          if (res.errMsg === 'getSystemInfo:ok') {
            that.setData({
              statusBarHeight: res.statusBarHeight
            });
            res.lang = res.language.indexOf('zh') !== -1 ? 'zh' : 'en';
            systemInfo = res;
          }
        },
      });
    },
    /* 展示選擇對話框
     */
    showSelectDailog: function (params, callBack) {
      let that = this;
      that.setData({
        showToast: false,
        showLoading: false,
        showSelect: true,
        selectTip: params.selectTip != undefined ? params.selectTip : selectLabel[systemInfo.lang]['selectTip'],
        selectContent: params.selectContent != undefined ? params.selectContent : selectLabel[systemInfo.lang]['content'],
        confirmText: params.confirmText != undefined ? params.confirmText : selectLabel[systemInfo.lang]['confirmText'],
        cancelText: params.cancelText != undefined ? params.cancelText : selectLabel[systemInfo.lang]['cancelText'],
      });
      selectCallBack = callBack;
    },

    hiddeenSelectDialog: function (e) {
      let that = this;
      that.setData({
        showSelect: false
      });
      selectCallBack(e.currentTarget.dataset.state);
    }
  }
})

基於插件相關功能Util類封裝(loadToastUtil)
插件頁面基本已經好了,還差一個工具類,調用比較方便。
dialogUtil.js
我是直接封裝成了一個class類,在構造方法中傳入小程序Page對象,baseUrl以及插件id,簡單暴露了一些方法供每一個頁面調用。
需要注意的是小程序中的一個api selectComponent
通過這個獲取到插件對象,以此來調用插件js文件中寫好的方法

const app = getApp();
export class jkDialogUtil {
  /* 
  構造方法 
  that:page對象
  baseUrl:網絡請求基礎url
  loadToast:對應插件id
  */
  constructor(that, baseUrl, loadToast) {
    this.that = that;
    this.baseUrl = baseUrl;
    this.loadToast = that.selectComponent(loadToast);
    if (this.loadToast != null)
      console.log('插件加載成功');
    else
      console.log('插件加載失敗');
  }

  /*展示網絡加載框  */
  showLoading() {
    if (this.loadToast != null)
      this.loadToast.showLoading();
  }
  /*隱藏網絡加載框  */
  hiddenLoading() {
    if (this.loadToast != null)
      this.loadToast.hiddenLoading();
  }
  /**
   * 網絡請求
   *let params = {
              url: '/authorization/v1/token/decode',
              data: data,
              method: 'POST',
              isCommon: true,
              isLoad: true
            }
          successCallback:成功回調
          errorCallback:失敗回調
   */
  baseRequest(params, successCallback, errorCallback) {
    if (this.loadToast == null) {
      return;
    }
    let that = this;
    let realUrl = that.baseUrl + params.url;
    if (params.type == 'realUrl') {
      realUrl = params.realUrl;
    }
    if (params.isLoad) {
      that.loadToast.showLoading();
    };
    let token = wx.getStorageSync('accessToken');
    let header = {
      'Authorization': 'Bearer ' + token,
      'X-TrackingId': that.getXTrackingId() + ''
    };
    if (params.header != undefined) {
      header = Object.assign({}, params.header, header);
    }
    wx.request({
      url: realUrl,
      method: params.method,
      header: header,
      data: params.data,
      success: res => {
        if (params.isLoad) {
          that.loadToast.hiddenLoading();
        }
        if (res.data.responseCode === undefined) {
          that.baseToast('未知錯誤');
          return;
        }
        if (res.data.responseCode === -2) {
          wx.login({
            success: res => {
              jKLogin(res.code, function () {
                that.baseRequest(params, successCallback, errorCallback)
              })
            }
          });
          return;
        }
        if (!params.isCommon) {
          successCallback(res.data);
          return;
        }
        if (res.data.responseCode != undefined) {
          if (res.data.responseCode === 0) {
            successCallback(res.data.data);
            return;
          } else {
            that.baseToast(res.data.responseMessage);
            errorCallback == undefined ? '' : errorCallback(res.data);
          }

          return;
        }

        baseToast('未知錯誤');
      },
      error: res => {
        that.loadToast.hiddenLoading();
        console.log(res, 'error');
        that.baseToast('未知錯誤')

      }
    })

  }
  /*展示選擇對話框  */
  showSelectDialog(params, callBack) {
    if (this.loadToast != null)
      this.loadToast.showSelectDailog(params, callBack);
  }
  /* 展示吐司對話框 */
  showToast(content) {
    if (this.loadToast != null)
      this.loadToast.showToast(content);
  }
  baseToast(content) {
    wx.showToast({
      title: content,
      icon: 'none',
      duration: 3000
    })
  }

  getXTrackingId() {
    let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
    let XTrackingId = "";
    for (let i = 0; i < 32; i++) {
      let id = parseInt(Math.random() * 61);
      XTrackingId += chars[id];
    }
    return XTrackingId;
  }

}

源碼地址點我前往
總結(summary)
在工具類中其實還封裝了baseRequest的網絡請求方法,大家可以按照具體需求自行修改。
沒bug 祝好運 ~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章