开发那点事(十)组件化开发微信小程序案例

开发背景
最近在优化一个两三年的老项目,奈何UI小姐姐出的图太高大上,用了好久的微信自带网络加载api,没法满足需求,但是在每个页面上写太多相同的setData太low,于是下定决心封装一个。

插件实现功能

  • 1 网络加载框
  • 2 选择对话框
  • 3 吐司对话框

本文要点

  • 1 插件效果演示(amazing)
  • 2 插件集成使用(import)
  • 2 小程序插件的开发与使用(component)
  • 3 基于插件相关功能Util类封装(loadToastUtil)
  • 4 总结(summary)

上干货

插件效果演示(amazing)

效果演示.gif

插件集成使用(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变量控制显隐


    



    
        
            {{selectTip}}
        
        
            {{selectContent}}
        
        
            {{confirmText}}
            {{cancelText}}
        
    



    
        {{toastContent}}
    

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 祝好运 ~

你可能感兴趣的:(开发那点事(十)组件化开发微信小程序案例)