微信小程序开发学习笔记

869 阅读10分钟

写在最前面

边看视频 * 边撸代码 * 边记笔记,学习效率蹭蹭蹭~~~

学习视频

  1. 【智能社】微信小程序从入门到精通
  2. 【智能社】微信小程序从入门到精通 II
  3. 【李炎恢】微信小程序开发 / 微实战 / 和风天气 / 十天案例课堂

快速访问


视图层

750rpx

  • 设计稿推荐 750px 宽度
  • 换算公式:750 * 被测量元素宽度 / 设计稿总宽度
  • 字体尺寸规范
设计稿 rpx 转换

已知设计稿总宽度 375px 某元素宽度 150px

width:calc(750rpx * 150 / 375);
设计稿 vw 转换

vw 适用于 web 端,此写法 Sass/Less 通用

已知设计稿总宽度 375px 某元素宽度 150px

$ui-width: 375px;

@function px2vw($px) {
  @return $px / $ui-width * 100vw;
}

width:px2vw(150px);

框架

app.js

  • 生命周期全局数据
  • 设置全局数据
    App({
      onLaunch: function () {
        //
      },
    
      globalData: {
        old:18
      }
    });
    
  • 页面 .js 读取全局数据
    const app = getApp();
    console.log(app.globalData.old);
    
  • wxml 无法直接读取全局数据,只能先保存到页面 .jsdata
    const app = getApp();
    Page({
      data: {
        old:app.globalData.old,
      }
    });
    
  • developers.weixin.qq.com/miniprogram…

app.json

加载 WeUI 组件库
  1. app.json
{
  "useExtendedLib": {
    "weui": true
  }
}
  1. 页面 .json 配置需要的组件
{
  "usingComponents": {
    "mp-icon": "weui-miniprogram/icon/icon"
  }
}
  1. 页面 .wxml 使用组件
<mp-icon icon="add"></mp-icon>
  1. wechat-miniprogram.github.io/weui/docs/
自定义 tabBar
  1. app.json
{
  "pages": [
    "pages/index/index",
    "pages/analysis/index",
    "pages/settings/index"
  ],
  "tabBar": {
    "color": "#515151",
    "selectedColor": "#1296db",
    "backgroundColor": "#FFFFFF",
    "list": [
      {
        "text": "首页",
        "pagePath": "pages/index/index",
        "iconPath": "images/calculator.png",
        "selectedIconPath": "images/calculator-active.png"
      },
      {
        "text": "统计",
        "pagePath": "pages/analysis/index",
        "iconPath": "images/data.png",
        "selectedIconPath": "images/data-active.png"
      },
      {
        "text": "设置",
        "pagePath": "pages/settings/index",
        "iconPath": "images/set.png",
        "selectedIconPath": "images/set-active.png"
      }
    ]
  }
}
  1. developers.weixin.qq.com/miniprogram…

页面.js

页面.json

下拉刷新
  1. index.json
{
  "enablePullDownRefresh": true
}
  1. index.js
Page({
  onPullDownRefresh() {
    console.log('下拉刷新了');
  }
});

获取到数据后关闭下拉

wx.stopPullDownRefresh()
页面背景色设置

index.json 设置的是手势下拉后面的背景色

{
  "backgroundColor": "#ededed"
}

如果要设置页面背景颜色,得在 wxss 中自定义

page {
    background-color: #ededed;
}

路由页面跳转


页面间通信

链接参数

  • 简单且使用率高
  • 单向
A向B传递数据

使用场景:打开详细页

A.js

Page({
  fn(){
    wx.navigateTo({
      url: '/pages/page2/index?id=1&old=18'
    })
  }
})

B.js

Page({
  onLoad(option){
    console.log(option);
  }
})

全局数据参数

  • app.jsglobalData
  • 可以在任意两个页面间通信
  • 但是不会实时更新

EventChannel

B向A传递数据

使用场景:B新规文章后 A刷新文章列表

A.js

events 里添加 a 事件

Page({
  fn(){
    wx.navigateTo({
      url: '/pages/page2/index',
      events:{
        a(data){
          console.log(data);
        }
      }
    })
  }
})

B.js

通过 eventChannel.emit 触发 A.jsa 事件并带参数

Page({
  fn(){
    this.getOpenerEventChannel().emit('a',{ 
      id: 1,
      old: 18
     });
  }
})
A向B传递数据

使用场景:打开详细页

A.js

通过在 success 里添加 eventChannel.emit 触发 B.jsa 事件并带参数

