微信小程序项目总结,持续更新

1.踩坑

  • css样式不能引用本地图片资源,只能引用线上资源(background-image),引用本地图片资源只能用<image>标签
  • {{}}不能执行函数方法,{{}}只支持基本的简单运算和ES6拓展运算符。如价格格式化这种常用的处理,只能在js代码中处理好然后再模板中渲染
  • 可以通过wxs模块解决{{}}中不能执行函数的问题。可以做到模拟vue.js中过滤器的功能。
  • 小程序不支持分享链接到朋友圈,暂时的通用做法是生成保存有页面小程序码的图片到本地相册。又用户自行发朋友圈转发。前端可以利用canvas来实现,减轻服务端压力。但是会有图片锯齿不清晰的问题。建议预览图和保存到真机的图片采用不同的尺寸。保存在真机的图片按照750的宽度实现。相比于预览图要大一些,这样保存到手机的图片会清晰很多。
  • 小程序布局采用rpx单位,UI稿按照750的宽度出图。可直接使用UI稿的尺寸。但是在某些机型上1rpx会无法显示。可以用H5的方式实现1px效果。
  • iphoneX吸底按钮的适配,可以用媒体查询获取wx.getSystemInfo获取机型。
  • 其他
    • 页面A -> 页面B,页面B的操作触发了页面A的数据更新。返回更新页面A的数据,通常有两种方式来实现(我司采用了方案二):

    • 在页面A监听onShow事件,在onShow事件触发时无脑更新页面数据。

    • 通过EventBus来实现跨页面通信。

    • 复杂组件的开发,省市区三级联动选择器的开发,获取微信地址库的地址的编码和业务采用的省市区编码对不上。

    • 页面路径的层级,最大不能超过10层。

    • 小程序小程序分包加载,微信对小程序包的大小有如下限制。

      • 整个小程序所有分包大小不超过 8M

      • 单个分包/主包大小不能超过 2M

@media only screen
    and (device-width : 375px)
    and (device-height : 812px)
    and (-webkit-device-pixel-ratio : 3) { }

2.IDE快捷键 ctrl+shift+P 手机自动预览, ctrl+?快捷注释与取消

3.小程序单元测试目前使用 miniprogram-simulate

4.1线上api请求获取到的src图片地址转换base64

  arrayBufferToBase64 (src) {
    let _this = this
    wx.request({
      url: src,
      method: \'GET\',
      responseType: \'arraybuffer\',
      success: function (res) {
        let base64 = wx.arrayBufferToBase64(res.data);
        _this.setData({
          base64String: base64
        })
      }
    })
  }

4.2本地上传图片或照片转base64

  首先用微信提供的API,wx.chooseImage,参考https://developers.weixin.qq.com/miniprogram/dev/api/

  然后结合API,wx.getFileSystemManager

wx.getFileSystemManager().readFile({
          filePath: res.tempFilePaths[0], //选择图片chooseImage返回的相对路径
          encoding: \'base64\', //编码格式
          success: res => { //成功的回调
            that.setData({
              base64String: res.data
            })
            // console.log("ourUrl=" + that.data.ourUrl)
            // console.log("data:image/jpeg;base64," + res.data)
          }
        })

4.3 【走不通了...】本地上传照片或图片获得的src转base64

  由于后端提供了upload接口,首页通过wx.chooseImage选择图片上传至,照片服务器API:upload 返回src

wx.uploadFile({
          url: upload,
          filePath: res.tempFilePaths[0],// wx.chooseImage返回的数据
          name: \'file\',
          header: {
            token: app.globalData.token
          },
          success(res) {
            console.log(res.data)
            that.arrayBufferToBase64(res.data)
          }
        })

5.小程序加载,有时需要 setTimeout 后才有效,例如用户点开分享的小程序,需求立马跳出选择组件。

6.skeleton

7.图片太多加载慢:

  判断用户的设备(主要用在移动端)、网络等,分别加载不同质量的图片(例如高端 iPhone wifi 情况下,就可以加载双倍高清图等,蜂窝网络下面,就加载个单倍或者有损压缩过的)。

  或者先加载低质量的图片,让浏览者可以看到,然后再在后台加载更高清的,等加载完了,浏览者还在观看,就插入替换掉。

  或者先加载低质量小图片列表,然后让用户点击,触发类似 fancybox 的效果,弹窗出现大图片。

  或者利用资源预加载(三个 HTML5 不常见特性简介)当用户还没打开的时候,就开始加载。

  还有好多思路,后面想到再补充。

8.侧滑 https://juejin.im/post/5c63770de51d45272c3fb33a?utm_source=gold_browser_extension

9.setData 复杂的对象

// 比如 marquee: { page: \'../user/lost/lost\', text: \'\' }
// marquee 是变量
this.setData({
      [`${\'marquee\'}.width`]: \'1\',
      bolClose: true,
      [`${\'marquee\'}.text`]: 1

})    

// marquee 是属性
this.setData({
       [\'marquee.text\']: \'1\'
})

10.百度:微信小程序1rpx border ios真机显示不全问题分析及解决方案

11.json 文件配置

window: {
    "backgroundTextStyle": "light",
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "",
    "navigationBarBackgroundColor": "#ffffff",
    "backgroundColor": "#efeff4",
    "disableScroll": true,    // 禁止下拉
    "enablePullDownRefresh": true,      // 下拉刷新
    "navigationStyle": "custom",    // 自定义导航
    "usingComponents": {
        "nav-bar": "../../components/nav-bar/nav-bar"
     }  
    // 待补充
}

12.方法传递参数

// 传递参数只能通过 WXML 上面的属性值来传递,需要在标签上增加 data-xx 这样的属性,可以在 fn 内 event 对象的 target/currentTarget 的 dataset 获取参数。如 
const {name, detail} = e.currentTarget.dataset

13.IDE

VS Code + minapp 插件

传递参数只能通过 WXML 上面的属性值来传递,需要在标签上增加 data-xx 这样的属性,可以在 fn 内 event 对象的 target/currentTarget 的 dataset 获取参数。如 const {name, detail} = e.currentTarget.dataset

14.for, index 的应用

wx:for="{{}}"
wx:for-item="item"
wx:for-index="idx"

15.传参数的data-balaContent,在调用时一律小写 e.currentTarget.dataset.balacontent

16.下拉刷新,触底翻页

init:
pageNo: 1,
loading: true,
hasNextPage: true


 onPullDownRefresh: function () {
    this.setData({
      pageNo: 1,   // 回到第一页
      loading: false,
      empty: false
    })
    this.findOrderPage()
  },


  onReachBottom: function () {
    if (this.data.hasNextPage) {  // 判断当前页pageNo < 总页数pages
      this.setData({
        loading: true,
        pageNo: this.data.pageNo + 1
      })
      this.findOrderPage();
    }
  },

findOrderPage: function () {
    // console.log(this.data.bolReachBottom)
    let _time = -new Date().getTime();
    let _this = this;
    
    wx.request({
      url: findOrderPageUrl,
      data: {
        pageNo: _this.data.pageNo
      },
      header: {
        \'content-type\': \'application/json\',
        token: app.globalData.token
      },
      success: function (res) {
        // console.log(res);
        if (res.data.code == 200) {
          _time += new Date().getTime();
          setTimeout(function () {
            res.data.result.list.forEach(function(value, index){
              value.createTime = formatTime(value.createTime);
              value.totalPrice = formatAmount(value.totalPrice);
              //value.orderNo = value.orderNo.replace(/(\d{4})(?=\d)/g, "$1 ")
            })
            // let tmp = _this.data.list.concat(res.data.result.list);
            let tmp;
            if (_this.data.pageNo > 1) {
              tmp = _this.data.list.concat(res.data.result.list);
            } else {
              tmp = res.data.result.list;
            }
            //console.log(tmp)
            _this.setData({
              list: tmp,
              loading: false,
              hasNextPage: _this.data.pageNo >= res.data.result.pages ? false : true,
              empty: tmp.length > 0 ? false : true,
            })
            _this.setData({
              bolEmptyOrder: _this.data.list.length === 0 ? true : false
            })
            // console.log(_this.data.list)
            wx.stopPullDownRefresh()
          }, _time > 600 ? 0 : 600 - _time);

          if (!_this.data.bolReachBottom) {
            wx.hideLoading()
          }
        } else {
          //_this.findOrderPage()
          wx.stopPullDownRefresh()
        }
      }
    })
  },

17.与页面web、app开发的区别:定时器一定要定义在 data 中,这样在跳转页面的时候执行 onhide 清除 data 里的参数才能真正销毁

18.分享和转发

// 1.每个页面胶囊分享
onShareAppMessage: function (res) {
    let _this = this
    let uid = app.globalData.token || wx.getStorageSync(\'token\') // 分享者的uid
    let path = uid ? `/pages/home/home?source=${uid}` : `/pages/home/home` // 用户来源&任意其他参数
    return {
      title: \'降价拍\',
      path: path,
      imageUrl: imgDef
      // success回调已经废弃
    }
},

// 2.隐藏右上角胶囊里的分享并使用按钮分享
onShow () {
    wx.hideShareMenu()
}

// 3.群分享带分享信息,获取位置,群号,openId等来源,需解密。
// 这段代码将限制群内的二次转发,视情况使用
onLoad: function (options) {
    wx.updateShareMenu({
      withShareTicket: true
    })
},

// 4.群分享后获取withShareTicket,只能在app.js里获取
onLaunch (options) {
    let shareTicket = options.shareTicket
}

// 5.分享给个人的来源source的获取
onLaunch (options) {
    let source = options.query && options.query.source
}
// 6.扩展:群分享shareTicket,app.js 
    // 1044 群分享shareTicket
    // 1007 单人分享
    let source = options.query && options.query.source
    if (source && options.scene === 1007) {
      wx.setStorageSync(\'source\', {
        source: source,
        type: \'person\'
      })
    }
    if (options && options.shareTicket) {
      wx.setStorageSync(\'shareTicket\', options.shareTicket)
      this.globalData.shareTicket = options.shareTicket
    }
  // 7.upload_share_Result 上报方法
  upload_share_Result(res, type, uid) {
    let params = {
      type, // 1.person 2.group;
      uid,    // 记录来源,如果在小程序打开,来源为空;如果从别人分享的打开,来源别人的uid
      encryptedData: res ? res.encryptedData : \'\',    // 分享到群的群消息
      iv: res ? res.iv : \'\'       // 分享到群的群消息
    }
    wx.request({
      url: \'\',
      method: \'POST\',
      data: params,
      header: {
        \'content-type\': \'application/json\',
        token: app.globalData.token
      },
      success() { }
    })
  },
// 8.首页自动上报
onLoad(options) {
    let _this = this
    let shareTicket = wx.getStorageSync(\'shareTicket\')
    if (shareTicket) {
      wx.getShareInfo({
        shareTicket: shareTicket,
        success(res) {
          let source = options && options.source
          console.log(\'source: \' + source)
          if (source) {
            wx.setStorageSync(\'source\', {
              source: source,
              type: \'group\'
            })
            _this.upload_share_Result(res, \'group\', source) // 上报
          }
        }
      })
    } else {
      let sourceCache = wx.getStorageSync(\'source\')
      if (sourceCache && sourceCache.type === \'person\') {
        this.upload_share_Result(null, sourceCache.type, sourceCache.source) // 上报
      }
    }
  },

19.小程序字符串时间的转换

let startDate = \'2020-01-02 09:50:00\'
startDate = startDate.replace(new RegExp("-", "gm"), "/");
startDate = (new Date(startDate)).getTime(); // 转为毫秒