小程序自定义组件库

面向微信小程序的可复用组件实践:评分组件、防抖按钮,含 WXML/JS 完整实现与使用方式。

场景:组件复用 技术栈:微信小程序原生组件 来源:GitHub 参考

在线演示:评分组件 + 防抖按钮

3当前评分
60%评分百分比
0点击次数
0实际触发

评分组件:点击星星打分,点击当前分值可清零(toggle)。

防抖按钮(窗口 800ms,leading + trailing):狂点按钮,观察「点击次数」与「实际触发」的区别。

等待操作:先点星星评分,再狂点防抖按钮。

代码示例:评分组件

js
// components/rating/rating.js —— 可复用评分组件
Component({
  properties: {
    value: { type: Number, value: 0 },   // 当前分数
    max:   { type: Number, value: 5 },   // 满分
    size:  { type: Number, value: 24 }   // 星号尺寸 rpx
  },
  data: { list: [1, 2, 3, 4, 5] },
  methods: {
    onTap(e) {
      const n = e.currentTarget.dataset.n;
      this.setData({ value: n });
      this.triggerEvent("change", { value: n });
    }
  }
});
wxml
<!-- components/rating/rating.wxml -->
<view class="rating" role="radiogroup">
  <view wx:for="{{list}}" wx:key="*this"
        class="star {{item <= value ? 'on' : ''}}"
        data-n="{{item}}" bindtap="onTap"
        style="width:{{size}}rpx;height:{{size}}rpx;font-size:{{size}}rpx">
    ★
  </view>
</view>
js
// components/debounce-btn/debounce-btn.js —— 防抖按钮
Component({
  properties: { text: { type: String, value: "提交" } },
  data: { locked: false },
  methods: {
    onTap() {
      if (this.data.locked) return;      // 已锁定则忽略
      this.setData({ locked: true });    // 300ms 内防重入
      this.triggerEvent("tap");
      setTimeout(() => this.setData({ locked: false }), 300);
    }
  }
});

操作步骤

  1. 在 components/rating/ 目录创建 rating.json 声明组件,rating.wxml/js/wxss 实现。
  2. 页面 usingComponents 注册后即可 <rating value="4"> 使用。
  3. 评分变化通过 triggerEvent("change") 通知父页面。
  4. 防抖按钮组件避免支付/提交被重复点击。

完整可运行工程

可直接运行(node 自测)需微信开发者工具导入纯逻辑层零第三方依赖

运行环境:Node.js 14+ 跑逻辑自测(零 npm 依赖);小程序端需「微信开发者工具」导入工程目录(已含 project.config.json / app.json,AppID 用测试号即可)。组件逻辑抽成 *-core.js(UMD),同一份代码既可被小程序引用,也能在 Node 下自测。

先执行 node tools/selftest.js 验证评分与防抖逻辑,再用开发者工具打开工程看真实交互。

miniapp-components/ ├── README.md 导入步骤、组件用法与配置项说明 ├── project.config.json 开发者工具工程配置(appid 用测试号即可) ├── app.json / app.js / app.wxss 小程序入口与全局样式 ├── sitemap.json 索引配置 ├── package.json 自测脚本入口(node tools/selftest.js) ├── components/ │ ├── rating/ 评分组件(wxml / wxss / js / json + rating-core.js) │ └── debounce-button/ 防抖按钮组件(含 debounce-core.js) ├── pages/index/ 演示页:引用两个组件并打印事件 └── tools/selftest.js Node 自测:评分边界 / 防抖窗口 / throttle 全覆盖

README.md

导入步骤、组件属性表、自测命令与需先改的配置项。

markdown
# miniapp-components · 微信小程序可复用组件库(可运行工程)

两个开箱即用的自定义组件 + 独立纯逻辑层,**先在电脑上跑通最小用例**,再导入开发者工具即可看到真实 UI。

## 目录结构

```
miniapp-components/
├── project.config.json            开发者工具工程配置(appid 为 touristappid 游客模式)
├── app.json / app.js / app.wxss   小程序入口与全局样式
├── sitemap.json
├── package.json                   仅用于电脑端自测(npm test)
├── pages/index/                   演示页:调用两个组件
├── components/rating/             星级评分组件
│   ├── rating.js / .wxml / .wxss / .json
│   └── rating-core.js             纯逻辑(UMD,可被 Node 直接 require)
├── components/debounce-button/    防抖按钮组件
│   ├── debounce-button.js / .wxml / .wxss / .json
│   └── debounce-core.js           leading+trailing 防抖 / 节流纯逻辑
└── tools/selftest.js              电脑端最小用例验证
```