Page({
  fn(){
    wx.navigateTo({
      url: '/pages/page2/index',
      success({eventChannel}){
        eventChannel.emit('a',{
          id: 1,
          old: 18
        })
      }
    })
  }
})

B.js

监听 a 事件

Page({
  onLoad(option){
    this.getOpenerEventChannel().on('a',function(option){
      console.log(option);
    })
  }
})

容器类

view

使用 hover-stop-propagation 可以防止 hover 事件冒泡
// 
<view><view 
    hover-class="hover"
    hover-stop-propagation
  ></view>
</view>

// 或者
<view><view 
    hover-class="hover"
    hover-stop-propagation="{{true}}"
  ></view>
</view>

web-view

必须是业务域名才可用
<web-view src="https://juejin.cn/"></web-view>

swiper & swiper-item

基本结构
<swiper
  indicator-dots
  autoplay
>
  <swiper-item>
    <image src="/images/1.jpg"/>
  </swiper-item>
  <swiper-item>
    <image src="/images/2.jpg"/>
  </swiper-item>
</swiper>

open-data

获取用户开放数据
<open-data type="userNickName"/>
<open-data type="userAvatarUrl"/>
<open-data type="userGender" lang="zh_CN"/>

内容类

icon

progress

text

用户可选中文字
<text
  selectable
>222</text>

rich-text

navigator

自定义页面内返回链接
<navigator
  open-type="navigateBack"
>
  返回
</navigator>

表单类

button

快速实现分享功能
<button
  type="primary"
  open-type="share"
>分享</button>

checkbox-group

picker

  • 功能强大,已封装时间、日期、地区选择器
  • 类似 html 里的 select + option 标签
  • json 数据可以通过 range-key 来指定显示内容
  • developers.weixin.qq.com/miniprogram…
自定义选择器内容

index.wxml

<picker
  range="{{array}}"
  bindchange="bindChange"
>
  当前选择:{{array[index]}}
</picker>

index.js

Page({
  data:{
    index:0,
    array:[
      '张三', '李四', '王五'
    ]
  },
  bindChange(e){
    console.log(e.detail.value);
    this.setData({
      index: e.detail.value
    })
  }
});

form

使用表单校验 WxValidate 扩展插件
  1. github.com/wux-weapp/w…

  2. WxValidate.js 放入 /utils/WxValidate.js

  3. index.wxml

    <form bindsubmit="bindSubmit">
      <label>
        NAME:
        <input name="your-name" type="text" placeholder="输入NAME"/>
      </label>
      <label>
        EMAIL:
        <input name="your-email" type="text" placeholder="输入EMAIL"/>
      </label>
      <label>
        MESSAGE:
        <textarea name="your-message" rows="10" placeholder="输入MESSAGE"></textarea>
      </label>
      <button formType="submit" type="primary">提交</button>
    </form>
    
  4. index.js

    import WxValidate from '../../utils/WxValidate.js'
    
    Page({
      data: {},
      onLoad: function (options) {},
      bindSubmit(e){
        // 验证字段的规则
        const rules = {
          'your-name': {
            required: true,
          },
          'your-email': {
              required: true,
              email: true,
          },
          'your-message': {}
        };
    
        // 验证字段的提示信息,若不传则调用默认的信息
        const messages = {
          'your-name': {
              required: '请输入姓名',
          },
          'your-email': {
              required: '请输入邮箱',
              email: '请输入正确的邮箱',
          },
          'your-message': {},
        }
    
        // 创建实例对象
        this.WxValidate = new WxValidate(rules,messages);
    
        // 传入表单数据,调用验证方法
        if (!this.WxValidate.checkForm(e.detail.value)) {
          console.log(this.WxValidate.errorList);
          const error = this.WxValidate.errorList[0];
          wx.showToast({
            icon: 'none',
            title: error.msg,
          })
          return false;
        }
    
        // 验证成功后表单数据发送给后端
        wx.request({
          url: 'https://juejin.cn/form',
          method: 'POST',
          data: e.detail.value,
          success: res=> {
            console.log(res);
          }
        });
      }
    })
    

媒体类

image

video

<video
  id="v1"
  src="https://juejin.cn/media/video.mp4" />
<button
  type="primary"
  bindtap="play"
>播放</button>
<button
  type="primary"
  bindtap="pause"
>暂停</button>
Page({
  play(){
    let v1 = wx.createVideoContext('v1')
    v1.play()
    v1.requestFullScreen()// 真机才能全屏
  },
  pause(){
    let v1 = wx.createVideoContext('v1')
    v1.pause()
  }
})

radio

背景音频


地图

map


画布

canvas


页面配置

page-meta

方便动态设置页面配置

