Skip to content

微信小程序

配置文件

全局配置文件 app.json

json
{
  "entryPagePath": "pages/index/index",
  "pages": [
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window": {
    "navigationBarTitleText": "Demo",
    "navigationBarBackgroundColor": "#ffffff",
    "backgroundColor": "#f8f8f8"
  },
  "tabBar": {
    "selectedColor": "#d0021b",
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页",
      "iconPath": "/assets/images/home.png",
      "selectedIconPath": "/assets/images/home_active.png"
    }]
  }
}
  • entryPagePath:小程序默认启动首页
  • pages:页面路径列表,第一个页面为首页
  • window:全局窗口表现
    • navigationBarBackgroundColor:导航栏背景色
    • navigationBarTextStyle导航栏标题颜色
    • navigationBarTitleText:导航栏标题
  • tabBar:底部/顶部tab栏配置
    • selectedColor:选中颜色
    • list:tab列表,至少2个最多5个

页面配置文件 page.json

json
{
  "usingComponents": {}
}
  • 只覆盖app.json中window的配置
  • 无需写window键名
  • 页面中.json文件非必须

工程配置文件 project.config.json

json
{
  "appid": "wx99a5813d6e1698c4",
  "compileType": "miniprogram",
  "libVersion": "3.7.3",
  "packOptions": {
    "ignore": [],
    "include": []
  },
  "setting": {
    "coverView": true,
    "es6": true,
    "postcss": true,
    "minified": true,
    "enhance": true,
    "showShadowRootInWxmlPanel": true,
    "packNpmRelationList": [],
    "babelSetting": {
      "ignore": [],
      "disablePlugins": [],
      "outputPath": ""
    }
  },
  "condition": {},
  "editorSetting": {
    "tabIndent": "insertSpaces",
    "tabSize": 2
  }
}
  • 保存项目级别的配置,提交到代码仓库
  • 用于团队协作时统一开发环境配置
  • 包含小程序开发工具、编译、上传等配置

TIP

json
{
  "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
  "projectname": "miniprogram-1",
  "setting": {
    "compileHotReLoad": true
  }
}
  • 保存开发者个人的本地配置,不应提交到代码仓库
  • 用于存储个人开发偏好和敏感信息
  • 优先级高于 project.config.json

索引配置文件 sitemap.json

json
{
    "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
    "rules": [{
    "action": "allow",
    "page": "*"
    }]
}
  • 控制小程序页面是否被微信索引
  • 影响小程序在微信内的搜索展现

渲染模式

WebView 渲染模式

  • 渲染引擎:基于浏览器 WebView 内核(类似 Chrome/WebKit)
  • 特点
    • 兼容性好:支持所有基础库版本,适合老项目或需要广泛兼容的场景
    • 性能一般:渲染管线较长,JS 逻辑可能阻塞 UI 渲染,动画和交互可能卡顿
    • 开发体验:类似传统 H5 开发,支持完整 CSS/JS,但受限于 WebView 能力

Skyline 渲染模式

  • 渲染引擎:微信自研的高性能引擎(底层可能基于 Flutter 优化)
  • 特点
    • 性能更强
      • 首屏加载快 17.6%,渲染耗时减少 50%
      • 支持 Worklet 动画(UI 线程直接执行,无通信延迟)
    • 更丰富的特性
      • 支持 Flex/Grid 布局、手势系统、共享元素动画等原生级交互
      • WXSS 预编译(比运行时解析快 5 倍)
    • 兼容性限制
      • 需基础库 ≥ 2.29.2(推荐 ≥ 3.0.0)
      • 部分 CSS 特性被精简,需适配

事件系统

事件类型

  • touchstart:手指触摸动作开始
  • touchmove:手指触摸后移动
  • touchcancel:手指触摸动作被打断,如来电提醒,弹窗
  • touchend:手指触摸动作结束
  • tap:手指触摸后马上离开
  • longpress:手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发

事件绑定

html
<!-- 普通绑定(不阻止事件冒泡) -->
<view bindtap="handleTap">点击我</view>

<!-- 阻止冒泡绑定 -->
<view catchtap="handleTap">点击我(阻止冒泡)</view>

<!-- 互斥事件绑定 -->
<view mut-bind:tap="handleMutTap1">互斥1</view>
<view mut-bind:tap="handleMutTap2">互斥2</view>

<!-- 捕获阶段事件绑定 -->
<view capture-bind:touchstart="handleCaptureStart">捕获阶段</view>

<!-- 自定义组件事件绑定 -->
<custom-component bind:myevent="handleCustomEvent" />

事件对象

