微信小程序常见的基础应用场景

347 阅读14分钟

事件处理

事件绑定和事件对象

//pages/home/home.wxml
<button bindtap="handler">按钮</button>
// pages/home/home.js
Page({
  // 页面的初始数据
  data: {},

  // 事件处理程序
  handler (event) {
    // console.log('我被触发了~~~')
    console.log(event)
  }
    
  // 其他 coding...
})

绑定并阻止事件冒泡

<view bindtap="parentHandler">
  <!-- 使用 bind 绑定的事件,会产生事件冒泡 -->
  <!-- <button bindtap="handler">按钮</button> -->

  <!-- 使用 catcht 绑定的事件,会阻止事件冒泡 -->
  <button catchtap="handler">按钮</button>
</view>

WXML 中冒泡事件列表如下表:

类型触发条件
touchstart手指触摸动作开始
touchmove手指触摸后移动
touchcancel手指触摸动作被打断,如来电提醒,弹窗
touchend手指触摸动作结束
tap手指触摸后马上离开
longpress手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发
longtap手指触摸后,超过350ms再离开(推荐使用 longpress 事件代替)
transitionend会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart会在一个 WXSS animation 动画开始时触发
animationiteration会在一个 WXSS animation 一次迭代结束时触发
animationend会在一个 WXSS animation 动画完成时触发
touchforcechange在支持 3D Touch 的 iPhone 设备,重按时会触发

事件传参-data-*自定义数据

在 wxml 文件中,使用 data-* 属性将数据传递给事件处理函数。例如:

<view bindtap="parentHandler" data-parent-id="1" data-parentName="tom">
  <!-- 如果需要进行事件传参,需要再组件上通过 data- 的方式传递数据 -->
  <!-- <button bindtap="btnHandler" data-id="1" data-name="tom">按钮</button> -->

  <button data-id="1" data-name="tom">按钮</button>
</view>

在 js 文件中,可以通过 event.currentTarget.dataset 获取传递的数据

// cate.js
Page({

  // 按钮触发的事件处理函数
  btnHandler (event) {
    // currentTarget 事件绑定者,也就是指:哪个组件绑定了当前事件处理函数
    // target 事件触发者,也就是指:哪个组件触发了当前事件处理函数
    // currentTarget 和 target 都是指按钮,因为是按钮绑定的事件处理函数,同时点击按钮触发事件处理函数
    // 这时候通过谁来获取数据都可以
    console.log(event.currentTarget.dataset.id)
    console.log(event.target.dataset.name)
  },


  // view 绑定的事件处理函数
  parentHandler (event) {
    // 点击蓝色区域(不点击按钮)
    // currentTarget 事件绑定者:view
    // target 事件触发者:view
    // currentTarget 和 target 都是指 view,如果想获取 view 身上的数据,使用谁都可以

    // 点击按钮(不点击蓝色区域)
    // currentTarget 事件绑定者:view
    // target 事件触发者:按钮
    // 如果想获取 view 身上的数据,就必须使用 currentTarget 才可以
    // 如果想获取的是事件触发者本身的数据,就需要使用 target
    console.log(event)

    // 在传递参数的时候,如果自定义属性是多个单词,单词与单词直接使用中划线 - 进行连接
    // 在事件对象中会被转换为小托峰写法
    console.log(event.currentTarget.dataset.parentId)

    // 在传递参数的时候,如果自定义属性是多个单词,单词如果使用的是小托峰写法
    // 在事件对象中会被转为全部小写的
    console.log(event.currentTarget.dataset.parentname)
  }

})

事件监听-上拉加载更多

小程序中实现上拉加载的方式:

  1. 在 app.json 或者 page.json 中配置距离页面底部距离: onReachBottomDistance;默认 50px
  2. 在 页面.js 中定义 onReachBottom 事件监听用户上拉加载

事件监听-下拉刷新

小程序中实现上拉加载更多的方式:

  1. 在 app.json 或者 page.json 中开启允许下拉,同时可以配置 窗口、loading 样式等
  2. 在 页面.js 中定义 onPullDownRefresh 事件监听用户下拉刷新

scroll-view加载

scroll-view 上拉加载

