微信小程序&服务号&订阅号

683 阅读9分钟

资源

mpvue允许用vue来开发小程序 但是不能把你之前写的vue项目直接转成小程序

生产环境部署注意事项

1, 小程序生产环境支持https请求,服务器要支持https; 如果所有的接口都是http, 需要服务器做中转, 服务器再调用http

小程序登录、用户信息相关接口调整说明官方

项目开发中的登录流程

image.png

小程序中完整的授权流程

  • 获取用户是否已经授权
`小程序授权 scope 列表`
scope	对应接口	描述
scope.userInfo	wx.getUserInfo	用户信息
scope.userLocation	wx.getLocation, wx.chooseLocation	地理位置
scope.userLocationBackground	wx.startLocationUpdateBackground	后台定位
scope.address	wx.chooseAddress	通讯地址(已取消授权,可以直接调用对应接口)
scope.invoiceTitle	wx.chooseInvoiceTitle	发票抬头(已取消授权,可以直接调用对应接口)
scope.invoice	wx.chooseInvoice	获取发票(已取消授权,可以直接调用对应接口)
scope.werun	wx.getWeRunData	微信运动步数
scope.record	wx.startRecord	录音功能
scope.writePhotosAlbum	wx.saveImageToPhotosAlbum, wx.saveVideoToPhotosAlbum	保存到相册
scope.camera	camera 组件	摄像头

`授权有效期`
一旦用户明确同意或拒绝过授权,其授权关系会记录在后台,直到用户主动删除小程序。

`何时授权?`
在真正需要使用授权接口时,才向用户发起授权申请,并在授权申请中说明清楚要使用该功能的理由。

`注意事项`
wx.authorize({scope: "scope.userInfo"}),不会弹出授权窗口,请使用 <button open-type="getUserInfo"/>
需要授权 scope.userLocation、scope.userLocationBackground 时必须配置地理位置用途说明。

`H5的登录授权流程`
第一步:用户同意授权,获取code
第二步:通过code换取网页授权access_token
第三步:刷新access_token(如果需要)
第四步:拉取用户信息(需scope为 snsapi_userinfo)
附:检验授权凭证(access_token)是否有效

在小程序中分享

用法:
Page({
    onShareAppMessage: function(){
         return {
            title: 'xxxxx', //自定义转发标题
            path: '/page/user?id=123', //分享页面路径
            imageUrl: '/common/images/xxx.png' //分享图片 宽高比 5:4
        }
    }
})
//如果只写成如下形式,title默认是小程序名,path为当前页面路径(不带参数),imageUrl为当前页面截图
Page({
    onShareAppMessage: function(){}
})

➤手动触发方法(一定要在page中先写入上述方法):
点击小程序的胶囊菜单,会从底部弹出转发选项。
<button> 组件 open-type="share" 即 <button open-type="share">,点击后触发。
  • 小程序中加载H5页面的分享,还应该使用小程序的分享,控制分享内容即可
  • JSSDK不可用于小程序中H5页面的分享,因为JSSDK只针对微信打开的网页,自定义微信自带的分享内容和菜单

公众号开发平台相关配置

  • js安全域名不能添加http或https,只添加域名即可.
`js接口安全域名指什么?`
只有在这个域名下面,才能使用你调用的微信js接口,
因为你配置的appid参数,已经证明了你使用哪个公众号,
所以当访问你写的程序时,微信会自动验证安全域名,
如果不是,就使用不了微信的js接口。
  • 生产账户,通过开发者ID及密码获取access_token接口时,需要设置访问来源IP为白名单。

JSSDK用法-微信内网页分享(即公众号才可以分享)

  • JSSDK官网地址
  • 可以使用JSSDK指定显示[点击微信右上角,底部分享弹框中要分享的菜单],也可以自定义分享内容
  • 测试开发的时候可以使用测试账号,包含所有的接口权限,包括JSSDK功能也可以测试开发

生产账户

测试账户