js
handleTap(e) {
  console.log(e.type);       // 事件类型
  console.log(e.timeStamp);  // 事件触发时间戳
  console.log(e.target);     // 触发事件的源组件
  console.log(e.currentTarget); // 当前绑定事件的组件
  console.log(e.detail);     // 额外信息(如点击位置)
  console.log(e.touches);    // 触摸点信息
  console.log(e.changedTouches);    // 变化触摸点信息
}

TIP

特性targetcurrentTarget
指向对象实际触发事件的组件绑定事件处理函数的组件
冒泡影响不随冒泡改变随冒泡层级变化
dataset获取获取触发点的data属性获取绑定元素的data属性
组件层级可能是深层子组件始终是绑定事件的组件
实际用途确定事件触发源头确定事件处理上下文
  • 当事件绑定在触发事件的元素本身时,两者指向相同
html
<!-- 点击按钮时,target和currentTarget都指向这个button -->
<button 
  bindtap="handleButtonTap"
  data-id="123"
>点击我</button>
js
handleButtonTap(e) {
  // e.target === e.currentTarget
  console.log(e.target.dataset.id); // "123"
  console.log(e.currentTarget.dataset.id); // "123"
}
  • 当事件通过冒泡传递到父元素时,两者指向不同
html
<view bindtap="handleParentTap" class="parent">
  <view class="child">
    <text class="grandchild">点击这里</text>
  </view>
</view>
js
handleParentTap(e) {
  console.log('target:', e.target); // 指向实际点击的text元素
  console.log('currentTarget:', e.currentTarget); // 指向绑定事件的view元素
}

事件传参

  • dataset:收集所有以data-开头的自定义属性
html
<view data-user-id="123" data-role="admin" bindtap="handleTap">
  <text data-note="test">点击区域</text>
</view>
js
handleTap(e) {
  // 点击text时:
  console.log(e.target.dataset); // {note: "test"}
  console.log(e.currentTarget.dataset); // {userId: "123", role: "admin"}
}
  • mark:实现跨层级数据传递(基础库2.7.1+)
html
<view bindtap="handleTap" mark:user="{{{id: 1, name: 'Alice'}}}">
  点击获取mark数据
</view>
js
handleTap(e) {
  console.log(e.mark.user); // {id: 1, name: "Alice"}
}

路由导航

声明式导航

  • 基础使用
html
<!-- 普通跳转 -->
<navigator url="/pages/detail/detail?id=123">查看详情</navigator>

<!-- 新窗口打开 -->
<navigator url="/pages/webview/webview" open-type="navigate">
  打开网页
</navigator>
  • open-type 类型
属性值对应API说明
navigatewx.navigateTo默认值,保留当前页
redirectwx.redirectTo关闭当前页跳转
switchTabwx.switchTab切换Tab页
reLaunchwx.reLaunch关闭所有页跳转
navigateBackwx.navigateBack返回上一页

编程式导航

  • wx.navigateTo
    • 保留当前页面(可返回)
    • 可跳转到非TabBar页面
    • 最多保留10层页面栈
js
wx.navigateTo({
  url: '/pages/detail/detail?id=123&name=product',
  events: {
    // 接收被打开页面发送的数据
    acceptData(data) {
      console.log('接收数据:', data)
    }
  },
  success(res) {
    console.log('跳转成功', res.eventChannel)
  }
})
  • wx.redirectTo
    • 关闭当前页面(不可返回)
    • 不增加页面栈深度
    • 适用于登录后跳转等场景
js
wx.redirectTo({
  url: '/pages/home/home?from=login'
})
  • wx.switchTab
    • 关闭所有非TabBar页面
    • 只能跳转到tabBar配置的页面
    • 不支持传递参数
js
wx.switchTab({
  url: '/pages/index/index'
})
  • wx.reLaunch
    • 关闭所有页面(清空页面栈)
    • 打开新页面(可跳转到任意页面)
    • 适合全局状态重置场景
js
wx.reLaunch({
  url: '/pages/main/main?reset=true'
})
  • wx.navigateBack
    • 返回上一页面或多层页面
    • 可传递返回数据
js
// 返回上一页
wx.navigateBack()

// 返回指定层数
wx.navigateBack({
  delta: 2 // 返回上2层
})

// 返回时传递数据
const pages = getCurrentPages()
const prevPage = pages[pages.length - 2]
prevPage.setData({ backData: '返回数据' })
wx.navigateBack()

setData 方法

更新单个数据

js
Page({
  data: {
    message: 'Hello'
  },
  
  updateMessage() {
    this.setData({
      message: 'Hello World!'
    })
  }
})