page-meta

<page-meta
  background-color="{{bgColor}}"
  background-color-top="{{bgColorTop}}"
>
  <navigation-bar
    title="{{title}}"
    loading="{{loading}}"
  />
</page-meta>

语法类

数据

  • {{}} 内不可以放入函数表达式,不会运行
  • developers.weixin.qq.com/miniprogram…
  • 数据只能放在页面 .jsdata
    Page({
      data: {
        oy:'技术黄'
      }
    });
    
  • .wxml 页面获取数据
    <view>{{oy}}</view>
    
    <view
        id="{{oy}}"
    ></view>
    
  • .js 获取数据
    this.data.oy
    

事件

通过事件实现数据递增
  1. index.wxml
<view>{{old}}</view>

<button
  type="primary"
  bindtap="bindTap"
>Add</button>
  1. index.js
Page({
  data: {
    old:18
  },
  bindTap(){
    this.setData({
      old : this.data.old + 1
    });
  }
});

循环

  • wx:for
  • 默认提供 indexitem 二个值,可自定义修改
  • 可循环:数组、字符串、数字、json
  • 为了性能需要设置 wx:key ,最好使用后台提供的 id 字段
只渲染循环内部分
  1. 通过 block 标签实现
    <block
      wx:for="{{['张三', '李四', '王五']}}"
      wx:key="index"
    >
      <text>{{index}}:{{item}}</text>
    </block>
    
  2. 染循输出结果
    <text>0:张三</text>
    <text>1:李四</text>
    <text>2:王五</text>
    
结合 swiper 循环
  1. index.wxml 这里的 wx:key 直接输入键名即可
<swiper
  indicator-dots
  autoplay
>
  <swiper-item
    wx:for="{{swiper_imgs}}"
    wx:key="id"
  >
    <image src="/images/{{item.src}}" />
  </swiper-item>
</swiper>
  1. index.js
Page({
  data: {
    swiper_imgs:[
      {id:1,src:'1.jpg'},
      {id:2,src:'2.jpg'}
    ]
  }
});

条件

结合 switch 开关效果
  1. index.wxml
<switch
  checked="{{show}}"
  bindchange="bindChange"
/>

<view
  wx:if="{{show}}"
>Hello World</view>
  1. index.js
Page({
  data: {
    show:false
  },
  bindChange(e){
    this.setData({
      show:!e.detail.value
    });
  }
});

模块化

  1. utils/location.js
module.exports = (age) => {
  console.log(age)
}
  1. pages/index/index
let location = require('../../utils/location')

location(18)

自定义组件

developers.weixin.qq.com/miniprogram…

创建自定义组件

  1. components 文件夹右键
  2. 新建文件夹命名组件名 header
  3. header 文件夹右键 新建 component
  4. pages/index/index.json
    {
      "usingComponents": {
        "header": "../../components/header"
      }
    }
    
  5. 页面使用 header 组件
    <header />
    
组件属性

页面设置组件的属性值,可以在组件内显示,通过 properties 来设置

页面 pages/index/index.wxml

<header title="hello world" />

组件 components/header/index.js

Component({
  properties: {
    title:{
      type: String,
      value: '默认值'// 没有默认值可以删除这行
    }
  }
})

组件 components/header/index.wxml

<view>{{title}}</view>
组件内方法

组件内方法必须放在 methods

组件 components/header/index.js

Component({
  methods: {
    fn(){
      console.log('hello world');
    }
  }
})

组件 components/header/index.wxml

<button
  bindtap="fn"
>组件方法调用</button>
组件自定义数据

获取在标签上自定义的 data-xx 数据,通过 dataset 属性获取

页面 pages/index/index.wxml

<header data-age="18" />

组件 components/header/index.js

Component({
  methods: {
    fn(){
      console.log('data-age: '+this.dataset.age);
    }
  }
})
监听组件属性变化

监听 properties 内数据的变化,通过 observer 实现,适用于 父向子 场景

组件 components/header/index.js

Component({
  properties: {
    title:{
      type: String,
      value: '默认值',
      observer: function(){
        console.log('title值每次变化都会触发');
      }
    }
  }
})
生命周期

developers.weixin.qq.com/miniprogram…

组件的的生命周期必须放在 lifetimes

组件的 ready 类似页面的 onload

Component({
  lifetimes: {
    ready: function() {
      console.log('onload');
    }
  }
})

组件的 pageLifetimes 可以监听组件所在页面的生命周期

Component({
  pageLifetimes: {
    show: function() {
      // 页面被展示
    },
    hide: function() {
      // 页面被隐藏
    },
    resize: function(size) {
      // 页面尺寸变化
    }
  }
})