这里是模拟微信服务器和本地项目服务器的验证,点击修改会出现提交按钮,点击提交,
微信服务会给这里指定的本地服务地址URL,推送微信的签名,
后台可以验证自己加密后的签名与微信返回的是否一致,
这里实际上没有什么作用,因为这时的签名没有设置安全域名地址参与。
总结:这里只要配置上后台服务地址和token即可,
前台把APPIDAPPSecretURL传给后台即可,也可以只传URL,让后台写死另外两个参数。
后台按照SHA1加密算法进行加密签名,然后把前端用到的字段返回给前端即可。
(用微信专门的加密签名工具验证是否与后台生成的加密签名一致即可。)
微信加密验证工具: https://work.weixin.qq.com/api/jsapisign
微信JS-SDK是微信公众平台 面向网页开发者提供的基于微信内的网页开发工具包。

配置步骤:
npm install weixin-js-sdk
在使用的vue单页面内引入:import wx from 'weixin-js-sdk'
  • 后台签名验证流程
1,后台用APPIDAPPSecret获取access_token
2,用access_token获取jsapi_ticket
3,后台需要用[时间戳timestamp、随机数nonceStr、临时票据jsapi_ticket和js安全域名url],按照签名算法拼接字符串,并用SHA1加密得到签名.

access_token:
由三个参数获取: grant_type、appid(开发者ID)、secret(即开发者密码appsecret)
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html

jsapi_ticket:
官方解释:生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。

//后台提供接口把加密后的签名以及时间戳、随机串返回给前端,
//注意:不要在前端写死!
//loadurl包含中文或特殊字符时,需要encodeURIComponent()编码, decodeURIComponent()解码
this.$myhttp
          .post('/DzjyServer/wx/getSignature', {
            appid: 'wxa790f2707df4c17b',
            appsecret: 'e687728a8e5aba1389d2948aed4fa900',
            url: localurl
          })
          .then(res => {
            console.log(res.data);
            console.log(res.data.timestamp);
            console.log(res.data.noncestr);
            console.log(res.data.signature);
            wx.config({
              debug: true,                 //部署的时候改为false,测试的时候设为true可输出日志
              appId: 'wxa790f2707df4c17b',   //开发者ID和密码,或测试账户ID和密码都可以
              timestamp: res.data.timestamp, //后台加密前的当前时间戳,参与sha1加密
              nonceStr: res.data.noncestr,   //后台生成的随机串,参与sha1加密
              signature: res.data.signature, //后台sha1加密后的签名字符串
              jsApiList: [  //所有涉及到的功能,需要在这里添加权限,否则无法使用JSSDK功能.
                'updateAppMessageShareData',
                'updateTimelineShareData',
                'openLocation',
                'getLocation',
                'hideMenuItems',
                'showMenuItems',
                'hideAllNonBaseMenuItem',
                'showAllNonBaseMenuItem'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
            });
            wx.ready(function () {
            console.log('准备');
            //验证jsApiList中的功能标签
              wx.checkJsApi({
                jsApiList: [
                  'getLocation',
                  'onMenuShareTimeline',
                  'onMenuShareAppMessage'
                ],
                success: function (res) {
                  // alert(JSON.stringify(res));
                  console.log('checkJsApi:');
                  console.log(res);
                }
              });
              wx.hideAllNonBaseMenuItem(); //隐藏所有非基础菜单功能(微信好友、朋友圈、QQ、QQ空间、微博等都会隐藏)
              //单击微信右上角分享,只显示这里指定的三个菜单
              wx.showMenuItems({
                menuList: [
                "menuItem:copyUrl",
                'menuItem:share:appMessage',
                'menuItem:share:timeline',
                ],
                success:function () {
                  console.log('弹框成功2');
                },
                fail: function () {
                  console.log('弹框失败2');
                }
              })
              
            });
            wx.error(function (res) {
              // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
              console.log('分享失败');
              console.log(res);
            });

          })
          .catch(err => {
            console.log('错误');
            console.log(err);
          });
      },
  • JSSDK拍照上传都是上传到微信服务器,有效期3天,需要后台根据ID从微信服务器同步到项目服务器
//微信内的网页-拍照
wx.chooseImage({
  count: 1, // 默认9
  sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
  sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
  success: function (res) {
    // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
    var localIds = res.localIds; 
  }
});

iOSAPP跳转微信小程序

vue跳转小程序,传递参数

  • npm install weixin-js-sdk
  • 在使用的vue单页面内引入:import wx from 'weixin-js-sdk'