bindscrolltolower:滚动到底部/右边时触发 lower-threshold:距底部/右边多远时,触发 scrolltolower 事件 enable-back-to-top:让滚动条返回顶部,iOS 点击顶部状态栏、安卓双击标题栏时,只支持竖向

scroll-view 下拉刷新

知识点: refresher-enabled:开启自定义下拉刷新 refresher-default-style自定义下拉刷新默认样式支持设置 black | white | none, none 表示不使用默认样式 refresher-background:自定义下拉刷新区域背景颜色 bindrefresherrefresh:自定义下拉刷新状态回调 refresher-triggered:设置当前下拉刷新状态,(true 下拉刷新被触发,false 表示下拉刷新未被触发,用来关闭下拉效果)

<scroll-view
  scroll-y
  class="scroll-y"

  lower-threshold="100"
  bindscrolltolower="getMore"
  enable-back-to-top

  refresher-enabled
  refresher-default-style="black"
  refresher-background="#f7f7f8"
  bindrefresherrefresh="refreshHandler"
  refresher-triggered="{{isTriggered}}"
  >

  <view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>

</scroll-view>

渲染问题

列表渲染

<view wx:for="{{ animal }}" wx:for-item="itemName" wx:for-index="i">
  {{ itemName.name }} - {{ itemName.avatar }} - {{ i }}
</view>

条件渲染

  1. 使用 wx:ifwx:elifwx:else 属性组
  2. 使用 hidden 属性
<view wx:if="{{condition}}"> True </view>

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

<view hidden="{{condition}}"> True </view>

wx:ifhidden 二者的区别:

  • wx:if :当条件为 true 时将内容渲染出来,否则元素不会进行渲染,通过移除/新增节点的方式来实现
  • hidden :当条件为 true 时会将内容隐藏,否则元素会显示内容,通过 display 样式属性来实现的
<!-- 使用 wx:if、wx:elif、wx:else 属性组控制元素的隐藏和控制 -->
<view wx:if="{{ num === 1 }}">num 等于 {{ num }}</view>
<view wx:elif="{{ num === 2 }}">num 等于 {{ num }}</view>
<view wx:else>大于 2</view>

<view hidden="{{ num !== 1 && num !== 2 && num !== 3 && num < 3}}">
  {{ num < 3 ? 'num 等于' + num : '大于 2' }}
</view>

<button type="primary" bindtap="updateNum">修改数据</button>