组件间通信

developers.weixin.qq.com/miniprogram…

父向子

使用场景:打开详细页

  1. 动态修改组件的 properties 属性
  2. 通过子组件属性的 observer 监听实现父向子传递

页面 pages/index/index.wxml

<page title="{{page.title}}" content="{{page.content}}"></page>
<button
  bindtap="fn"
>父向子</button>

页面 pages/index/index.js

Page({
  data: {
    page:{
      title: 'title',
      content: 'content'
    }
  },
  onLoad: function (options) {
    
  },
  fn(){
    this.setData({
      page:{
        title: this.data.page.title + '+',
        content: this.data.page.content + '+'
      }
    })
  }
})

组件 components/page/index.wxml

<view>{{title}}</view>
<view>{{content}}</view>

组件 components/page/index.js

// components/view/index.js
Component({
  properties: {
    title:{
      type: String,
      observer: function(){
        console.log('page.title属性监听');
      }
    },
    content:{
      type: String,
      observer: function(){
        console.log('page.content属性监听');
      }
    },
  }
})
子向父

使用场景:子页面数据库添加数据后,通知父页面刷新

  1. 子组件通过 triggerEvent 通知父
  2. 页面引用的组件设置 bind:xxx 绑定通知
  3. 页面 e.detail 可以获取参数

页面 pages/index/index.wxml

<add
  bind:add="fn"
></add>

页面 pages/index/index.js

Page({
  data: {},
  onLoad: function (options) {
    
  },
  fn(e){
    console.log(e.detail);// 可获取子组件的参数
    console.log('子组件触发父方法');
  }
})

组件 components/add/index.wxml

<button
  bindtap="fn"
>子向父</button>

组件 components/add/index.js

Component({
  methods: {
    fn(){
      this.triggerEvent('add', {name:'oy',age:18})
    }
  }
})

behaviors

developers.weixin.qq.com/miniprogram…

  1. 可以向组件内部注入“组件”内容,如果内容相同组件优先
  2. 适合复用相同内容
  3. 只能用在组件里
  4. 不建议使用,维护难度大哈哈哈

云开发

【微信小程序【云】开发学习笔记】

  • 安全性高、可扩容、自带鉴定权限。
  • 配置云开发目录 project.config.json
    {
      "miniprogramRoot": "miniprogram/",
      "cloudfunctionRoot": "cloudfunctions/"
    }
    

云函数

  1. cloudfunctions 右键 [新建 Node.js 云函数] add
  2. cloudfunctions/add/index.js
    // 云函数入口文件
    const cloud = require('wx-server-sdk')
    
    cloud.init()
    
    // 云函数入口函数
    exports.main = async (event, context) => {
      return 'Hello World ' + event:name;
    }
    
  3. add 右键 [上传并部署:云端安装依赖]
  4. 前端使用 miniprogram/pages/index/index.js
    Page({
      data: {
          name:'张三'
      },
      
      onLoad: function (options) {},
    
      fn(){
        wx.cloud.callFunction({
          name:'add',
          data:{},
          success(res){
            console.log(res);
          },
          fail(res){
            console.log(res);
          }
        });
      }
    })
    

网络

请求

wx.request()

wx.request({
  url: 'https://api.juejin.cn/',
  method: 'POST',
  data: {
    x: '',
    y: ''
  },
  header: {
    'content-type': 'application/json'
  },
  success (res) {
    console.log(res.data)
  },
  fail (err) {
    console.error(err)
  }
})
封装成 Promise 风格

wx.request 默认不支持 Promise 风格,我们可以自己封装。

miniprogram/utils/request.js

module.exports = (name,age,url) => {
  return new Promise((resolve, reject) => {
    wx.request({
      url,
      data: {
        name,
        age
      },
      success (res) {
        resolve(res.data)
      },
      fail (err) {
        reject(err)
      }
    })
  })
}

使用

let utilsRequest = require('../../utils/request')

Page({
  onLoad: async function (options) {
    let res = await utilsRequest('Hello World', '18', 'https://juejin.cn/')
  }
})

上传

wx.uploadFile()

wx.chooseImage({
  success (res) {
    const tempFilePaths = res.tempFilePaths
    wx.uploadFile({
      url: 'https://juejin.cn/upload',
      filePath: tempFilePaths[0],
      success ({data}){
        console.log(data)
      }
    })
  }
})

WebSocket

  1. 可持续连接
  2. 数据双向通信

wx.connectSocket()

wx.connectSocket({
  url: 'wss://juejin.cn'
})