//xxx.vue
<button @click="onCLick">跳转小程序</button>
onCLick() {
    console.log(1111111);
    wx.miniProgram.postMessage({
      data: {
        info: '返回数据了'
      }
    })
    // 注意:这里不返回,不会直接出发小程序的方法, 需要在返回小程序时才触发.
    // 小程序后退、组件销毁、分享,时才触发小程序中的onVueMsg方法
    wx.miniProgram.navigateBack({
      url: '/pages/webview/webview' // 小程序中页面的路径
    })
},

小程序接收参数

<web-view src="{{url}}" bindload="bindload" binderror="binderror" bindmessage="onVueMsg"></web-view>
/*输出从vue中返回的数据*/
onVueMsg: function (e) {
    // 小程序后退、组件销毁、分享,时才触发onVueMsg方法
    /* 当前不会有输出,vue中也没有编写相关代码 */
    console.log(e.detail.data)
},

小程序程序跳转第三方网页

//A页面(mine.js),跳转到B页面(网页容器),并接收参数url,进行网页加载.
wx.navigateTo({
  url: "/pages/webview/webview?url=http://172.16.14.34:8080",
})

//B页面(.wxml)
//注意:url里面的参数不要添加引号,否则路径里面会有多余的引号
<web-view src="{{url}}" bindload="bindload" binderror="binderror"></web-view>

//web-view.js
bindload(e) {
  //网页加载成功时候触发此事件。
  console.log(e.detail); //{src: "https://m.baidu.com/?from=844b&vit=fps"}
},
binderror(e) {
  //网页触发此事件
  console.log(e.detail);
},
/**
* 生命周期函数--监听页面加载
* 跳转页面传过来的url在onLoad()函数中的默认参数options中可以拿到
*/
onLoad: function (options) {
   console.log(options); //{url: "http://www.baidu.com"}
   this.setData({
     url: options.url
   })
},

vue中接收小程序参数

//小程序传递参数
onLoad: function (options) {
    let p = {
      token:'abc',
      name:'mrhan'
    }
    // encodeURIComponent()是为了防止url中参数有中文或特殊字符
    let myurl = this.data.url + "?p=" + encodeURIComponent(JSON.stringify(p));
    console.log(myurl);
    this.setData({
      url: myurl
    })
},
//vue接收参数
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger') {    //判断是否是微信环境
    this.$toast('来自小程序')
    let params = decodeURIComponent(window.location.href).split('?')[1].split('=')[1];
    params = params.replace('#/',''); //替换小程序默认添加的后缀#/
    console.log(params);
    let p = JSON.parse(params);
    console.log(p.token);
}else {
    this.$toast('来自网页')
}

获取用户授权状态

/* 
      获取微信用户授权状态
      scope	                对应接口描述
      scope.userInfo		用户信息
      scope.userLocation		地理位置
      scope.userLocationBackground		后台定位
      scope.address		通讯地址(已取消授权,可以直接调用对应接口)
      scope.invoiceTitle	发票抬头(已取消授权,可以直接调用对应接口)
      scope.invoice		获取发票(已取消授权,可以直接调用对应接口)
      scope.werun		微信运动步数
      scope.record		录音功能
      scope.writePhotosAlbum	保存到相册
      scope.camera		摄像头
    */
    wx.getSetting({
      withSubscriptions: true,
      success(params){
        console.log(`获取授权状态成功:`);
        console.log(params);
        console.log(params.authSetting['scope.address']); //这里不能用.获取
      },
      fail(error){
        console.log(`获取授权状态失败:`);
        console.log(error);
      } 
    })

todo

  • 小程序如何使用npm添加依赖?
  • 网络请求,响应拦截,处理token失效等问题
  • 如何组件化?
  • 在组件中各自进行网络请求?˛
  • 下拉刷新,怎么刷新页面中多个网络请求?
  • 跨页面通信,传值
  • 启动图怎么设置
  • 小程序跳转h5页面
  • 跨页面通讯传值

小程序如何引入WeUI组件库

引入组件
方式1,通过 useExtendedLib 扩展库 的方式引入,这种方式引入的组件将不会计入代码包大小。
方式2,可以通过npm方式下载构建,npm包名为weui-miniprogram

