微信小程序微信小程序开发躺坑之路微信小程序开发

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

2019-04-25  本文已影响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个优化方案,肯定也还存在其他的方案的,欢迎交流。

上一篇下一篇

猜你喜欢

热点阅读