更新多个数据

js
this.setData({
  userName: '张三',
  userAge: 28,
  isVIP: true
})

更新嵌套数据

js
Page({
  data: {
    user: {
      name: '李四',
      address: {
        city: '北京',
        district: '朝阳区'
      }
    }
  },
  
  updateUser() {
    this.setData({
      'user.name': '王五',
      'user.address.district': '海淀区'
    })
  }
})

数据路径更新

js
// 更新数组特定项
this.setData({
  'list[2].name': '新名称'
})

// 使用变量作为路径
const index = 3
const field = 'status'
this.setData({
  [`list[${index}].${field}`]: 'completed'
})

使用回调函数

js
this.setData({
  counter: this.data.counter + 1
}, () => {
  // 数据更新完成后的回调
  console.log('数据已更新,当前值:', this.data.counter)
  wx.showToast({ title: '更新成功' })
})

批量更新优化

js
// 不推荐:多次调用setData
this.setData({ a: 1 })
this.setData({ b: 2 })
this.setData({ c: 3 })

// 推荐:单次批量更新
this.setData({
  a: 1,
  b: 2,
  c: 3
})

TIP

调试技巧

js
// 封装调试setData
function debugSetData(data) {
  console.log('setData调用:', data)
  const start = performance.now()
  this.setData(data, () => {
    console.log(`更新完成,耗时: ${(performance.now() - start).toFixed(2)}ms`)
  })
}

wx.request 方法

HTTP 方法

方法用途Header 使用场景Data 使用场景示例
GET获取数据通常无需特殊 Header
可选:Cache-Control
URL 参数
data: {page:1, size:10}
/api?page=1&size=10
wx.request({ url:'/api', data:{q:'搜索词'} })
POST创建数据必设:Content-Type
application/json
application/x-www-form-urlencoded
请求体内容
data: {name:'张三'}
wx.request({ method:'POST', header:{'Content-Type':'application/json'}, data:{...} })
PUT更新数据同 POST请求体内容
提供完整资源数据
data: {id:123, title:'新标题'}
wx.request({ method:'PUT', url:'/api/123', data:{...} })
DELETE删除数据通常只需认证 Header通常为空
偶尔用于批量删除
wx.request({ method:'DELETE', url:'/api/123' })

Header 使用指南

Header 类型常用值使用场景是否必须示例
Content-Typeapplication/jsonPOST/PUT 发送 JSON 数据✅ 是'Content-Type': 'application/json'
application/x-www-form-urlencoded表单提交⚠️ 按需'Content-Type': 'application/x-www-form-urlencoded'
multipart/form-data文件上传❌ 否(小程序自动设置)
AuthorizationBearer <token>身份认证⚠️ 按需'Authorization': 'Bearer xyz123'
Cache-Controlno-cache禁用缓存⚠️ 按需'Cache-Control': 'no-cache'
max-age=3600缓存 1 小时⚠️ 按需'Cache-Control': 'max-age=3600'
X-CSRF-Token任意字符串防跨站攻击⚠️ 按需'X-CSRF-Token': 'abc456'

Data 使用指南

方法Data 格式示例注意事项
GET键值对对象data: {page:1, size:10}自动转为 URL 参数:/path?page=1&size=10
POSTJSON 对象data: {name:'李四', age:25}需配合 Content-Type: application/json
表单字符串data: 'name=李四&age=25'需配合 Content-Type: application/x-www-form-urlencoded
PUTJSON 对象data: {id:123, title:'新标题'}应提供完整资源数据(全量更新)
DELETE通常为空data: null资源 ID 放 URL 中:/resource/123

本地存储

API 方法类型功能描述参数说明返回值/回调使用场景
wx.setStorage异步存储数据到本地key: 键名
data: 值(任意类型)
success/fail/complete 回调非关键操作,无需立即结果
wx.setStorageSync同步同步存储数据key: 键名
data: 值
需立即确认存储结果的操作
wx.getStorage异步读取本地存储数据key: 键名success 返回数据非阻塞读取
wx.getStorageSync同步同步读取数据key: 键名对应 key 的数据需要立即使用数据的场景
wx.removeStorage异步删除指定 key 的存储数据key: 键名success/fail/complete 回调清理特定数据
wx.removeStorageSync同步同步删除数据key: 键名需要立即删除确认的操作
wx.clearStorage异步清空所有本地数据无参数success/fail/complete 回调用户退出登录时清理数据
wx.clearStorageSync同步同步清空所有数据无参数需要立即清空存储的场景

存储数据