推荐使用方式1, 目前支持以下项目:
kbone: 多端开发框架
weui: WeUI 组件库

//具体步骤:
//app.json中引入即可
"useExtendedLib": {
    "kbone": true,
    "weui": true
  }
}
//在使用weui组件的页面.json, 引入即可
{
  "usingComponents": {
    "mp-dialog": "weui-miniprogram/dialog/dialog"
  }
}
//在页面.wxml中使用
<mp-dialog title="我是弹框标题" show="{{true}}" bindbuttontap="tapDialogButton" buttons="{{[{text: '取消'}, {text: '确认'}]}}">
    <view>我是弹框内容</view>
</mp-dialog>

注意事项

  • 配置JS接口安全域名时,注意是 域名 不需要填写http/https
如果填写的话,在代码执行wx.config()时,会报 config:fail,Error: invalid url domain 错误,
正确示例:www.whongbin.cn
  • 小程序代码修改后,需要重启真机调试,否则最新代码无效果
  • 在.wxml文件中不能调用方法,可以在双括号中拼接变量
  • 在.wxml文件中,不管是便签属性,还是标签之间,使用变量要使用{{变量}}
<a href="{{baiduURL}}">{{baiduURL}}</a>
  • 在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。
  • 项目路径介绍:
../表示上一级目录;
./ 表示同级目录;
/  表示根目录;
  • app.json中"style": "v2",表示项目采用新版本2.x的效果
  • var和let的区别
1.使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象;
2.使用let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升;
3.let不允许在相同作用域内,重复声明同一个变量。
for (var i = 0; i <10; i++) {  
    setTimeout(function() {  // 同步注册回调函数到 异步的 宏任务队列。
         console.log(i);        // 执行此代码时,同步代码for循环已经执行完成
      }, 0);
}
// 输出结果10

// i虽然在全局作用域声明,但是在for循环体局部作用域中使用的时候,变量会被固定,不受外界干扰。
for (let i = 0; i < 10; i++) { 
  setTimeout(function() {
    console.log(i);    //  i 是循环体内局部作用域,不受外界影响。
  }, 0);
}
// 输出结果:
0  1  2  3  4  5  6  7  8 9

navigator

  • 不支持跳转第三方网络连接(百度之类的不行哦)
  • 支持跳转项目内页面, 还有其他小程序

布局

  • 实践经验总结:
  • 不要在标签text上设置padding或margin,容易不生效,在父容器view设置即可
  • 通常在app.wxss中会设置flex的布局方向为垂直布局column
  • 标签scroll-view中内容要使用flex布局,需要设置enable-flex="true"
  • 如果出现padding、margin都为0,但渲染还是出现了位置偏移,这俩货设置flex-start试试
   align-items: flex-start;
   justify-content: flex-start;
  • width、height设置100%时,会导致padding、margin失效的情况,这时候要添加box-sizing:border-box;
  • font-size的单位是px而不是rpx
font-size: 10px
  • 父容器通常要设置width:100%
  • 在style中使用变量
style="height:{{winHeight}}px;"
  • 标签swiper可以通过current属性, 控制标签swiper-item(可以是轮播的图片,也可以是整个页面)

  • 标签swiper和标签scroll-view,最好都要设置width:100%,不然会受外界容器flex布局导致无法显示,有时候不设可能也显示,最好加上约束宽度是屏幕宽.

  • 标签scroll-view,如果要使用flex布局,需要设置enable-flex="true"

  • 横向布局文字多行时,顶部对齐; 需要给文字设置固定高度即可 image.png

微信小程序如何获取元素节点信息

//方式一
const child = this.selectComponent(".myswiper");
console.log(child);
queryString 类型
ID选择器:#the-id
class选择器(可以连续指定多个):.a-class.another-class
子元素选择器:.the-parent > .the-child
后代选择器:.the-ancestor .the-descendant
跨自定义组件的后代选择器:.the-ancestor >>> .the-descendant
多选择器的并集:#a-node, .some-other-nodes

//方式二
let query = wx.createSelectorQuery()
let queryString = '.weui-search-bar__text'
let queryNode = query.selectAll(queryString)
console.log(queryNode);