wx.sendSocketMessage()

wx.sendSocketMessage({
  data: 'hello world'
})

数据缓存

读取

wx.getStorageSync()

wx.getStorageSync('title')

写入

wx.setStorageSync()

wx.setStorageSync('title', 'hello world')

清空

wx.clearStorageSync()

wx.clearStorageSync()

获取用户信息

不包含 OPENID

wx.getUserProfile

必须点击,弹出窗口用户确认后才能获取

wx.getUserProfile()

<button type="primary" bindtap="getUserProfile">授权登录</button>
Page({
  getUserProfile() {
    wx.getUserProfile({
      desc: '用于完善会员资料',
      success: (res) => {
        console.log(res.userInfo);
      }
    })
  }
})

wx.getUserInfo

必须点击,弹出窗口可获取

官方自 2021年4月13日 起只能获取匿名的用户个人信息,所以可以弃用了。

wx.getUserProfile()

<button type="primary" open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">授权登录</button>
// pages/login/index.js
Page({
  bindGetUserInfo (e) {
    console.log(e.detail.userInfo)
  }
})

用户设置

wx.getSetting

查询 userInfo userLocation 用户授权结果

wx.getSetting()

wx.openSetting

打开小程序授权设置界面

wx.openSetting()

授权定位

判断如果没有授权,就通过 openSetting 打开小程序授权设置界面

Page({
  onLoad: async function (options) {
    // 授权定位
    try{
      let res = await wx.getLocation({type: 'wgs84'})
    }catch(e){
      // 未授权或者拒绝定位 就止步到这里了
      this.checkAuthSetting()
      return
    }
  },

  async checkAuthSetting(){
    let {authSetting} = await wx.getSetting()
    if (!authSetting['scope.userLocation']) {
      // console.log('未授权定位');
      wx.showModal({
        title: '请授权定位',
        success: ({confirm}) => {
          if(confirm){
            // 打开小程序授权设置界面
            wx.openSetting({
              success: () => {
                // 再核实下是否授权定位
                wx.redirectTo({
                  url: '/pages/index/index',
                })
              }
            })
          }
        }
      })
      return {
        code: 400,
        msg: '未授权定位'
      };
    }else{
      return {
        code: 200,
        msg: '已经授权定位'
      }
    }
  }
})

技巧篇

支持 sass

  1. 修改 project.config.jsonsettinguseCompilerPlugins 值,如果没有这个值就添加
    "useCompilerPlugins": ["sass"],
    
  2. 修改 index.wxss 的后缀为 index.scss
  3. 清缓存刷新
  4. 如果还是不行控制台报错就 详情 里找到 将JS编译成ES5 取消选中然后再选中(不知道算不算编辑器bug)

获取 openid

  1. 小程序:云函数,最方便但是。。。
  2. web:url -> code -> access_token -> openid
  3. 小程序 + web:wx.login(code) -> access_token -> openid 最优方案

检测小程序新版本

  1. developers.weixin.qq.com/miniprogram…
  2. 自动监测下载最新版小程序
  3. app.js
    // app.js
    App({
      onLaunch: function () {
        this.globalData = {};
    
        // 自动监听是否有新版本
        const updateManager = wx.getUpdateManager()
        updateManager.onUpdateReady(function () {
          wx.showModal({
            title: '更新提示',
            content: '新版本已经准备好,是否重启应用?',
            success: function (res) {
              if (res.confirm) {
                // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
                updateManager.applyUpdate()
              }
            }
          })
        })
    
      }
    });
    

小程序生成URL

这里使用的是 URL Link 生成的,因为时效只有 30 天,所以放在 html 里跳转方便替换。

  1. 获取 access_token developers.weixin.qq.com/miniprogram…
  2. 通过获取的 access_token 来获取 url_link developers.weixin.qq.com/miniprogram… image.png
  3. 把获取到的 url_link 贴入 html
    // 生成日期20221010 有效期 30天
    location.href = 'https://wxaurl.cn/abcdefg';
    

HTML5 中使用 WeUI 样式库

  1. github.com/Tencent/weu…
  2. 文档:github.com/Tencent/weu…
  3. alert
    <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.0.1/weui.min.css">
    <script src="https://res.wx.qq.com/t/wx_fed/cdn_libs/res/weui/1.2.3/weui.min.js"></script>
    <script>
        weui.alert('alert');
    </script>
    
  4. loading
    let loadingWeUI = weui.loading('加载中');
    setTimeout(function () {
        loadingWeUI.hide(function() {
             console.log('加载完成');
         });
    }, 3000);
    

持续更新