js
// 异步存储 - 适合非关键操作
wx.setStorage({
  key: 'userInfo',
  data: {name: '张三', id: 'U123456'},
  success: () => console.log('存储成功'),
  fail: err => console.error('存储失败', err)
})

// 同步存储 - 需立即确认的场景
try {
  wx.setStorageSync('token', 'Bearer xyz789')
  console.log('Token 已存储')
} catch (e) {
  console.error('存储失败', e)
}

获取数据

js
// 异步读取
wx.getStorage({
  key: 'userInfo',
  success: res => console.log('用户数据:', res.data),
  fail: () => console.log('数据不存在')
})

// 同步读取 - 适合需要立即使用的数据
try {
  const token = wx.getStorageSync('token')
  if (token) {
    console.log('获取到Token:', token)
  } else {
    console.log('Token不存在')
  }
} catch (e) {
  console.error('读取失败', e)
}

删除数据

js
// 异步删除单个key
wx.removeStorage({
  key: 'tempData',
  success: () => console.log('临时数据已清除')
})

// 同步删除
try {
  wx.removeStorageSync('obsoleteKey')
  console.log('旧数据已删除')
} catch (e) {
  console.error('删除失败', e)
}

清空数据

js
// 异步清空所有存储
wx.clearStorage({
  success: () => console.log('所有本地数据已清除')
})

// 同步清空 - 用户退出登录时
function logout() {
  try {
    wx.clearStorageSync()
    console.log('存储已清空,跳转到登录页')
    wx.reLaunch({ url: '/pages/login/login' })
  } catch (e) {
    console.error('清空存储失败', e)
  }
}

上拉刷新与下拉加载

scroll-view 组件

scroll-view 是一个可滚动的容器组件,需要​​手动指定高度​​。通过其内置事件实现滚动行为:

  • 上拉加载更多​​

    • ​属性:​​

      • bindscrolltolower:滚动到底部/右边时触发。
      • lower-threshold:触发底部的距离阈值(单位:px/rpx)。
    • ​示例:​​

      html
      <scroll-view
      scroll-y
      lower-threshold="100"
      bindscrolltolower="loadMore"
      style="height: 500rpx"
      >
      <!-- 内容 -->
      </scroll-view>
  • 下拉刷新​​

    • ​​属性:​​

      • refresher-enabled:开启自定义下拉刷新。
      • refresher-triggered:控制刷新状态。
      • bindrefresherrefresh:下拉刷新时触发。
    • ​​示例:​​

      html
      <scroll-view
      scroll-y
      refresher-enabled
      refresher-triggered="{{isRefreshing}}"
      bindrefresherrefresh="onRefresh"
      style="height: 500rpx"
      >
      <!-- 内容 -->
      </scroll-view>

TIP

优点:

  • ​局部控制​​:适用于页面中的​​特定滚动区域​​(非全屏)。
  • ​灵活性强​​:可自定义刷新动画和加载行为。

缺点:

  • ​需固定高度​​:必须设置明确的高度(如 height: 100vh 或固定值)。
  • ​无法覆盖导航栏​​:下拉刷新的动画仅在 scroll-view 内部生效。

页面原生事件

通过页面配置文件(.json)和生命周期函数实现​​全局页面​​的滚动行为。

  • 上拉加载更多​​

    • ​配置项:

      json
      {
          "onReachBottomDistance": 100 // 距离底部100rpx时触发
      }
      • ​​生命周期函数:​​
      js
      onReachBottom() {
      // 加载更多逻辑
      }
  • 下拉刷新​

    • ​配置项:

      json
      {
          "enablePullDownRefresh": true // 开启全局下拉刷新
      }
      • ​​生命周期函数:​​
      js
      onPullDownRefresh() {
      // 刷新逻辑
      wx.stopPullDownRefresh() // 手动停止刷新动画
      }

TIP

优点:

  • ​简单易用​​:无需处理滚动容器,天然支持整个页面。
  • ​全局覆盖​​:下拉刷新动画覆盖整个页面(包括导航栏)。
  • ​无高度限制​​:自动适配页面高度。

缺点:

  • ​无法局部控制​​:只适用于整个页面的滚动区域。
  • ​刷新动画固定​​:无法完全自定义下拉刷新样式(仅支持全局配置)。

强制更新

js
App({
  onLaunch() {
    // 1. 获取更新管理器
    const updateManager = wx.getUpdateManager();

    updateManager.onCheckForUpdate(res => {
    if (res.hasUpdate) {
        wx.showToast({
        title: '正在下载新版本',
        icon: 'loading'
        });
    }
    });

    // 显示下载进度(增强用户体验)
    updateManager.onProgressUpdate((res) => {
    console.log('下载进度', res.progress);
    wx.showToast({
        title: `下载中... ${res.progress}%`,
        icon: 'none'
    });
    });
    
    // 2. 正确设置监听器
    updateManager.onUpdateReady = () => {
      wx.showModal({
        title: '更新提示',
        content: '新版本已准备就绪',
        success(res) {
          if (res.confirm) {
            // 应用更新
            updateManager.applyUpdate();
          }
        }
      });
    };
    
    // 3. 添加失败监听器(关键补充)
    updateManager.onUpdateFailed = () => {
      wx.showModal({
        title: '更新失败',
        content: '新版本下载失败',
        showCancel: false
      });
    };
  }
})

其他

转发给朋友

js
Page({
  onShareAppMessage() {
    return {
      title: '自定义标题',  // 必填
      path: '/pages/index?id=123',  // 携带参数
      imageUrl: 'https://example.com/share.jpg' // 建议比例 5:4
    }
  }
})

分享到朋友圈

js
Page({
  onShareTimeline() {
    return {
      title: '朋友圈标题',  // 最多12字
      query: 'id=123',    // 通过页面onLoad接收
      imageUrl: 'https://example.com/timeline.jpg'
    }
  }
})

获取微信头像

html
<!-- 页面内添加头像选择按钮 -->
<button open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
  <image mode="aspectFill" src="{{avatarUrl}}"></image>
</button>
js
Page({
  data: {
    avatarUrl: '/images/default-avatar.png'
  },
  
  onChooseAvatar(e) {
    const { avatarUrl } = e.detail
    wx.uploadFile({
      url: 'https://your.domain.com/upload', // 服务器接口
      filePath: avatarUrl,
      name: 'avatar',
      success: (res) => {
        // 获取服务器返回的头像URL
        const serverAvatar = JSON.parse(res.data).url
        this.setData({ avatarUrl: serverAvatar })
      },
      fail: (err) => {
        wx.showToast({ title: '上传失败', icon: 'error' })
      }
    })
  }
})

获取微信昵称

html
<!-- WXML文件 -->
  <input 
    type="nickname" 
    placeholder="请输入昵称" 
    value="{{nickname}}"
    bindinput="handleNicknameInput"
  />
js
// JS文件
Page({
  data: {
    nickname: '' // 存储用户昵称
  },
  
  // 处理昵称输入框失焦事件
  handleNicknameInput(e) {
    const nickname = e.detail.value.trim();
     
    // 保存昵称
    this.setData({ nickname });
    
    // 发送到服务器
    this.saveNicknameToServer(nickname);
  },
  
 // 保存昵称到服务器
  saveNicknameToServer(nickname) {
    wx.request({
      url: 'https://your-api.com/save-nickname',
      method: 'POST',
      data: {
        nickname,
        openid: wx.getStorageSync('openid')
      },
      success: (res) => {
        if (res.data.code === 200) {
          wx.setStorageSync('nickname', nickname);
        }
      }
    });
  }
})

获取手机号

html
<button open-type="getPhoneNumber" bindgetphonenumber="handleGetPhoneNumber">
  授权获取手机号
</button>
js
Page({
  data: {
    phoneLoading: false
  },
  
  onGetPhoneNumber(e) {
    const { code } = e.detail;
    
    // 用户拒绝授权处理
    if (e.detail.errMsg === "getPhoneNumber:fail user deny") {
      wx.showToast({ title: '您已拒绝授权', icon: 'none' });
      return;
    }
    
    // 防止重复提交
    if (this.data.phoneLoading) return;
    this.setData({ phoneLoading: true });
    
    wx.request({
      url: 'https://your-api.com/get-user-phone',
      method: 'POST',
      data: { code }, // 传递微信返回的code
      success: (res) => {
        if (res.data.code === 200) {
          const phone = res.data.phone;
          wx.setStorageSync('userPhone', phone);
          this.handlePhoneSuccess(phone);
        } else {
          this.showPhoneError();
        }
      },
      fail: () => {
        this.showPhoneError();
      },
      complete: () => {
        this.setData({ phoneLoading: false });
      }
    });
  },
  
  handlePhoneSuccess(phone) {
    wx.showModal({
      title: '手机号获取成功',
      content: `您的手机号:${phone}`,
      showCancel: false
    });
  },
  
  showPhoneError() {
    wx.showToast({
      title: '手机号获取失败',
      icon: 'error',
      duration: 3000
    });
  }
})

编程、设计、剪辑、摄影、画画、弹琴、读书