自定义组件

注意事项:

  • 在父组件wxss中不应使用ID选择器、属性选择器和标签名选择器。

创建自定义组件步骤:

  • pages下面创建components文件夹
  • components下创建组件my-swiper(一个自定义组件由 json wxml wxss js 4个文件组成)
  • 在my-swiper.json中,将 component 字段设为 true
  • 在父组件.json中,引入子组件页面模板的json文件显式定义,否则会被当作一个无意义的节点
{
  "usingComponents": {
    "my-swiper" : "/pages/components/my-swiper/my-swiper"
  }
}
  • 在my-swiper.js中,创建Component({})
  • 生命周期函数、页面周期函数、属性监听器、父传子数据、子调用父方法, Demo如下👇
Component({
  properties: {
    //父组件传递的图片数据
    carouselData: {
      type: Object,
      value: {}
    },
    //图片的前缀
    carouselPath: {
      type: String,
      value: ''
    }
  },
  data: {
    // 这里是一些组件内部数据
    someData: {
      'name': 'mrhan'
    },
  },
  // 监听父组件传递的属性、子组件属性的变化(包括对象的属性变化也会监听到)
  //数据监听器监听的是 setData 涉及到的数据字段,即使这些数据字段的值没有发生变化,数据监听器依然会被触发。
  //如果在数据监听器函数中使用 setData 设置本身监听的数据字段,可能会导致死循环,需要特别留意。
  //从小程序基础库版本 2.6.1 开始支持。
  observers: {
    'carouselData': function (subfield) {
      // 使用 setData 设置 this.data.some.subfield 时触发
      // (除此以外,使用 setData 设置 this.data.some 也会触发)
      console.log('监听类型:' + typeof subfield);
      console.log(JSON.stringify(subfield));
    },
  },

  /* 
    组件的方法需要放在methods对象中
    this.triggerEvent() 类似Vue中的this.$emit()
  */
  methods: {
    clickSwiperItem() {
      console.log('触发swiper点击事件');
      this.triggerEvent('myevent', {
        text: '我是子组件传递的text参数'
      });
    },
  },
  //生命周期函数(推荐在 lifetimes 字段内进行声明,优先级最高,也可以写在最外层.)
  lifetimes: {
    created: function () {
     //组件实例刚刚被创建好时, created 生命周期被触发。
     //此时还不能调用 setData 。 
     //通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。
    },
    attached: function () {
      // 在组件实例进入页面节点树时执行
      console.log(this.properties); //父组件传递给子组件的所有属性
    },
    detached: function () {
     // 在组件实例被从页面节点树移除时执行
    },
    ready: function () {
     // 在组件在视图层布局完成后执行
    },
  },
  
  //组件所在页面的生命周期函数
  pageLifetimes: {
    show: function() {
      // 页面被展示
    },
    hide: function() {
      // 页面被隐藏
    },
    resize: function(size) {
      // 页面尺寸变化
    }
  },
})

bus进行跨页面,逆向传值(正向不可以😂)

  • 新建eventBus.js
//eventBus.js
class EventBus{
  constructor(){
    this.task = {}
  }

  on(name, cb){
    console.log('我是on');
    if(!this.task[name]){
      this.task[name] = []
    }
    typeof cb === 'function' && this.task[name].push(cb)
  }

  emit(name, ...arg){
    console.log('我是emit');
    let taskQueen = this.task[name]
    if(taskQueen && taskQueen.length > 0){
      taskQueen.forEach(cb=>{
        cb(...arg)
      })
    }
  }

  off(name, cb){
    let taskQueen = this.task[name]
    if(taskQueen && taskQueen.length > 0){
      let index = taskQueen.indexOf(cb)
      index != -1 && taskQueen.splice(index, 1)
    }
  }

  once(name, cb){
    function callback(...arg){
      this.off(name, cb)
      cb(...arg)
    }
    typeof cb === 'function' && this.on(name, callback)
  }
}

export default EventBus
  • 使用方法
//A页面
wx.$bus.on('urlParams', (params)=>{
  console.log('on监听到:');
  console.log(params); //5201314
})
//B页面
/**
* 生命周期函数--监听页面加载
* 从跳转页面中传过来的url在options中可以拿到
*/
onLoad: function (options) {
    wx.$bus.emit('urlParams',5201314)
},