Page({
  // 页面的初始数据
  data: {
    num: 1
  },
  
  // 更新数据
  updateNum() {
    this.setData({
      num: this.data.num + 1
    })
  }
    
  // coding...
}
Page({
  // 页面的初始数据
  data: {
    num: 1
  },
  
  // 更新数据
  updateNum() {
    this.setData({
      num: this.data.num + 1
    })
  }
    
  // coding...
}

原生api(wx.方法)

网络请求

wx.request({
  // 接口地址,仅为示例,并非真实的接口地址
  url: 'example.php',
  // 请求的参数
  data: { x: '' },
  // 请求方式
  method: 'GET|POST|PUT|DELETE',
  success (res) {
    console.log(res.data)
  },
  fail(err) {
    console.log(err)
  }
})

要在小程序管理后台进行设置请求的域名,打开微信公众后台:点击左侧 开发 → 开发管理 → 开发设置 → 服务器域名。域名只支持 https 而且要求已备案

原生弹窗组件

loading加载

loading 提示框常配合网络请求来使用,用于增加用户体验,对应的 API 有两个:

  1. wx.showLoading 显示加载提示框
  2. wx.hideLoading 隐藏加载提示框

语法如下:

wx.showLoading({
  title: '提示内容', // 提示的内容
  mask: true, // 是否显示透明蒙层,防止触摸穿透
  success() {}, // 接口调用成功的回调函数
  fail() {} // 接口调用失败的回调函数
})

请求结合例子:

Page({

  data: {
    list: []
  },
  // 获取数据
  getData () {
     // 显示 loading 提示框
     wx.showLoading({
      // 用来显示提示的内容
       // 提示的内容不会自动换行,如果提示的内容比较多,因为在同一行展示
       // 多出来的内容就会被隐藏
       title: '数据加载中...',
       // 是否展示透明蒙层,防止触摸穿透
       mask: true
    })
    // 如果需要发起网络请求,需要使用 wx.request API
    wx.request({
      // 接口地址
      url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
      // 请求方式
      method: 'GET',
      // 请求参数
      data: {},
      // 请求头
      header: {},
      // API 调用成功以后,执行的回调
      success: (res) => {
        // console.log(res)
        if (res.data.code === 200) {
          this.setData({
            list: res.data.data
          })
        }
      },
      // API 调用失败以后,执行的回调
      fail: (err) => {
        console.log(err)
      },
      // API 不管调用成功还是失败以后,执行的回调
      complete: (res) => {
        // console.log(res)
         // 关掉 loading 提示框
         // hideLoading 和 showLoading 必须结合、配对使用才可以
        wx.hideLoading()
      }
    })

  }

})

comfirm选择弹窗

知识点: wx.showToast():消息提示框用来根据用户的某些操作来告知操作的结果,如退出成功给用户提示,提示删除成功等,语法如下:

wx.showToast({
  title: '标题', // 提示的内容
  duration: 2000, // 提示的延迟时间
  mask: true, // 是否显示透明蒙层,防止触摸穿透
  icon: 'success', // 	图标
  success() {}, // 接口调用成功的回调函数
  fail() {} // 接口调用失败的回调函数
})

wx.showModal() 模态对话框也是在项目中频繁使用的一个小程序 API,通常用于向用户询问是否执行一些操作,例如:点击退出登录,显示模态对话框,询问用户是否真的需要退出等等

wx.showModal({
  title: '提示', // 提示的标题
  content: '您确定执行该操作吗?', // 提示的内容
  confirmColor: '#f3514f',
  // 接口调用结束的回调函数(调用成功、失败都会执行)
  success({ confirm }) {
    confirm && consle.log('点击了确定')
  }
})

本地存储

同步 API异步 API作用
wx.setStorageSyncwx.setStorage将数据存储在本地缓存中指定的 key 中
wx.getStorageSyncwx.getStorage从本地缓存中同步获取指定 key 的内容
wx.removeStorageSyncwx.removeStorage从本地缓存中移除指定 key
wx.clearStorageSyncwx.clearStorageSync清理本地数据缓存

路由与通信

在小程序中实现页面的跳转,有两种方式:

  1. 声明式导航:navigator 组件
  2. 编程式导航:使用小程序提供的 API
  • wx.navigateTo():保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面
  • wx.redirectTo():关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面
  • wx.switchTab():跳转到 tabBar 页面,路径后不能带参数
  • wx.navigateBack():关闭当前页面,返回上一页面或多级页面
  1. 路径后可以带参数,参数需要在跳转到的页面的 **onLoad** 钩子函数中通过形参进行接收
  • 参数与路径之间使用 ? 分隔
  • 参数键与参数值用 = 相连
  • 不同参数用 & 分隔
  • 例如 path?key=value&key2=value2

自定义组件

创建-注册-使用组件

使用自定义组件 开发中常见的组件主要分为 公共组件 和 页面组件 两种,因此注册组件的方式也分为两种:

  1. 全局注册:在 app.json 文件中配置 usingComponents 节点进行引用声明,注册后可在任意组件使用
  2. 局部注册:在页面的 json 文件中配置 usingComponents 节点进行引用声明,只可在当前页面使用

组件中相互传参的例子:

<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->

<view class="custom-checkbox-container">
  <view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
    <label class="custom-label">
      <checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />

      <view class="content">
        <!-- lable 和 子节点内容都进行了展示 -->
        <!-- 要么展示 lable 要么展示 子节点内容 -->
        <!-- 如果用户传递了 lable 属性,就展示 lable -->
        <!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
        <text wx:if="{{ label !== '' }}">{{ label }}</text>

        <slot wx:else />
      </view>
    </label>
  </view>
</view>
Component({
  options: {
    styleIsolation: 'shared'
  },
  // 如果 styleIsolation 属性值是 shared
  // 这时候呢 externalClasses 选项会失效
  externalClasses: ['xxxx'],
  /**
   * 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据
   */
  properties: {
    // 如果需要接收传递的属性,有两种方式:全写、简写
    // label: String
    label: {
      // type 组件使用者传递的数据类型
      // 数据类型:String、Number、Boolean、Object、Array
      // 也可以设置为 null,表示不限制类型
      type: String,
      value: ''
    },
    position: {
      type: String,
      value: 'right'
    },
    // 复选框组件公共组件
    // 需要再多个页面、在多个项目中进行使用
    // 在使用的时候,有的地方希望默认是选中的效果,有的地方希望默认是没有被选中的效果
    // 怎么处理 ?
    // 首先让复选框默认还是没有被选中的效果
    // 如果希望复选框默认被选中,这时候传递属性(checked=true)到复选框组件
    checked: {
      type: Boolean,
      value: false
    }
  },
  /**
   * 组件的初始数据:用来定义当前组件内部所需要使用的数据
   */
  data: {
    isChecked: false
  },
  observers: {
    // 如果需要将 properties 中的数据赋值给 data
    // 可以使用 observers 进行处理
    checked: function (newChecked) {
      // console.log(newChecked)
      this.setData({
        isChecked: newChecked
      })
    }
  },
  /**
   * 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
   */
  methods: {
    // 更新复选框的状态
    updateChecked () {
      // this.$emit('ayncCheck', (sss:boolean)=>console.log(sss));
      this.setData({
        isChecked: !this.data.isChecked,
        // label: '在组件内部也可以修改 properties 中的数据'
      })
      // 在 JS 中可以访问和获取 properties 中的数据
      // 但是一般情况下,不建议修改,因为会造成数据流的混乱
      // console.log(this.properties.label)
      // console.log(this.data.isChecked)

      // 目前复选框组件的状态是存储在复选框组件内部的、存储在自定义组件内部的
      // 但是,在以后实际开发中,组件使用者、父组件有时候也需要获取到复选框内部的状态
      // 怎么办 ?
      // 这时候,自定义组件内部就需要发射一个自定义事件,
      // 如果组件使用者、父组件需要使用数据,绑定自定义事件进行获取即可
      this.triggerEvent('changechecked', this.data.isChecked)
    }

  }

})
   <custom-checkbox label='qweqwe' position='left' bind:changechecked="ayncCheck"/>

ayncCheck:(c:any)=>{
    console.log(c.detail);
    
  },

组件 wxml 的 slot

在使用基础组件时,可以给组件传递子节点传递内容,从而将内容展示到页面中,自定义组件也可以接收子节点内容 只不过在组件模板中需要定义 节点,用于承载组件引用时提供的子节点 默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。 同时需要给 slot 添加 name 来区分不同的 slot,给子节点内容添加 slot 属性来将节点插入到 对应的 slot 中

组件 wxml 的 observers(监听数据变化)

Component({
  data: {
    num: 10,
    count: 1,
    obj: { name: 'Tom', age: 10 },
    arr: [1, 2, 3]
  },
  observers: {
    // key 是需要检测数据
    // value 是一个函数,函数接收一个形参作为参数,是最新的值
    num: function(newNum) {
      console.log(newNum)
    },
    
    // 数据监听器支持监听属性或内部数据的变化,可以同时监听多个
    'num, count': function (newNum, newCount) {
       console.log(newNum, newCount)
    }
    
    // 监听器可以监听子数据字段
    'obj.age': function(newAge) {
      console.log(newAge)
    },
    
    // 如果需要监听所有子数据字段的变化,可以使用通配符 ** 
    'obj.**': function(newAge) {
      console.log(newAge)
    },
        
    'arr[0]': function (val) {}
  }
})

传参方式

父往子传值

用properties方式直接传:

     <sonTem name="{{fatherName}}" ></sonTem>
    <button bindtap="changName">修改fatherName</button>//直接改fatherName,sonTem会绑定并渲染
// components/sonTem/sonTem.ts
Component({

  /**
   * 组件的属性列表
   */
  properties: {
    name:{
      type: String, // 传递的数据类型
      value: '' // 默认值
    }
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   */
  methods: {
    changeName(){
      this.setData({name:'lzj'})
    }
  }
})

或用observers监听传入father的变化:

// components/sonTem/sonTem.ts
Component({

  /**
   * 组件的属性列表
   */
  properties: {
    name:{
      type: String, // 传递的数据类型
      value: '' // 默认值
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
      sonName:"wzz"
  },
  observers: {
     // 如果需要将 properties 中的数据赋值给 data
     // 可以使用 observers 进行处理
     name: function (newName) {
       // console.log(newChecked)
       this.setData({
         sonName: newName
       })
     }
   },
  /**
   * 组件的方法列表
   */
  methods: {
    changeName(){
      this.setData({name:'lzj'})
    }
  }
})

子往父传值

  1. 自定义组件触发事件时,需要使用 triggerEvent 方法发射一个自定义的事件
  2. 自定义组件标签上通过 bind 方法监听发射的事件
      <sonTem name="{{fatherName}}" bind:nameEvent="syncName"></sonTem>
      father:{{fatherName}}
      <button bindtap="changName">修改fatherName</button>
--------------------------ts----------------------------
        changName:function(){
          this.setData({
            fatherName:'wgp'
          })
        },//father修改name的方法
        syncName:function(sonName:any){
          console.log(sonName);
          
          this.setData({fatherName:sonName.detail})
        },//同步name
son:{{name}}
<button bind:tap="changeName">lzj</button>
/**
 * 组件的方法列表
 */
  methods: {
    changeName(){
      this.triggerEvent('nameEvent', "son")
    }
  }

父类获取子类组件

<sonTem name="{{fatherName}}" bind:nameEvent="syncName" class="sonClass"></sonTem>
father:{{fatherName}}
<button type="primary" bindtap="getChild">获取子组件实例对象</button>
/**
 * 组件的方法列表
 */
getChild:function () {

  // this.selectComponent 方法获取子组件实例对象
  // 获取到实例对象以后,就能获取子组件所有的数据、也能调用子组件的方法
  const res = this.selectComponent('.sonClass')
  console.log(res.data.name)

},

页面组件url传参

    onLoad (options) {//回去query内容
      // console.log('页面加载 - 2')
      // console.log(options)
      console.log(this.data.id)
      console.log(this.data.title)
      console.log(this.properties.id)
    },

小程序的生命周期

组件的生命周期

定义段描述
created在组件实例刚刚被创建时执行,注意此时不能调用 setData (还没有对模板解析)
attached在组件实例进入页面节点树时执行 (模板已经解析完毕,并且挂载到页面上)
ready在组件布局完成后执行
moved在组件实例被移动到节点树另一个位置时执行
detached在组件实例被从页面节点树移除时执行 (组件被销毁了)
Component({
  
  lifetimes: {
    created: function () {
      // 在组件实例刚刚被创建时执行,注意此时不能调用 setData 
      // 一般用来为组件添加一些自定义属性字段。
    },
    attached: function() {
      // attached 在组件完全初始化完毕、进入页面节点树后执行
      // 模板已经解析完毕,并且挂载到页面上
      // 一般都是在这里写对应的交互
    },
    detached: function() {
      // 在组件实例被从页面节点树移除时执行
    },
    
    // coding...
  }
    
  // coding...
})

组件所在页面的生命周期

组件还有一些特殊的生命周期,这类生命周期和组件没有很强的关联 主要用于组件内部监听父组件的展示、隐藏状态,从而方便组件内部执行一些业务逻辑的处理 组件所在页面的生命周期有 4 个: show、 hide、 resize、 routeDone,需要在 pageLifetimes 字段内进行声明

// components/custom06/custom06.js
Component({

  // coding...

  // 组件所在页面的生命周期
  pageLifetimes: {

    // 监听组件所在的页面展示(后台切前台)状态
    show () {
      console.log('组件所在的页面被展示')
    },

    // 监听组件所在的页面隐藏(前台切后台、点击 tabBar)状态
    hide () {
      console.log('组件所在的页面被隐藏')
    }

  }

})

生命周期总结

小程序冷启动,钩子函数执行的顺序 保留当前页面(navigate) 以及 关闭当前页面(redirect) 切后台 以及 切前台(热启动)