## 可直接运行

**① 电脑端(零依赖,先验证逻辑)**

```bash
cd miniapp-components
node tools/selftest.js      # 或 npm test
```

预期输出末尾为 `ALL CHECKS PASSED`,覆盖:评分 toggle/percent/clamp、防抖窗口内合并、
尾随执行、cancel 清除、throttle 周期丢弃。

**② 微信开发者工具(看真实 UI)**

1. 打开「微信开发者工具 → 导入项目」,目录选择本工程根目录;
2. AppID 选择「测试号 / 游客模式」(工程内已填 `touristappid`);
3. 编译后首页即可点击星星评分、狂点按钮观察 `点击 N 次 / 实际触发 M 次` 的防抖效果。

## 需先改的配置项

| 位置 | 配置项 | 说明 |
|---|---|---|
| `project.config.json` | `appid` | 换成你自己的小程序 AppID;仅本地体验可保留 `touristappid` |
| `project.config.json` | `libVersion` | 基础库版本,按开发者工具提示调整 |
| `components/rating/rating.js` | `max` / `size` | 星星总数与尺寸(属性可传,默认 5 / 24) |
| `components/debounce-button/debounce-button.js` | `wait` | 防抖窗口,演示页传 800ms |
| `app.wxss` | 颜色令牌 | 演示用深色基底,接入自有设计体系时替换 |

## 依赖清单

- 小程序端:**无任何第三方 npm 依赖**(`package.json` 的 `dependencies` 为空);
- 电脑端自测:Node.js ≥ 14,零依赖。

## 组件用法

```xml
<rating value="{{score}}" max="5" bind:change="onRatingChange" />
<debounce-button text="提交" wait="{{800}}" bind:action="onAction" />
```

```js
onRatingChange(e) { this.setData({ score: e.detail.value }); },   // detail: { value, percent }
onAction(e) { console.log(e.detail.count); }                      // detail: { count, wait }
```

## 来源参考

- 微信小程序自定义组件官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/
- 组件间通信与事件(triggerEvent):https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html
- 开发者工具导入项目说明:https://developers.weixin.qq.com/miniprogram/dev/devtools/importproject.html

project.config.json

开发者工具工程配置:appid 处填测试号即可编译。

json
{
  "description": "miniapp-components 组件库演示工程(可直接用微信开发者工具导入)",
  "packOptions": { "ignore": [{ "type": "folder", "value": "tools" }] },
  "setting": {
    "urlCheck": false,
    "es6": true,
    "enhance": true,
    "postcss": true,
    "minified": true
  },
  "compileType": "miniprogram",
  "libVersion": "3.5.0",
  "appid": "touristappid",
  "projectname": "miniapp-components",
  "miniprogramRoot": "./"
}

app.json

小程序入口配置:注册页面与组件页面依赖。

json
{
  "pages": ["pages/index/index"],
  "window": {
    "navigationBarTitleText": "NOCAU 组件库",
    "navigationBarBackgroundColor": "#0d0f12",
    "navigationBarTextStyle": "white",
    "backgroundColor": "#0d0f12",
    "backgroundTextStyle": "dark"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

app.js

入口逻辑:仅保留最小启动代码,便于直接复用。

javascript
// app.js —— 小程序入口
App({
  onLaunch() {
    // 演示工程无远端逻辑;保留生命周期便于扩展
    console.log('[miniapp-components] launched');
  },
  globalData: { version: '1.0.0' }
});

app.wxss

全局样式:使用主题变量占位,不写死颜色。

css
/* app.wxss —— 全局样式(演示工程深色基底,可按需换成浅色) */
page {
  background: #0d0f12;
  color: #e6e8ec;
  font-size: 28rpx;
  line-height: 1.6;
}

.page {
  padding: 32rpx;
}

.card {
  margin-bottom: 32rpx;
  padding: 28rpx;
  border: 1rpx solid #23262c;
  border-radius: 16rpx;
  background: #15181d;
}

.card__title {
  margin-bottom: 20rpx;
  color: #6fb3a8;
  font-size: 30rpx;
  font-weight: 600;
}

.tip {
  color: #8b93a1;
  font-size: 24rpx;
}

sitemap.json

搜索索引配置(默认全部允许索引)。

json
{
  "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
  "rules": [{ "action": "allow", "page": "*" }]
}

package.json

自测脚本入口:npm test -> node tools/selftest.js。

json
{
  "name": "miniapp-components",
  "version": "1.0.0",
  "private": true,
  "description": "微信小程序可复用组件库演示工程(rating 星级评分 / debounce-button 防抖按钮)",
  "scripts": {
    "test": "node tools/selftest.js"
  },
  "engines": { "node": ">=14" },
  "dependencies": {}
}

components/rating/rating.js

评分组件:属性、事件与逻辑层的桥接。

javascript
// components/rating/rating.js —— 可复用评分组件
// 纯逻辑放在同目录 rating-core.js,便于在电脑上用 node 直接单测(tools/selftest.js)
const core = require('./rating-core.js');

Component({
  properties: {
    value: { type: Number, value: 0, observer(v) { this.setData({ list: core.starList(this.data.max), percent: core.percent(v, this.data.max) }); } },
    max: { type: Number, value: 5, observer(v) { this.setData({ list: core.starList(v), percent: core.percent(this.data.value, v) }); } },
    size: { type: Number, value: 24 },          // 星号尺寸(rpx 基准 ×2)
    readonly: { type: Boolean, value: false }
  },

  data: {
    list: [1, 2, 3, 4, 5],
    percent: 0
  },

  lifetimes: {
    attached() {
      this.setData({ list: core.starList(this.data.max), percent: core.percent(this.data.value, this.data.max) });
    }
  },

  methods: {
    onTap(e) {
      if (this.data.readonly) return;
      const n = Number(e.currentTarget.dataset.n);
      const next = core.toggle(this.data.value, n, this.data.max);
      this.setData({ value: next, percent: core.percent(next, this.data.max) });
      this.triggerEvent('change', { value: next, percent: this.data.percent });
    }
  }
});

components/rating/rating-core.js

评分的纯逻辑层(UMD):toggle / percent / starList,含边界与除零保护。

javascript
/**
 * rating-core.js —— 评分组件的纯逻辑层(UMD)
 * 之所以拆出纯逻辑:小程序端 require 使用,电脑端可用 node 直接 require 做单元测试,
 * 无需启动微信开发者工具即可验证“最小用例”。
 */
(function (root, factory) {
  var api = factory();
  if (typeof module === 'object' && module.exports) {
    module.exports = api;            // Node.js / 小程序 CommonJS
  } else {
    root.RatingCore = api;           // 浏览器/其他环境兜底
  }
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
  'use strict';

  function clamp(v, lo, hi) {
    v = Number(v);
    if (isNaN(v)) return lo;
    return v < lo ? lo : (v > hi ? hi : v);
  }

  /** 点击第 index 颗星(1-based):点当前值则清零,否则设为该值 */
  function toggle(current, index, max) {
    var cur = clamp(current, 0, max);
    var idx = clamp(index, 0, max);
    return idx === cur ? 0 : idx;
  }

  /** 评分百分比,用于宽度类样式(如进度条) */
  function percent(value, max) {
    if (!max || max <= 0) return 0;
    return Math.round(clamp(value, 0, max) / max * 100);
  }

  /** 生成星星列表(视图层 wx:for 使用) */
  function starList(max) {
    var list = [];
    for (var i = 1; i <= max; i++) list.push(i);
    return list;
  }

  return { clamp: clamp, toggle: toggle, percent: percent, starList: starList };
});

components/rating/rating.wxml

评分组件结构:按 starList 渲染星星。

html
<!-- components/rating/rating.wxml —— 评分组件视图 -->
<view class="rating" role="radiogroup" aria-label="评分">
  <view
    wx:for="{{list}}"
    wx:key="*this"
    class="star {{item <= value ? 'on' : ''}}"
    style="font-size: {{size * 2}}rpx;"
    data-n="{{item}}"
    bindtap="onTap"
  >★</view>
  <text class="rating__text">{{value}} / {{max}}({{percent}}%)</text>
</view>

components/rating/rating.wxss

评分组件样式:尺寸随 rpx 自适应。

css
/* components/rating/rating.wxss */
.rating {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}

.star {
  padding: 0 6rpx;
  color: #3a3f47;
  line-height: 1;
  transition: color .15s ease, transform .15s ease;
}

.star.on {
  color: #e0af68;
  transform: scale(1.06);
}

.rating__text {
  margin-left: 16rpx;
  color: #8b93a1;
  font-size: 24rpx;
}

components/rating/rating.json

评分组件声明:component 为 true。

json
{
  "component": true,
  "usingComponents": {}
}

components/debounce-button/debounce-button.js

防抖按钮:leading + trailing 合并、可选 throttle。

javascript
// components/debounce-button/debounce-button.js —— 防抖按钮组件
// 逻辑复用 debounce-core.js(可被 node 单测),组件只负责 UI 与事件派发。
const { createDebouncer } = require('./debounce-core.js');

Component({
  properties: {
    text: { type: String, value: '提交' },
    wait: { type: Number, value: 800 },        // 防抖窗口(ms)
    size: { type: String, value: 'normal' },   // normal | small
    disabled: { type: Boolean, value: false }
  },

  data: {
    pressed: 0,      // 实际触发次数(被合并的点击不计数)
    tapped: 0,       // 用户点击次数(含被合并的)
    hint: ''
  },

  lifetimes: {
    attached() {
      this._deb = createDebouncer(this.properties.wait, () => this._emit(), {});
      this._count = 0;
    },
    detached() {
      if (this._deb) this._deb.cancel();
    }
  },

  methods: {
    _emit() {
      this._count += 1;
      this.setData({ pressed: this._count, hint: '已触发 ' + this._count + ' 次' });
      this.triggerEvent('action', { count: this._count, wait: this.properties.wait });
    },

    onTap() {
      if (this.properties.disabled) return;
      const fired = this._deb.call();
      this.setData({
        tapped: this.data.tapped + 1,
        hint: fired ? '立即触发' : '窗口内合并,等待尾随触发'
      });
    }
  }
});

components/debounce-button/debounce-core.js

防抖纯逻辑层(UMD):可注入定时器,便于 Node 下逐帧自测。

javascript
/**
 * debounce-core.js —— 防抖/节流纯逻辑层(UMD)
 * 采用「leading + trailing」策略:窗口内首次点击立即生效,窗口内的后续点击被合并为
 * 最后一次尾随执行,避免用户狂点导致重复提交。
 *
 * 时间与定时器均支持注入(opts.now / opts.setTimeout / opts.clearTimeout),
 * 因此可以在 node 中用假时钟做确定性单元测试,无需真实等待。
 */
(function (root, factory) {
  var api = factory();
  if (typeof module === 'object' && module.exports) {
    module.exports = api;
  } else {
    root.DebounceCore = api;
  }
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
  'use strict';

  function createDebouncer(wait, fn, opts) {
    opts = opts || {};
    var now = opts.now || function () { return Date.now(); };
    var setT = opts.setTimeout || function (cb, ms) { return setTimeout(cb, ms); };
    var clearT = opts.clearTimeout || function (id) { clearTimeout(id); };

    var timer = null;
    var last = -Infinity;      // 上次真正执行的时间
    var pendingArgs = null;

    function invoke(args) {
      last = now();
      timer = null;
      pendingArgs = null;
      fn.apply(null, args || []);
    }

    return {
      /** 返回 true 表示本次调用立即执行,false 表示被合并等待尾随执行 */
      call: function () {
        var args = Array.prototype.slice.call(arguments);
        var t = now();
        if (t - last >= wait) {
          invoke(args);                   // leading:窗口外的点击直接生效
          return true;
        }
        pendingArgs = args;               // 窗口内:记录最后一次参数
        if (timer) clearT(timer);
        timer = setT(function () { if (pendingArgs) invoke(pendingArgs); }, wait - (t - last));
        return false;
      },
      /** 取消未决的尾随执行(组件销毁时调用) */
      cancel: function () {
        if (timer) clearT(timer);
        timer = null;
        pendingArgs = null;
      },
      get pending() { return !!timer; }
    };
  }

  /** 节流:固定周期首调用立即执行,周期内其余调用直接丢弃(不做尾随) */
  function throttle(wait, fn, now) {
    now = now || function () { return Date.now(); };
    var last = -Infinity;
    return function () {
      var t = now();
      if (t - last < wait) return false;
      last = t;
      fn.apply(null, arguments);
      return true;
    };
  }

  return { createDebouncer: createDebouncer, throttle: throttle };
});

components/debounce-button/debounce-button.wxml

防抖按钮结构:禁用态与 loading 态。

html
<!-- components/debounce-button/debounce-button.wxml -->
<view class="dbtn-wrap">
  <button
    class="dbtn dbtn--{{size}}"
    type="primary"
    disabled="{{disabled}}"
    bindtap="onTap"
  >{{text}}</button>
  <text class="dbtn__hint">点击 {{tapped}} 次 / 实际触发 {{pressed}} 次 · {{hint}}</text>
</view>

components/debounce-button/debounce-button.wxss

防抖按钮样式。

css
/* components/debounce-button/debounce-button.wxss */
.dbtn-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.dbtn {
  margin: 0;
  border-radius: 12rpx;
  background: #2f6f63;
  color: #eaf2f0;
  font-size: 28rpx;
}

.dbtn--small {
  width: 200rpx;
  font-size: 24rpx;
}

.dbtn[disabled] {
  background: #2a2f36;
  color: #6b7280;
}

.dbtn__hint {
  margin-top: 12rpx;
  color: #8b93a1;
  font-size: 24rpx;
}

components/debounce-button/debounce-button.json

防抖按钮组件声明。

json
{
  "component": true,
  "usingComponents": {}
}

pages/index/index.js

演示页逻辑:接收组件事件并写入日志数组。

javascript
// pages/index/index.js —— 组件库演示页:评分 + 防抖按钮
Page({
  data: {
    score: 3,
    wait: 800,
    events: []
  },

  onRatingChange(e) {
    this.setData({ score: e.detail.value });
    this._log('rating:change -> ' + e.detail.value + '(' + e.detail.percent + '%)');
  },

  onDebounceAction(e) {
    this._log('debounce:action -> 第 ' + e.detail.count + ' 次(窗口 ' + e.detail.wait + 'ms)');
  },

  _log(text) {
    const events = this.data.events.slice(0, 5);
    events.unshift(text);
    this.setData({ events });
  }
});

pages/index/index.wxml

演示页结构:两个组件各来一组示例。

html
<!-- pages/index/index.wxml —— 演示页 -->
<view class="page">
  <view class="card">
    <view class="card__title">评分组件 rating</view>
    <rating value="{{score}}" max="5" bind:change="onRatingChange" />
    <view class="tip">当前分值 {{score}};点击已选中的星号可清零。</view>
  </view>

  <view class="card">
    <view class="card__title">防抖按钮 debounce-button</view>
    <debounce-button text="提交(狂点试试)" wait="{{wait}}" bind:action="onDebounceAction" />
    <view class="tip">窗口 800ms:窗口外首次点击立即生效,窗口内连点合并为一次尾随触发。</view>
  </view>

  <view class="card">
    <view class="card__title">事件日志</view>
    <view wx:if="{{events.length === 0}}" class="tip">暂无事件,交互后在此显示。</view>
    <view wx:for="{{events}}" wx:key="index" class="tip">· {{item}}</view>
  </view>
</view>

pages/index/index.wxss

演示页样式。

css
/* pages/index/index.wxss */
.activity-log {
  margin-top: 8rpx;
}

pages/index/index.json

演示页配置:usingComponents 注册两个组件。

json
{
  "navigationBarTitleText": "组件库演示",
  "usingComponents": {
    "rating": "/components/rating/rating",
    "debounce-button": "/components/debounce-button/debounce-button"
  }
}

tools/selftest.js

Node 自测脚本:15 项断言覆盖评分边界、防抖窗口与 cancel/throttle。

javascript
#!/usr/bin/env node
/**
 * tools/selftest.js —— 电脑端最小用例验证(无需微信开发者工具)
 * 运行:node tools/selftest.js   (或 npm test)
 *
 * 覆盖:评分逻辑(toggle/percent/starList/clamp)与防抖逻辑(leading+trailing、
 * 窗口内合并、cancel、throttle),全部通过输出 ALL CHECKS PASSED。
 */
'use strict';

const rating = require('../components/rating/rating-core.js');
const { createDebouncer, throttle } = require('../components/debounce-button/debounce-core.js');

let failed = 0;
function check(name, cond, extra) {
  console.log('  [' + (cond ? 'OK' : 'FAIL') + '] ' + name + (cond ? '' : '  => ' + JSON.stringify(extra)));
  if (!cond) failed++;
}

/** 可注入的假时钟,让防抖测试不依赖真实等待 */
function makeClock() {
  let t = 0, seq = 0;
  const timers = new Map();
  return {
    now: () => t,
    setTimeout: (fn, ms) => { const id = ++seq; timers.set(id, { fn, at: t + ms }); return id; },
    clearTimeout: (id) => { timers.delete(id); },
    tick: (ms) => {
      t += ms;
      Array.from(timers.entries()).forEach(([id, o]) => {
        if (o.at <= t) { timers.delete(id); o.fn(); }
      });
    },
    get size() { return timers.size; }
  };
}

console.log('rating-core:');
check('toggle(3,3,5) 点击当前值应清零', rating.toggle(3, 3, 5) === 0, rating.toggle(3, 3, 5));
check('toggle(0,4,5) 应设为 4', rating.toggle(0, 4, 5) === 4, rating.toggle(0, 4, 5));
check('toggle(3,9,5) 越界应被 clamp 到 5', rating.toggle(3, 9, 5) === 5, rating.toggle(3, 9, 5));
check('percent(3,5) = 60', rating.percent(3, 5) === 60, rating.percent(3, 5));
check('percent(5,0) 除零保护 = 0', rating.percent(5, 0) === 0, rating.percent(5, 0));
check('starList(5) 长度 5', rating.starList(5).length === 5, rating.starList(5));
check('clamp(NaN,0,5) 回退下界 0', rating.clamp(NaN, 0, 5) === 0, rating.clamp(NaN, 0, 5));

console.log('debounce-core(leading + trailing):');
{
  const c = makeClock();
  let n = 0;
  const deb = createDebouncer(100, () => n++, c);
  const r1 = deb.call();
  const r2 = deb.call();
  const r3 = deb.call();
  check('窗口外首击立即执行', r1 === true && n === 1, { r1, n });
  check('窗口内连击被合并(未立即执行)', r2 === false && r3 === false && n === 1, { r2, r3, n });
  c.tick(100);
  check('尾随执行合并为 1 次', n === 2, { n });
  c.tick(50);
  check('尾随执行后无残留定时器', c.size === 0, c.size);
  c.tick(100);
  const r4 = deb.call();
  check('超过窗口后再次立即执行', r4 === true && n === 3, { r4, n });
}

console.log('debounce-core(cancel / throttle):');
{
  const c = makeClock();
  let n = 0;
  const deb = createDebouncer(100, () => n++, c);
  deb.call(); deb.call();
  deb.cancel();
  c.tick(500);
  check('cancel 后尾随不再执行', n === 1 && c.size === 0, { n, size: c.size });
}
{
  const c = makeClock();
  let n = 0;
  const th = throttle(100, () => n++, c.now);
  const a = th(), b = th();
  c.tick(120);
  const d = th();
  check('throttle 周期内丢弃、周期后放行', a === true && b === false && d === true && n === 2, { a, b, d, n });
}

console.log(failed === 0 ? 'ALL CHECKS PASSED' : 'SELF TEST FAILED (' + failed + ')');
process.exit(failed === 0 ? 0 : 1);

可直接运行 / 需先改的配置项:

  • 可直接运行:node tools/selftest.js 或 npm test(零依赖,全部断言应有 [OK])。
  • 需先改·project.config.json 的 appid:换成你自己的 AppID,或直接用「测试号」导入。
  • 需先改·组件属性:rating 的 max/value、debounce-button 的 wait(默认 500ms)按业务调整。
  • 导入方式:微信开发者工具 → 导入项目 → 选择本工程目录(无需 npm install)。
复制结果为 # ===== 文件名 ===== 分隔的多文件文本,按文件名分别保存即可;也可逐块用右上角“复制代码”单独取用。

来源参考