页面之间传值的三种方式

1、URL传值


这种方式最常用,比如:

wx.navigateTo({
  url: '../detail/detail?cid='+cid+'&access_token='+access_token
})
这里面直接通过跳转页面的URL进行传值,然后在另一个页面进行接收:

onLoad: function (opt) {
  console.log('cid =' + opt.cid);
  console.log('access_token =' + opt.access_token);
}
这种传值方式只适合值比较少的时候使用,传值比较多的时候,还是建议写本地缓存~

2、本地缓存

小程序API提供了本地缓存数据的API,默认可以缓存10M的数据,如下:

wx.setStorageSync('checkin', checkin);
checkin是一个object,在需要的页面直接调用wx.getStorageSync即可获取,这样就解决了传值较少的问题了。

3、全局APP

其实还有第三种方式,就是全局APP变量。app.js和app.wxss中的代码都是全局生效的,所以我们可以利用这一点儿,在不同页面之间进行传值。

App({
  onLaunch: function () {
    
  },
  globalData: {
    host: 'https://api-xcx-qunsou.weiyoubot.cn/xcx',
    // 版本升级时这里的version加1并替换versionFeature的文案即可
    version: 2,
    versionFeature: '更新说明'
  }
})

父组件给子组件传值 & 子组件调用父组件事件 & 父组件获取子组件属性 & 父组件调用子组件方法

//父组件.wxml
//bind:myevent 可以写成bindmyevent, 
//carouselPath、carouselData是父组件传给子组件的值
//父组件给子组件指定ID,目的是获取子组件所有的属性和方法
<my-swiper id="myswiperID" bind:myevent="bindTapLog" class="w-100" carouselPath="{{carouselPath}}" carouselData="{{carouselData}}"></my-swiper>

//父组件.js
Page({
  bindTapLog() {
    console.log('父组件-事件-被触发!')
    this.getChildComponent();
  },
  // 获取子组件的节点(方便直接操作子组件属性和方法)
  getChildComponent: function () {
    const child = this.selectComponent("#myswiperID");
    child.parentTriggerChild(5211314); //调用子组件方法,并传递参数
    console.log('child:')
    console.log(child);
  },
})
//子组件.wxml
<!-- carouselData有值时,再渲染轮播图 -->
<view class="w-100" wx:if="{{carouselData}}">
  <swiper class="w-100" indicator-dots='true' indicator-active-color="white" autoplay="true">
    <block wx:for="{{carouselData}}" wx:key='{{index}}'>
      <!-- 子组件每张轮播图的点击事件方法clickSwiperItem -->
      <swiper-item bindtap="clickSwiperItem">
        <image style="width:100%;height:300rpx" mode="aspectFill" src="{{carouselPath+item.articleid}}"></image>
      </swiper-item>
    </block>
  </swiper>
</view>

//子组件.js
/* 
组件的方法需要放在methods对象中
this.triggerEvent() 类似Vue中的this.$emit()
*/
methods: {
    //子组件点击轮播图
    clickSwiperItem() {
      console.log('触发swiper点击事件');
      this.triggerEvent('myevent', {
        age: 29
      });
    },
    //父组件触发子组件方法
    parentTriggerChild(params) {
      console.log('父组件直接调用子组件方法,传递参数:');
      console.log(params);
    }
},

wx:for、wx:key、wx:if、wx:else、标签block应用场景

  • wx:for默认会有两个参数index(数组中对象的坐标)和item(数组中遍历的对象)
  • wx:key 来指定列表中项目的唯一的标识符。
wx:key 的值以两种形式提供:
1, 唯一的字符串
代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
2, 保留关键字 *this 
代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
  • 如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
  • 标签block与wx:for一块使用,在代码块内容使用wx:if和wx:else
  • wx:if的优先级比wx:for优先级高,所以不要在一个标签中同时使用wx:for和wx:if
      <block wx:for="{{superviseData}}">
        <view class="scroll-view" wx:if="{{index==0}}">
          {{index}}
          <image class="scroll-image" src="{{item.img}}"></image>
          <text class="scroll-text">{{item.title}}</text>
        </view>
        <view class="scroll-view2" wx:else>
          {{index}}
          <image class="scroll-image" src="{{item.img}}"></image>
          <text class="scroll-text">{{item.title}}</text>
        </view>
      </block>

