微信小程序之长表单页面优化

作者: 甚时跃马归来 | 来源:发表于2019-04-25 20:38 被阅读2次

    1、前言

    长表单录入信息在业务系统中是非常常见的一个场景,然而长表单加业务系统,那流畅度,简直不忍直视。所以趁着现在空闲,想了一些关于长表单页面的优化方案。

    2、优化方案一: 减少绑定方法

    我们平时写小程序页面的时候,可能会不太注意,会有这样的写法。

    <!-- html页面 -->
    <view class='content2'>
        <view class='item'>
          <text class='item_title'>姓名</text>
          <input  type='text' value ='{{name}}' bindinput ="getName"></input>
        </view>
        <view class='item'>
          <text class='item_title'>手机号</text>
          <input type='text' value ='{{phone}}' bindinput ="getPhone"></input>
        </view>
    </view>
    
    
    // js页面如下
     getName(e){
        this.setData({
          name : e.detail.value
        })
      },
    
      getPhone(e){
        this.setData({
          name: e.detail.value
        })
      }
      // 其他 ....
    

    表单少的时候还好,如果你想一下业务系统中,有些页面有2/3十个字段呢?难道每个input都给一个相应方法吗?

    不仅代码重复多,而且js页面中定义的方法过多占用内存、占用体积、而且还导致代码多阅读困难等,而且哪一天如果要对输入的内容做检查处理,就算可以把检查的代码抽象出来,难道还要调用二三十次?

    当然,上一种方法也不是没有优点,优点就是:职责单一,哪天不小心把某个方法改bug也不影响其他的使用。

    但从性能方面考虑,我们就可以对某一类相似的输入方法就行优化,绑定同一个方法,像下面这样:

    <!-- html页面 -->
    <view class='content2'>
        <view class='item'>
          <text class='item_title'>姓名</text>
          <input  type='text' value ='{{name}}' data-type="name" bindinput ="getValue"></input>
        </view>
        <view class='item'>
          <text class='item_title'>手机号</text>
          <input type='text' value ='{{phone}}' data-type="phone" bindinput ="getValue"></input>
        </view>
    </view>
    
    
    // js页面如下
      getValue(res){
        let type = res.currentTarget.dataset.type;
        switch(type)
        {
          case 'name':
            this.setData({
              name : res.detail.value
            })
          break;
    
          case 'phone':
            this.setData({
              phone : res.detail.value
            })
          break;
    
          // 其他...
    
          default:
          break;
        }
      },
      // 其他 ....
    

    3、优化方案二: 不使用this.setData

    在方案一中,我们主要针对长表单页面进行了简单的优化,而事实上你优化后运行,也感觉不出性能有什么提升,但从逻辑上来说,性能确实提升了一些,只是效果不明显而已。

    而方案二,才是优化的重点。

    想想,我们在vue中一般都是使用双向数据绑定,vue帮我们处理了许多逻辑。而在小程序中,页面并不是双向数据绑定的,而且由于小程序中获取不到dom节点,我们无法在点提交的时候才去获取各个input里的值,所以我们一般都会习惯性地把各个input的值存入data中,每次存数据到data中都要调用this.setData这个方法,并且由于小程序双线程的构架,this.setData调用后,js逻辑线程就会通知渲染线程去渲染数据更新页面,而2个线程之间的通信及渲染层渲染才是造成性能下降的罪魁祸首。

    比如,输入名字中,我输入一个: 任 字。

    由于使用拼音的缘故,我要输入 ren,然后选择 任 字。你在getValue()方法中打印一下,看getValue()被调用了多少次?

    4次!!!

    不仅是getValue()被调用了4次,this.setData()也被调用了4次!!

    如果我要输入的内容超多,打字速度超快(20多年的手速...),然后你就会看到,页面卡顿...

    然后分析我们的wxml代码,发现有一行 value = '{{name}}' 。

    嗯??? 我们为什么要设置这一行代码?为了使页面所渲染的数据和data中保持同步!但本身input数据是刚从渲染层(Input是原生组件,实际是native层,不过这不重要)传递到逻辑层,数据本身就是同步的了!!!

    所以,对于input这种输入性标签,我们只需要保存数据就好了!而不用多此一举再次通知渲染层渲染!!!

    如果我们不需要通知渲染层渲染,那就不需要用data来存储数据,那么代码将会变成下面这样:

    <!-- html页面 -->
    <view class='content2'>
        <view class='item'>
          <text class='item_title'>姓名</text>
          <input  type='text' data-type='name' bindinput ="getValue"></input>
        </view>
        <view class='item'>
          <text class='item_title'>手机号</text>
          <input type='text'  data-type='phone' bindinput ="getValue"></input>
        </view>
        <view class='item'>
          <text class='item_title'>身份证</text>
          <input  type='text' bindinput ="getBankName"></input>
        </view>
    </view>
    
    
    //js页面如下
    data:{
    
    },
    
    // 这里才是核心!!!
    // 本质上Page()是一个方法,传进来的是一个对象,
    // 那么既然能存在data,肯定也可以使用自定义mydata来存数据啦。
    mydata:{
      phone:'',
      name:'',
      cardId:'',
      barnchName:'',
      bankCardNo:'',
      linkName:'',
      linkShip:'',
      linkPhone:''
    },
    getValue(res){
        let type = res.currentTarget.dataset.type;
      // 如果你不需要对输入的内容各自处理的话,甚至还可以这样做,
       // 这样就可以去掉裹脚布一样的switch了
       // this.mydata[type] = res.detail.value;
        switch(type)
        {
          case 'name':
            this.mydata.name = res.detail.value
          break;
    
          case 'phone':
            this.mydata.phone = res.detail.value
          break;
    
          default:
          break;
        }
      },
    
    

    在这份代码中,主要使用了mydata来存储数据,页面显示的就是用户输入的数据,不过我们把数据备份了一份,同时切断了他们之间的“同步”。就这样,没有了“同步”,没有了this.setData(),也没有了卡顿。

    然后在下一步的方法中,打印一下数据,发现数据也能完整取到,如下图:

    image.png

    4、完整代码

    <!-- html -->
    <!--pages/task/information/information.wxml-->
    <!-- <view class='showTips' animation="{{animation}}">{{message}}</view> -->
    <view class='container2'>
      <view class='pageItem'>
          <!-- <text>1/3</text> -->
          <view class='content2'>
            <view class='item'>
              <text class='item_title'>姓名</text>
              <input  type='text' data-type='name' bindinput ="getValue"></input>
            </view>
            <view class='item'>
              <text class='item_title'>手机号</text>
              <input type='text'  data-type='phone' bindinput ="getValue"></input>
            </view>
            <view class='item'>
              <text class='item_title'>身份证</text>
              <input  type='text' data-type='cardId' bindinput ="getValue"></input>
            </view>
            <!-- <view class='item'>
              <text class='item_title'>开户行</text>
               <select prop-array='{{myBankArr}}' bind:myget='getBank'></select> 
            </view> -->
            <view class='item'>
              <text class='item_title'>支行名称</text>
              <input  type='text' data-type='branchName' bindinput ="getValue" ></input>
              
            </view>
            <view class='item'>
              <text class='item_title'>银行卡号</text>
              <input  type='text' data-type='cardNo' bindinput ="getValue"></input>
              
            </view>
            <view class='item'>
              <text class='item_title'>紧急联系人</text>
              <input  type='text' data-type='linkName' bindinput ="getValue"></input>
              <!-- <text class='tips_1'>*</text> -->
            </view>
            <view class='item'>
              <text class='item_title'>联系人关系</text>
              <input  type='text' data-type='linkShip' bindinput ="getValue"></input>
              <!-- <text class='tips_1'>*</text> -->
            </view>
            <view class='item'>
              <text class='item_title'>联系人电话</text>
              <input  type='text' data-type='linkPhone' bindinput ="getValue" ></input>
              <!-- <text class='tips_1'>*</text> -->
            </view>
          </view>
    
          <button bindtap='next'>下一步</button>
      </view>
      <view class='pageItem'>
      </view>
      <view class='pageItem'>
      </view>
      <button bindtap='nextStep'>下一步</button>
    </view>
    
    // js
    // pages/t
    Page({
    
      mydata:{
        phone:'',
        name:'',
        cardId:'',
        barnchName:'',
        bankCardNo:'',
        linkName:'',
        linkShip:'',
        linkPhone:''
      },
      data: {
        isChecked: false,
        animation: {},
        message: '银行卡必须是本人的,用作发佣金',
        myBankArr:[
          {
            "id": 0,
            "text": '中国招商银行'
          },
          {
            "id": 1,
            "text": '中国工商银行'
          },
          {
            "id": 2,
            "text": '中国农业银行'
          },
          {
            "id": 3,
            "text": '中国银行'
          },
          {
            "id": 4,
            "text": '中国建设银行'
          },
          {
            "id": 5,
            "text": '中国邮政储蓄银行'
          }
        ]
      },
    
      onLoad: function (options) {
      },
      onShow: function () {
      },
    
      getValue(res){
        let type = res.currentTarget.dataset.type;
        switch(type)
        {
          case 'name':
            this.mydata.name = res.detail.value
          break;
    
          case 'phone':
            this.mydata.phone = res.detail.value
          break;
    
          case 'cardId':
          break;
    
          case 'barnchName':
          break;
    
          case 'bankCardNo':
          break;
    
          case 'linkName':
          break;
    
          case 'linkShip':
          break;
    
          case 'linkPhone':
          break;
          
          default:
          break;
        }
      },
      next(){
        console.log(this.mydata)
      }
    })
    
    /* css 部分 */
    /* pages/task/information/information.wxss */
    page,view{
      margin: 0;
      padding: 0;
    }
    .showTips{
      position: absolute;
      left: 0;
      top: -70rpx;
      width: 750rpx;
      height: 70rpx;
      line-height: 70rpx;
      text-align: center;
      background-color: #c6d0e3;
      color: #00276f;
    }
    .container2{
      display: flex;
      flex-direction: row;
      justify-content: flex-start;
      align-items: center;
      padding: 0;
      overflow: hidden;
    }
    
    .pageItem{
      width: 750rpx;
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      align-items: center;
      flex-shrink: 0;
    }
    
    .content2{
      width: 100%;
      /* border-top: solid 2rpx #333;
      border-bottom: solid 2rpx #333; */
      padding-left: 20rpx;
    }
    
    .item{
      margin-top: 10rpx;
      padding: 10rpx 10rpx;
      width: 100%;
      /* margin: 25rpx 0; */
      display: flex;
      flex-direction: row;
      justify-content: flex-start;
      align-items: center;
      border-bottom: solid 2rpx #EDEDED;
    }
    .item_title{
      width: 150rpx;
      flex-shrink: 0;
      text-align: right;
      font-size: 28rpx;
    }
    
    .item > input{
      text-align: left;
      font-size: 34rpx;
      height: 60rpx;
      line-height: 60rpx;
      padding: 0 20rpx;
      width: 490rpx;
      /* border-radius: 10rpx; */
      
    }
    
    .item > select{
      text-align: left;
      font-size: 34rpx;
      height: 60rpx;
      line-height: 60rpx;
      padding: 0 20rpx;
      width: 490rpx;
      /* border-radius: 10rpx; */
      /* border-bottom: solid 2rpx #EDEDED; */
    }
    
    .inputPlace{
      font-size: 30rpx;
    }
    
    .tips_1{
      color: red;
      margin-left: 10rpx;
      width: 10rpx;
    }
    
    .tips{
      height: 33rpx;
      line-height: 33rpx;
      margin-top: 180rpx;
    }
    .tips .img image{
      width: 27rpx;
      height: 33rpx;
      margin: 0 10rpx 0 150rpx;
    }
    .tips text{
      color: #1D4692;
      font-size: 25rpx;
    }
    button{
      margin-top: 50rpx;
    }
    .checkbox{
      text-align: center;
      color: #1D4692;
      margin: 50rpx 0;
    }
    .checkbox view,.checkbox navigator{
      font-size: 26rpx;
    }
    .checkbox navigator{
      display: inline;
      text-decoration: underline;
    }
    checkbox .wx-checkbox-input {
      width: 20rpx;
      height: 20rpx;
      margin-right: 30rpx;
    }
    
    

    5、目前我个人就想到这2个优化方案,肯定也还存在其他的方案的,欢迎交流。

    相关文章

      网友评论

        本文标题:微信小程序之长表单页面优化

        本文链接:https://www.haomeiwen.com/subject/cjjtnqtx.html