简介:本资源为100个微信小程序完整源码合集,面向小程序初学者、前端开发者及希望快速掌握WXML/WXSS/JavaScript三端协同开发的实践者,解决学习缺乏真实项目参考、难以理解页面生命周期与API调用逻辑等痛点。压缩包为ZIP格式,大小185.3MB,包含全部可直接导入微信开发者工具运行的项目文件,涵盖页面结构(WXML)、样式布局(WXSS)、业务逻辑(JS)、本地存储、网络请求、地图与分享等高级功能实现,典型文件类型包括app.js/app.json/app.wxss全局配置,以及pages目录下完整的页面组件体系。已有554人学习下载,读者可从中系统掌握小程序单向数据流机制、组件化开发范式、真机调试技巧及常见性能优化策略,尤其适合通过逆向分析优质案例来夯实基础、拓展思路、复用代码结构与交互设计模式。
1. 微信小程序源码:不是拿来就能跑的“成品包”,而是需要你亲手拧紧每一颗螺丝的工程现场
很多刚接触微信小程序开发的开发者,第一次在资源站看到标着“完整可运行”“含后台+数据库”的小程序源码压缩包时,心里想的是“终于不用从零搭环境了”。结果解压、导入开发者工具、点击编译——报错:app.json 中未找到 pages/index/index;再查project.config.json,发现开发者工具版本被锁死在 1.05.2103020,而你本地是 1.07.2405100;改完配置,又卡在wx.request:fail net::ERR_CONNECTION_REFUSED,一查才发现所谓“含后台”只是本地 mock 的 3 行 JSON 数据,连 Node.js 都没装。这不是源码不行,而是你拿到的是一份带上下文依赖的工程快照,不是开箱即用的 App 安装包。它适合三类人:正在学小程序架构的在校学生、需要快速复用登录/支付/商品列表等标准模块的独立开发者、以及接手他人遗留项目做功能迭代的前端工程师。如果你只想要一个能直接上线的“小程序”,这份源码大概率会让你在第三步就放弃;但如果你愿意花 2 小时理清路径、补全依赖、重置配置,它就是你理解真实业务小程序如何组织、如何联调、如何规避线上坑的最短学习路径。
2. 源码结构解剖:从 project.config.json 到 app.js,识别每个文件的真实职责
微信小程序源码不是一堆 JS 文件的简单堆砌,而是一个有明确分层和强约束的工程结构。一份合格的源码包,必须包含project.config.json(开发者工具配置)、app.json(页面路由与窗口样式)、app.js(全局逻辑)、app.wxss(全局样式)以及至少一个合法的pages/xxx/xxx.js页面文件。但现实中,90% 的公开源码包在这几处存在隐性缺陷:路径写死、版本锁死、环境变量缺失。我们以一份典型电商类小程序源码为例,逐层拆解其真实构成与常见失配点。
2.1 project.config.json:决定你能否打开这个项目的“钥匙文件”
这是微信开发者工具识别项目的第一道关卡。它不参与运行时逻辑,但决定了你能否成功加载项目。关键字段包括:
description:"project configuration file"—— 仅描述,无实际作用setting: 包含urlCheck(是否校验 request 域名)、es6(是否转 ES5)、enhance(是否启用增强编译)等布尔开关compileType:"miniprogram"(小程序)或"plugin"(插件),必须与实际类型一致libVersion: 如"2.28.2",表示基础库最低版本,若低于此值,真机将白屏
提示:很多源码包把
libVersion锁在旧版本(如2.10.4),导致新版开发者工具提示“基础库版本过低,建议升级”,但升级后又因 API 变更(如wx.getSystemInfoSync().model在 2.25+ 返回值格式变更)引发兼容问题。这不是 bug,是作者当时开发环境的真实快照。
下面是一段经过清洗、适配当前主流环境(基础库 ≥ 2.27.0,开发者工具 ≥ 1.07.2405100)的project.config.json示例:
{ "description": "微信小程序源码工程配置", "setting": { "urlCheck": false, "es6": true, "enhance": true, "postcss": true, "preloadBackgroundData": false, "minified": true, "newFeature": true, "coverView": true, "nodeModules": true, "autoAudits": false, "showShadowRootInWxmlPanel": true, "scopeDataCheck": false, "uglifyFileName": false, "checkInvalidKey": true, "checkSiteMap": true, "uploadWithSourceMap": true, "compileHotReLoad": false, "useMultiFrameRuntime": true, "useApiHook": true, "babelSetting": { "ignore": [], "disablePlugins": [], "outputPath": "" } }, "compileType": "miniprogram", "libVersion": "2.27.0", "appid": "wx1234567890abcdef", "projectname": "demo-shop-miniprogram", "isGameTourist": false, "condition": { "search": { "current": -1, "list": [] }, "conversation": { "current": -1, "list": [] }, "game": { "currentL": -1, "list": [] }, "miniprogram": { "current": 0, "list": [ { "id": -1, "name": "首页", "pathName": "pages/index/index", "query": "" } ] } } }这段配置的关键改动点在于:
urlCheck: false:关闭域名校验,避免因未配置 request 合法域名而无法调试网络请求;libVersion: "2.27.0":设定为当前稳定支持 Promise 化 API(如wx.login().then())的最低版本;appid字段保留占位符wx1234567890abcdef,提醒你必须替换为自己的 AppID,否则真机调试会失败;condition.miniprogram.list显式声明首页路径,防止因app.json缺失或路径错误导致启动页找不到。
2.2 app.json:页面路由与窗口样式的“宪法级”配置
app.json是小程序的骨架定义文件,它不写一行逻辑,却决定整个应用能否被正确挂载。它的核心字段只有三个:pages(必填)、window(可选)、tabBar(可选)。但正是这些看似简单的字段,承载了最多的历史包袱。
pages数组必须按页面加载顺序排列,首项即为启动页。常见错误是复制粘贴时漏掉末尾逗号,或路径多写/(如"pages//index/index"),导致解析失败;window中的navigationBarTitleText若含中文,需确保文件编码为 UTF-8 无 BOM,否则开发者工具可能显示乱码;tabBar的list中每个 item 必须包含pagePath(必须是pages中已声明的路径)、text、iconPath和selectedIconPath,且两个 icon 文件必须真实存在,否则 tabBar 渲染为空白。
以下是一个精简但生产可用的app.json示例,已去除所有冗余字段,并标注关键约束:
{ "pages": [ "pages/index/index", "pages/product/list", "pages/product/detail", "pages/cart/index", "pages/user/profile" ], "window": { "navigationBarBackgroundColor": "#ffffff", "navigationBarTextStyle": "black", "navigationBarTitleText": "优品商城", "backgroundColor": "#f5f5f5", "backgroundTextStyle": "light" }, "tabBar": { "color": "#999", "selectedColor": "#1aad19", "borderStyle": "black", "backgroundColor": "#ffffff", "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "assets/images/tabbar/home.png", "selectedIconPath": "assets/images/tabbar/home-active.png" }, { "pagePath": "pages/product/list", "text": "商品", "iconPath": "assets/images/tabbar/goods.png", "selectedIconPath": "assets/images/tabbar/goods-active.png" }, { "pagePath": "pages/cart/index", "text": "购物车", "iconPath": "assets/images/tabbar/cart.png", "selectedIconPath": "assets/images/tabbar/cart-active.png" }, { "pagePath": "pages/user/profile", "text": "我的", "iconPath": "assets/images/tabbar/user.png", "selectedIconPath": "assets/images/tabbar/user-active.png" } ] }, "style": "v2", "sitemapLocation": "sitemap.json" }该配置中特别注意三点:
pages路径全部使用正斜杠/,且不以/开头(微信规范要求相对路径);tabBar图标路径统一放在assets/images/tabbar/下,避免分散在各页面目录造成管理混乱;"style": "v2"明确启用新版组件样式,确保button、input等组件渲染符合当前设计规范。
2.3 app.js:全局生命周期与状态管理的“中枢神经”
app.js是小程序的入口逻辑文件,承担着初始化、全局数据托管、登录态维护等关键任务。它导出一个对象,必须包含onLaunch(冷启动触发)、onShow(前台显示时触发)、onHide(退至后台时触发)等生命周期函数。但很多源码包在此处埋下最隐蔽的雷——把业务逻辑硬编码进onLaunch,例如直接调用wx.login()并拼接code发起网络请求,却不做fail回退,导致用户拒绝授权后整个应用卡死。
一个健壮的app.js应该做到:
- 登录流程异步化、可中断;
- 全局数据(如用户 token、购物车数量)通过
getApp().globalData统一暴露,而非在每个页面重复定义; - 网络请求封装成
request方法,内置 loading、超时、错误重试机制。
以下是经过实战验证的app.js核心骨架(已剥离具体业务,保留可复用结构):
// app.js App({ // 全局共享数据,非响应式,仅用于跨页面传递简单值 globalData: { userInfo: null, token: '', cartCount: 0, apiBase: 'https://api.example.com/v1' }, // 小程序初始化完成时触发(冷启动) onLaunch(options) { console.log('App launched with options:', options); this.checkLoginStatus(); }, // 小程序进入前台时触发(热启动/切回前台) onShow(options) { console.log('App shown with options:', options); // 可在此处刷新 token 或同步购物车数量 }, // 小程序进入后台时触发 onHide() { console.log('App hidden'); }, // 检查登录态:优先读取 storage,失效则重新登录 checkLoginStatus() { const token = wx.getStorageSync('token'); if (token && this.isValidToken(token)) { this.globalData.token = token; console.log('Token valid, skip login'); return; } this.doLogin(); }, // 执行登录流程:code → token → 用户信息 doLogin() { wx.login({ success: (res) => { if (res.code) { // 此处应调用你自己的登录接口,传 code 换 token // 示例伪代码:this.request('/auth/login', { code: res.code }) console.log('Login code obtained:', res.code); } else { console.error('wx.login failed:', res); } }, fail: (err) => { console.error('wx.login failed:', err); // 用户拒绝授权时,应引导至设置页或展示提示 wx.showModal({ title: '授权失败', content: '请允许获取用户信息以继续使用', showCancel: false }); } }); }, // 简单 token 校验(实际应结合 exp 时间戳) isValidToken(token) { return typeof token === 'string' && token.length > 10; }, // 封装 request,统一处理 loading、错误、鉴权 request(url, data = {}, method = 'GET') { return new Promise((resolve, reject) => { wx.showLoading({ title: '加载中...', mask: true }); wx.request({ url: this.globalData.apiBase + url, data, method, header: { 'Authorization': `Bearer ${this.globalData.token}`, 'Content-Type': 'application/json' }, success: (res) => { wx.hideLoading(); if (res.statusCode === 200) { resolve(res.data); } else if (res.statusCode === 401) { // token 过期,清除并跳转登录 wx.removeStorageSync('token'); this.globalData.token = ''; wx.navigateTo({ url: '/pages/auth/login' }); reject(new Error('登录已过期')); } else { reject(new Error(`HTTP ${res.statusCode}: ${res.data?.message || '请求失败'}`)); } }, fail: (err) => { wx.hideLoading(); reject(err); } }); }); } });这段代码的核心价值在于:
checkLoginStatus和doLogin分离了“检查”与“执行”,避免冷启动时强制弹窗打断用户体验;request方法返回 Promise,支持async/await写法,且自动注入Authorization头,省去每个页面手动拼接;isValidToken是轻量校验,不依赖服务端,避免每次启动都发起网络请求;- 所有
console.log保留,方便你在调试时快速定位生命周期触发时机。
3. 页面与组件:从 WXML 渲染到 WXSS 样式,还原真实业务逻辑链
小程序的页面(Page)和自定义组件(Component)是功能落地的最终载体。一份源码的价值,不在于它写了多少行 JS,而在于它如何把用户操作(点击、滑动、输入)映射为数据变化,再驱动视图更新。很多初学者误以为 WXML 就是 HTML,WXSS 就是 CSS,结果在wx:for循环里写{{item.name}}却始终为空,或给view加margin-top: 20rpx却毫无反应——这背后是小程序特有的数据绑定机制与 rpx 单位计算规则在起作用。
3.1 页面生命周期与数据绑定:为什么 setData 是唯一真相
小程序 Page 实例的生命周期函数(onLoad、onReady、onShow等)与app.js类似,但更聚焦于单个页面。其中最关键的是data对象与setData方法:所有影响视图的数据,必须通过setData设置,直接修改this.data.xxx = yyy不会触发更新。这是新手翻车第一高发区。
以商品列表页pages/product/list.js为例,其典型结构如下:
// pages/product/list.js Page({ data: { productList: [], loading: false, hasMore: true, page: 1, pageSize: 10 }, onLoad(options) { // options 是页面路径携带的 query 参数,如 ?category=shoes console.log('Page loaded with options:', options); this.setData({ page: 1 }); // 初始化页码 this.loadProducts(); }, onReady() { console.log('Page ready'); }, onShow() { console.log('Page shown'); }, // 加载商品列表 loadProducts() { if (this.data.loading || !this.data.hasMore) return; this.setData({ loading: true }); // 调用 app.js 封装的 request 方法 getApp().request('/products', { page: this.data.page, size: this.data.pageSize }, 'GET') .then(res => { const newData = { loading: false, page: this.data.page + 1 }; if (res.list && res.list.length > 0) { // 追加新数据(上拉加载场景) newData.productList = this.data.productList.concat(res.list); newData.hasMore = res.list.length === this.data.pageSize; } else { newData.hasMore = false; } this.setData(newData); }) .catch(err => { console.error('Failed to load products:', err); this.setData({ loading: false }); }); }, // 上拉触底加载更多 onReachBottom() { this.loadProducts(); }, // 下拉刷新 onPullDownRefresh() { this.setData({ page: 1, productList: [], hasMore: true }); this.loadProducts(); wx.stopPullDownRefresh(); // 必须手动停止,否则刷新动画不消失 } });这段代码体现了三个关键实践:
onLoad中不直接调用loadProducts(),而是先setData({ page: 1 }),确保数据状态与 UI 同步;loadProducts使用this.data.page获取当前页码,而非闭包捕获,避免异步回调中page值错乱;onPullDownRefresh必须调用wx.stopPullDownRefresh(),这是微信强制要求,否则下拉动画卡死。
对应 WXML 模板(pages/product/list.wxml)需严格匹配data结构:
<!-- pages/product/list.wxml --> <view class="container"> <!-- 商品列表 --> <view wx:for="{{productList}}" wx:key="id" class="product-item" bindtap="goToDetail">// components/cart-badge/cart-badge.js Component({ properties: { // 接收外部传入的 count 值 count: { type: Number, value: 0, observer: '_countChanged' } }, data: { showBadge: false }, lifetimes: { attached() { // 组件挂载时,尝试从全局获取初始值 const app = getApp(); if (typeof app.globalData.cartCount !== 'undefined') { this.setData({ count: app.globalData.cartCount }); } } }, methods: { _countChanged(newVal) { this.setData({ showBadge: newVal > 0 }); } } });对应 WXML(components/cart-badge/cart-badge.wxml):
<!-- components/cart-badge/cart-badge.wxml --> <view class="cart-badge" wx:if="{{showBadge}}"> <text class="badge-text">{{count}}</text> </view>对应 WXSS(components/cart-badge/cart-badge.wxss):
/* components/cart-badge/cart-badge.wxss */ .cart-badge { position: relative; display: inline-block; } .badge-text { position: absolute; top: -6rpx; right: -6rpx; background-color: #e64340; color: white; font-size: 20rpx; width: 36rpx; height: 36rpx; line-height: 36rpx; text-align: center; border-radius: 50%; padding: 0; }使用方式(在任意页面 WXML 中):
<!-- 引入组件 --> <import src="/components/cart-badge/cart-badge.wxml"/> <view> <cart-badge count="{{cartCount}}"></cart-badge> </view>注意:组件使用前需在页面 JSON 中声明:
{ "usingComponents": { "cart-badge": "/components/cart-badge/cart-badge" } }
这样做的好处是:
- 购物车数量变更时,只需修改
getApp().globalData.cartCount,所有引用该组件的页面自动更新; - 样式与逻辑完全隔离,后续要改成红点+文字、或增加动画,只需改组件内部,不影响页面;
observer监听count变化,比在每个页面onShow中手动setData更可靠。
3.3 WXSS 与 rpx:为什么 750rpx = 屏幕宽度,但你的按钮还是偏小?
WXSS 是小程序的样式语言,语法接近 CSS,但单位rpx(responsive pixel)是核心差异点:以 750rpx 为屏幕宽度基准,所有设备按比例缩放。这意味着在 iPhone 6/7/8(375px 宽)上,1rpx = 0.5px;在 iPhone 12 Pro Max(428px 宽)上,1rpx ≈ 0.566px。但很多源码包直接写width: 100px,导致在高端机上按钮被压缩得看不见。
正确的做法是:
- 所有宽度、高度、内边距、外边距、字体大小,一律使用
rpx; font-size推荐范围:24–32rpx(正文)、36–44rpx(标题);border使用1rpx实现 0.5px 细线效果(微信底层做了像素对齐);- 避免
position: absolute+top/left精确像素定位,改用flex布局。
以下是一段经过真机测试的按钮 WXSS(pages/index/index.wxss):
/* pages/index/index.wxss */ .container { padding: 20rpx; } .banner { width: 100%; height: 300rpx; margin-bottom: 20rpx; } .btn-primary { width: 100%; height: 80rpx; background-color: #1aad19; color: white; font-size: 32rpx; border-radius: 8rpx; line-height: 80rpx; text-align: center; margin-top: 20rpx; } .btn-outline { width: 100%; height: 80rpx; border: 1rpx solid #1aad19; color: #1aad19; font-size: 32rpx; border-radius: 8rpx; line-height: 80rpx; text-align: center; margin-top: 20rpx; }关键点说明:
.banner高度设为300rpx,在所有机型上占屏幕约 40%,视觉比例恒定;.btn-primary高度80rpx,行高line-height: 80rpx,确保文字垂直居中;border: 1rpx solid #1aad19在 retina 屏上渲染为真正的 0.5px 细线,比0.5px更可靠;- 所有
margin、padding均用rpx,避免父子容器因单位混用导致布局塌陷。
4. 网络请求与 API 联调:从 mock 数据到真实后端,绕过域名、HTTPS、证书三座大山
小程序的网络请求(wx.request)是业务闭环中最容易卡死的一环。很多源码包写着“含后台”,结果你打开pages/user/profile.js,发现getApp().request('/user/info')指向http://localhost:3000/api/user/info—— 这在真机上必然失败,因为微信强制要求:所有 request 必须使用 HTTPS 协议,且域名必须在「小程序后台 → 开发管理 → 服务器域名」中提前配置。这不是限制,而是安全底线。本节带你一步步把源码中的 mock 请求,替换成可联调的真实后端,同时避开所有常见拦截点。
4.1 域名配置:三步走通「开发者工具 → 真机」全流程
微信对 request 域名的管控分为两层:
- 开发者工具层:可通过
project.config.json中的urlCheck: false临时关闭校验,仅限调试; - 真机运行层:必须在小程序管理后台配置合法域名,且协议必须为
https,端口只能是443(不可写:443)。
配置步骤(务必按顺序):
- 登录 微信公众平台 →「开发管理」→「开发设置」→「服务器域名」;
- 在「request 合法域名」栏添加你的后端地址,如
https://api.yourdomain.com(注意:不能带路径,不能带http://,不能带端口); - 保存后,等待 5–10 分钟生效(微信有缓存),期间真机调试会提示“不在合法域名列表中”。
提示:本地开发时,推荐使用
ngrok或localtunnel将本地https://localhost:3000映射为公网https地址(如https://abc123.ngrok.io),然后将该地址填入后台。这样既满足 HTTPS 要求,又能实时调试本地后端。
4.2 HTTPS 证书:Nginx 配置中常被忽略的三行关键指令
即使你已购买 SSL 证书并部署到 Nginx,仍可能遇到真机请求返回net::ERR_CERT_AUTHORITY_INVALID。原因通常是:
- 证书链不完整(只部署了域名证书,未部署中间证书);
- Nginx 未开启
ssl_trusted_certificate; - 未强制 HTTP 跳转 HTTPS。
一个生产级 Nginx 配置片段(/etc/nginx/conf.d/api.yourdomain.com.conf)如下:
server { listen 443 ssl http2; server_name api.yourdomain.com; # SSL 证书路径(根据你的实际路径修改) ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem; # 关键:指定信任的中间证书链 ssl_trusted_certificate /etc/letsencrypt/live/api.yourdomain.com/chain.pem; # 强制使用 TLS 1.2+ ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; # 开启 OCSP Stapling(提升证书验证速度) ssl_stapling on; ssl_stapling_verify on; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } # HTTP 强制跳转 HTTPS server { listen 80; server_name api.yourdomain.com; return 301 https://$server_name$request_uri; }其中ssl_trusted_certificate是多数人遗漏的关键项。Let’s Encrypt 的fullchain.pem已包含域名证书+中间证书,但 Nginx 要求显式指定信任链,否则部分安卓机型会校验失败。
4.3 接口联调实战:用 Postman 模拟小程序 Header,定位 401/403 错误根源
当wx.request返回401 Unauthorized或403 Forbidden,不要急着改代码,先用 Postman 模拟相同请求,确认是前端问题还是后端权限逻辑问题。
假设小程序请求如下:
getApp().request('/user/info', {}, 'GET') // 实际发出的请求头包含: // Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... // Content-Type: application/jsonPostman 配置步骤:
- Method 选
GET,URL 填https://api.yourdomain.com/user/info; - Headers 标签页,添加两行:
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...(从开发者工具 Network 面板复制)Content-Type:application/json;
- Send,观察响应状态码与 body。
常见定位结论:
- 若 Postman 返回
200,但小程序返回401→ 问题在小程序端 token 未正确注入,检查app.js中request方法的header构造; - 若 Postman 也返回
401,但curl -H "Authorization: Bearer xxx"返回200→ 后端框架(如 Express)可能未正确解析Authorization头,检查中间件是否漏写req.headers.authorization解析逻辑; - 若 Postman 返回
403,且 body 提示 “IP not allowed” → 后端做了 IP 白名单,需将微信服务器出口 IP 段加入白名单(微信官方文档提供最新 IP 段列表)。
5. 避坑指南:五个血泪经验换来的高频报错与根治方案
拿到一份微信小程序源码,最耗时间的往往不是写代码,而是解决那些“看起来很奇怪、搜不到答案、重启十次才好”的玄学问题。以下是我接手过 17 个不同来源小程序项目后,总结出的五大高频坑,每一条都附带现象、根本原因与可立即执行的根治方案,帮你省下至少 8 小时无效搜索。
5.1 现象:开发者工具显示“编译成功”,真机扫码却白屏,控制台无任何报错
原因:app.json中pages数组首项路径错误,或该路径下缺少xxx.wxml/xxx.js/xxx.wxss三件套。微信真机会严格校验页面完整性,而开发者工具为兼容性会降级处理。
根治方案:
- 打开
app.json,确认pages[0]的路径(如"pages/index/index"); - 进入对应目录
pages/index/,检查是否存在index.wxml、index.js、index.wxss三个文件(.json可选); - 若缺失任一文件,从其他页面复制模板并清空内容,或删除该路径并修正
pages数组。
5.2 现象:wx.request在开发者工具正常,真机返回fail net::ERR_CONNECTION_REFUSED
原因:后端服务监听127.0.0.1:3000,未绑定0.0.0.0,导致局域网内其他设备(包括手机)无法访问;或防火墙阻止了端口。
根治方案:
- 后端启动命令改为
node server.js --host 0.0.0.0 --port 3000(Express/Koa 通用); - Ubuntu/CentOS 执行
sudo ufw allow 3000;Mac 执行sudo pfctl -f /etc/pf.conf(确保端口开放); - 手机浏览器访问
http://[电脑局域网IP]:3000/test(如http://192.168.1.100:3000/test),确认可通。
5.3 现象:自定义组件properties传值后,observer不触发,this.data.xxx始终为初始值
原因:properties中type类型声明与传入值类型不一致。例如声明type: Number,但 WXML 中传count="{{'5'}}"(字符串),微信不会自动转换,observer直接跳过。
根治方案:
- 在组件 WXML 中,确保传值类型匹配:
<cart-badge count="{{5}}"></cart-badge>(数字不加引号); - 若必须传字符串,
properties改为type: String,并在observer中手动parseInt; - 使用
this.triggerEvent主动通知父组件,而非依赖observer。
5.4 现象:wx:for列表渲染后,点击某一项,e.currentTarget.dataset.id为undefined
原因:style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />