Skip to content

微信小程序开发手册

原生起步

UnoCSS

按照教程快速配置

TIP

有时需要在某个页面下创建components文件夹存放页面组件,教程的 glob pattern 匹配不到该文件夹下的 wxml 文件,需要更改下:unocss pages/**/{*.wxml,components/*.wxml} -c unocss.config.js --watch -o unocss.wxss

生成全新的 unocss.wxss 文件比较简单,但原生小程序没有编译过程,导致 wxml 写的 class 是什么就是什么,比如 class="bg-[red]",unocss 生成的类名为.bg-_lfl_red_lfr_,而 wxml 中的类名没有变化,自然就匹配不上了

因此要解决这个问题,方案一就是按教程中的配置transformer,去改变 wxml 中的类名。它的弊端也很明显,把原来可读性高的类名改成编译后的了,增加了维护成本

方案二就是在 wxml 中写类名时,尽量考虑可读性高的替代方案:

  • # -> hex

  • :/ -> _

    bg-#81ecec/50替代为bg-hex-81ecec_50

  • . -> _dl_

  • 针对hover:active:,设置separators分隔符(默认配置是__

    因此hover:bg-red-500替代为hover__bg-red-500

基础颜色设置

app.json中的backgroundColor设置的是页面下拉显示的背景色而不是页面背景色

要设置页面背景色,在全局 wxss 文件中以page选择器设置

页面路径配置和跳转

配置

app.json中配置pagestabBar.list字段时,路径为相对路径,比如下边两种形式都是合法的:

json
{
  "pages": ["pages/index/index", "home/home"]
}

可以理解为相对app.json的位置有pageshome文件夹,pages下还有index文件夹,其下有个index文件;同理home文件夹下有个home文件(可以理解为:无论是页面还是组件,引用时都要具体到其 js/json 文件,省略后缀)

跳转

无论是switchTab还是navigateTo这类导航,url可以是相对的也可以是绝对的

建议统一为绝对路径,以上述为例,假如你要跳转index页面,wx.switchTab({ url: 'pages/index/index' })是错误的,因为没有/开头代表的是相对路径

开发页面

Component构造页面时,额外注意事项

页面/组件统一用Component构建,页面相关函数放在methods[1]

此时页面 json 文件要包含usingComponents定义,可以为{} [2](如果不定义这个字段,会使得页面的 this 对象的原型稍有差异 [3]

页面跳转传参

Componentproperties可用来接收页面传参,如/pages/index/index?paramA=123&paramB=xyzparamAparamB可通过properties获取到 [2:1]

访问形式就是:this.data.paramApropertiesdata字段都是通过this.data.访问);如果在组件生命周期中访问值,最好是在attached及之后的钩子里访问,测试了一个created还访问不到传的值

TIP

properties中声明的 query 属性,type 一般为String。这是因为跳转方法传递参数只能通过url属性传递,而该属性值只能是String

除了String,可以声明为Number,这样/path/to?timestamp=157352323this.data.timestamp会自动格式化为Number类型

而数组和对象就不太清楚行不行了(暂时没试),即使不行,可以在传递和接收时,用JSON.parseJSON.stringify进行转换

页面生命周期

页面生命周期onLoad -> onShow -> onReady,之后可能触发onHideonUnload。除了这些,还有许多如onPullDownRefresh这样的页面事件函数

开发组件

起步仨文件,json中声明{ "component": true },并且样式隔离设置成“页面影响组件,组件不影响页面”:{ "styleIsolation": "apply-shared" } [4]

js
Component({
  data: {
    msg: 'hello',
  },

  // 组件生命周期 - 一共6个 - 也可以写在顶层兼容低版本,但不推荐
  lifetimes: {
    created() {}, // 在组件实例刚刚被创建时执行,注意此时不能调用 setData
    attached() {}, // 在组件实例进入页面节点树时执行
    ready() {}, // 在组件布局完成后执行
    moved() {}, // 在组件实例被移动到节点树另一个位置时执行
    detached() {}, // 在组件实例被从页面节点树移除时执行
    error(err) {}, // 每当组件方法抛出错误时执行
  },

  // 组件所在页面生命周期 - 一共4个 - 如果你的页面是通过Component构造的,可以在methods中添加页面生命周期而不是pageLifetimes
  // 自定义tabbar组件并不会触发页面的生命周期
  pageLifetimes: {
    show() {}, // 组件所在的页面被展示时执行
    hide() {}, // 组件所在的页面被隐藏时执行
    resize(sizeObj) {}, // 组件所在的页面尺寸变化时执行
    routeDone() {}, // 组件所在页面路由动画完成时执行
  }

  // 纯数据字段,就是不加监听器,可以通过this.setData修改,但感觉没必要,可以直接this.data.xxx = xxx
  options: {
    pureDataPattern: /^_/, // 指定所有 _ 开头的数据字段为纯数据字段
  },
  data: {
    _a: 'xxx',
  },

  // 事件通信
  methods: {
    onTap: function () {
      // 父组件可以 bindmyevent 也可以 bind:myevent 接收
      // detailObj === evt.detail
      // option: { bubbles: false, composed: false, capturePhase: false } 默认值都是false
      this.triggerEvent('myevent', detailObj, option)
    }
  }
})
json
{
  "component": true,
  "styleIsolation": "apply-shared"
}
html
<view class="bg-red-100"> {{ msg }}, world </view>

如果开发一个 组件 A 要包含一个第三方组件 B,会发现:在A.wxss写的样式是无法影响到组件 B 的

组件模板和样式中,提供了externalClasses引用页面或父组件的样式两种方式,都不适合当前场景,这俩方法的前提是 组件 A 和 B 都是你自己维护的才行

其实第三方组件基本都能被页面样式覆盖,所以样式还在A.wxss中维护,哪个页面用 A 组件了,在页面.wxss@import "path/to/A.wxss"即可

WXML 语法

html
<!-- boolean -->
<view checked="{{ false }}"></view>
<view checked></view>
<!-- 绑定事件 -->
<view bindtap="onTap"></view>
<!-- for -->
<!-- 默认就是index、item,显式指定一般用于嵌套 -->
<view
  wx:for="{{ array }}"
  wx:key="index"
  wx:for-index="index"
  wx:for-item="item"
>
  {{ index }}: {{ item.message }}
</view>

WXS 语法

可以直接写在 wxml 中的<wxs>标签内,也可以.wxs为后缀的文件

可以把它作为 computed 在 wxml 中使用,增强模板。

html
<wxs module="tools">
var msg = 'hello'
var bar = function (d) { return d }
module.exports = { msg: msg, bar: bar }
</wxs>
<view> {{tools.msg}} </view>
<view> {{tools.bar('world')}} </view>

当报错时,考虑下是不是语法不支持,目前遇到的不支持的语法:

npm 支持

官方的文档说的有些复杂,一些实际场景操作起来很快

  • 安装某个组件库

    npm i xxx -S --production,然后工具 -> 构建 npm,即可在项目根目录下生成一个miniprogram_npm的文件夹,其下是dependencies的包

    比如安装了tdesign-miniprogram,构建完之后使用button组件,就可以写为(不用加miniprogram_npm):

    json
    {
      "usingComponents": {
        "t-button": "tdesign-miniprogram/button/button"
      }
    }

使用插件

大部分插件都需要在 后台(左下角头像) -> 账号设置 -> 第三方设置 的插件管理中先搜索、申请、添加

但是像“腾讯位置服务地图选点”这个,就搜不到

可以通过微信服务市场直接添加,https://fuwu.weixin.qq.com/service/detail/000c2a50a58c206b3d1957a2d5b015

同理,其它插件也可以通过此形式试试

  • 腾讯的地图选点插件的坑

    腾讯的地图选点插件需要腾讯地图的 key,核心功能依赖地点搜索,个人开发者实际额度每日 PV200,查一下就没了,不把个人开发者当人。而高德免费额度更是调整到了 100,个人开发者 g 了

    image

setData

js
this.setData({
  'array[2].message': 'newVal',
  'a.b.c.d': 'newVal',
})

组件

scroll-view(skyline)

type="custom" height="固定值",这俩直接设置,避免各种问题。(如果scroll-x时不设置type="custom"显示就会有问题;在低版本 skyline 中不设置height,内部元素无法撑开)

scroll-view就是 flex 容器。scroll-x就是flex-direction: rowscroll-y就是flex-direction: col。其下的直接子元素自动填充其交叉轴高度。

html
<scroll-view
  scroll-x
  type="custom"
  class="h-100"
>
  <view class="w-50 bg-red-200"></view>
  <view class="w-50 bg-green-200"></view>
</scroll-view>

image

html
<scroll-view
  scroll-y
  type="custom"
  class="h-200"
>
  <view class="h-50 bg-red-200"></view>
  <view class="h-50 bg-green-200"></view>
</scroll-view>

image

如果需要设置gap,参见skyline 下的 flex 相关问题

scroll-y设置align-items不生效,不支持吗?

picker

picker 是没有宽高的,如果其子元素也没有宽高(比如空值且没有设定宽高),那永远也点不出来

当和第三方组件 cell 搭配时,应该是<picker><cell>...</cell></picker>这种

全屏页面顶部数据获取

通过页面配置设置navigationStylecustom

image

  • 黄色:状态栏高度
  • 绿色:胶囊距离顶部高度
  • 蓝色:胶囊高度
  • 灰色:导航栏高度

状态栏高度(黄色)可以通过wx.getWindowInfo().statusBarHeight获取(API -> 基础 -> wx.getWindowInfo

胶囊高度(蓝色)和距离顶部高度(绿色)可以通过wx.getMenuButtonBoundingClientRect()heighttop获取(API -> 界面 -> wx.getMenuButtonBoundingClientRect

最终得出:导航栏高度(灰色)= (绿色 - 黄色) * 2 + 蓝色

javascript
const { statusBarHeight } = wx.getWindowInfo()
const { top, height } = wx.getMenuButtonBoundingClientRect()
const navBarHeight = (top - statusBarHeight) * 2 + height

应用

Taro中,直接注册到全局

js
import Taro from '@tarojs/taro'
import { setGlobalDataPlugin } from '@tarojs/taro'
const app = createApp({ ... })
const { statusBarHeight } = Taro.getWindowInfo()
const { top, height } = Taro.getMenuButtonBoundingClientRect()
app.use(setGlobalDataPlugin, {
  statusBarHeight,
  navBarHeight: (top - statusBarHeight) * 2 + height,
})
vue
<script setup>
import Taro from '@tarojs/taro'
const { statusBarHeight, navBarHeight } = Taro.getApp()
</script>

参考链接


  1. 指南 -> 小程序框架 -> 注册页面 -> #使用 Component 构造器构造页面 ↩︎

  2. 指南 -> 自定义组件 -> Component 构造器 -> #使用 Component 构造器构造页面 ↩︎ ↩︎

  3. 指南 -> 自定义组件 -> 介绍 -> #注意事项 ↩︎

  4. 指南 -> 自定义组件 -> 组件模板和样式 -> #组件样式隔离 ↩︎