标签动态添加多个class

  • 没有用到变量的不需要使用{{}},双括号中的class要添加单/双引号
<view class="class3 {{index==0?'class1':'class2'}}">hello world</view>

image.png

常见问题

  • 网络请求超时 失败:{"errMsg":"request:fail timeout"} image.png
解决方案:
是因为wx.request()中的timeout单位是毫秒(ms), 1秒 = 1000毫秒
把timeout设大一些,比如40000ms(即40秒)
问题场景:前端vue、postman登录获取数据正常,
小程序请求成功返回状态码200证明网络访问通了,但提示账号密码错误
小程序项目打印请求路径、参数都正确,
最终确定问题,是后台接收的参数因编码问题导致账号密码参数不正确。
真TM无语...偏偏只有小程序请求有问题。

➤解决方案:
在网络请求的header(小程序)或headers(weex、vue)中设置
'Content-Type':'application/x-www-form-urlencoded;charset=utf-8'

注意:
以后开新项目时,Content-Type的类型要和后台同事确认好。
  • application/json
前后端接口交互采用json格式
  • application/x-www-form-urlencoded
作为表单提交中默认的类型,其表明数据以标准的编码格式被编码为键值对。
数据被编码成以 '&' 分隔的键-值对, 同时以 '=' 分隔键和值. 
非字母或数字的字符会被 percent-encoding: 这也就是为什么这种类型不支持二进制数据的原因 (应使用 multipart/form-data 代替).
  • multipart/form-data
一般用于涉及文件的表单提交,用于post请求
格式:Content-Type: multipart/form-data; boundary=aBoundaryString
其中boundary指明了请求体中每部分的分割符(对于multipart的类目,都会存在该字段,以区分请求体中数据的分割),其请求体中则是对应表单每部分的内容。每部分都会有自己的请求体和相关内容。
  • application/javascript application/x-javascript text/javascript
对于js文件,常见MIME类型为text/javascript
传统的js程序对应的MIME类型为text/javascript,其他的还有"application/x-javascript"(x前缀表示这是实验性类型), 因为text/javascript是最常见的类型,所以RFC4329定义了“text/javascript”。不过,js文件实际上并不是真正的文本类型,因此推出了application/javascript类型,不过现行的支持性并不好,所以常常会用application/x-javascript来代替。
  • application/zip application/gzip
zip 对应zip压缩文件,gzip是若干种文件压缩程序的简称,通常指GNU计划的实现,此处的gzip代表GNU zip。
  • application/http
此类型包含的http请求包含在binary消息体中,在邮件协议传输中需要指明Content-Transfer-Encoding

全局替换字符串

  • 替换字符串中所有的空格
//替换全部空格
var reg = new RegExp(' ','g');
var account = this.data.l_account.replace(reg,'');

//替换第一个空格
var account = this.data.l_account.replace(' ','');

//无效
var account = this.data.l_pwd.trim();

隐藏导航

//在.json文件中添加
"navigationStyle": "custom"

隐藏导航的返回按钮

方式1: 使用redirectTo和switchTab跳转,导航左侧会有home按钮可隐藏,但返回需要刷新tabbar页面

wx.reLaunch({
    url: '/pages/login/login',
    success() {
      if (wx.canIUse('hideHomeButton')) { //从基础库 2.8.3 开始支持
        wx.hideHomeButton(); //隐藏下一页导航的home按钮
      }
    }
})

方式2: 还是保留返回按钮吧, 想其他优化方案.

bintap传递参数

bindtap用于给标签添加点击事件, bindtap的值, 只能是方法名, 不能直接传递参数;
通过data-xxx来传递参数, xxx就是参数名
//.wxml
<view>
  <view style="background-color:red; height:80rpx" bindtap="clickBtn" data-name="张三" data-age="29">
    <view>测试1</view>
  </view>

  <view style="background-color:green; margin-top:100rpx; height:80rpx" >
    <view bindtap="clickBtn" data-name="李四" data-age="92">测试2</view>
  </view>
</view>

//.js
clickBtn:function(e){
  console.log(e)
  //推荐使用currentTarget, 因为如果view(点我)下面有个父view2, 点击
  console.log(e.currentTarget.dataset.name)
  console.log(e.currentTarget.dataset.age)
  console.log(e.target.dataset.name)
  console.log(e.target.dataset.age)
},

image.png

本地存储

清空本地所有缓存: 注意该方法没有参数, 只能全部清空, 不能指定key进行清空, 可以自己set为空串

wx.clearStorageSync();  //清空全部缓存
wx.setStorageSync('token','');  //清空token为''

image适配模式推荐

默认模式为:scaleToFill

//推荐使用aspectFill
<image class="img" mode="aspectFill" src="{{carouselPath+item.articleid}}"></image>

APP可视化页面的高度

  • 可视化高度 = 屏幕高度- 状态栏高度-导航高度-tabbar高度
倘若启动页没有TabBar,此时的可视化高度,没有计算TabBar的高度

通过style动态设置scroll-view宽和高

  • scroll-view必须要设置高度, 且高度<=可视化高度,否则会出现在任意位置下拉刷新都触发下发刷新事件
//index.js
<scroll-view style="width: 100%; height: {{windowH}}"></scroll-view>
Page({
  data: {
    windowH: '',
  }
})
//生命周期函数--监听页面初次渲染完成
onReady: function () {
    let res = wx.getSystemInfoSync(); //当前页面加载完成后, 再获取当前设备的信息(包括可视化高度)
    this.setData({
      windowH: res.windowHeight + 'px' //不要忘记拼接px, 会导致无效
    })
    console.log('--------------------------------系统可视化高度2:');
    console.log(res); //设备信息
    console.log(res.windowHeight); //可视化高度
},

image.png

报错集合

//起因:
在小程序的代码中使用到了asyncawait

image.png

Promise之async、await用法

this.requestHomeURL(); //输出11,22,33 
async requestHomeURL() {
    //轮播图
    await http.getCarouselImage_url().then((data) => {
      console.log('11');
      this.setData({
        carouselData: data.body
      })
    })

    //大类待办
    await http.getBigClass_url().then((data) => {
      console.log(`大类待办data:${JSON.stringify(data)}`);
      console.log('22');
      this.setData({
        todoData: data
      })
    })
    console.log('33');
  },

Promise.all用法

  this.requestHomeURL(); //输出11,22,33 
  async requestHomeURL() {
    //轮播图
    const h1 = http.getCarouselImage_url().then((data) => {
      console.log('11');
      this.setData({
        carouselData: data.body
      })
    })

    //大类待办
    const h2 = http.getBigClass_url().then((data) => {
      console.log(`大类待办data:${JSON.stringify(data)}`);
      console.log('22');
      this.setData({
        todoData: data
      })
    })

    //注意: 需要把h1和h2放在数组中;
    //等待h1和h2接口都请求完毕, 才会触发回调事件
    Promise.all([h1,h2]).then((data)=>{
      console.log('33');
      this.setData({
        triggered: false
      })
    }).catch((e)=>{
      console.log('44');
      console.log(e);
    })

  },

小程序跳转webview时,导航标题显示小程序设置的导航title, 然后显示xxx, 最后显示vue中document.title设置的标题

  • 原因: vue项目中index.html中title设置了名称
  • 解决方案
vue项目中index.html中title设置空字符串
  • 优化导航title显示的速度
在小程序webview加载vue页面的时候,设置小程序的导航标题.

image.png

动态修改小程序-浏览器中的title

image.png

微信小程序自定义弹框禁止页面滚动

添加自定义的弹出框后,当滚动弹框时时下边的页面也会跟着滚动

解决办法:
在弹出层上添加 catchtouchmove=’true

服务号

  • 服务号、订阅号、小程序是三个账号, 分别有对应的平台
  • 只有绑定了微信用户, 才可以扫码登录, 否则需要账号密码进行登录

自定义菜单&系统菜单开关

image.png

  • 基本配置中启用后, 这里也需要在自定义菜单【详情】中启用,服务号中的菜单才会显示 image.png

image.png