微信小程序学习
记录本人在学习过程中觉得需要重复复习的点
目录
一、页面之间的信息传递
1)父传子
2)子传父
3)任意两个页面之间
1. 全局数据共享(Global Data)
2.页面间数据传递
3.使用 Storage(持久化存储)
各种任意两个页面信息传递方式的对比:
二、 data属性值之间如何互相使用
错误写法:
正确解法:
一、在 onLoad 或 onShow 中动态赋值
三、生命周期函数:
1.作用:
2.核心函数及执行顺序
四、路由导航
五、点击图片或某个组件想要实现页面跳转
1)直接绑定点击事件(最基础)
3)结合前面所学组件信息传递,通过跳转实现数据传递
六、自定义组件的使用
细节1:自定义组件怎么在外部定义格式
细节2:自定义组件的生命周期
1.组件自身生命周期函数
2. 所在页面触发的生命周期函数(需在 pageLifetimes 中定义)
细节3.实例:
小程序项目代码:
小程序代码
一、页面之间的信息传递
1)父传子
通过 **properties
** 属性传递
// 父组件.wxml
<child-component myProp="{{parentData}}"></child-component>// 子组件.js
Component({properties: {myProp: {type: String, // 类型value: '' // 默认值}},observers: {'myProp': function(newVal) {// 监听属性变化}}
})
其中observes用来监听传递对象的某个属性值,可以对属性值进行格式化等操作或处理
Component({properties: {// 接收父组件传递的商品对象goods: {type: Object,value: {}},// 接收父组件传递的索引index: {type: Number,value: 0}},observers: {// 监听商品数据变化(例如价格格式化)'goods.price'(price) {this.setData({ formattedPrice: `¥${price.toFixed(2)}` });}},
<view class="goods-card" bind:tap="onTap"><image src="{{goods.image}}" class="goods-image" /><view class="goods-info"><text class="goods-name">{{goods.name}}</text><text class="goods-price">{{formattedPrice}}</text></view>
</view>
2)子传父
// 子组件.js
this.triggerEvent('myEvent', { data: value }, { bubbles: true /* 是否冒泡 */ });// 父组件.wxml
<child-component bind:myEvent="onMyEvent"></child-component>// 父组件.js
Page({onMyEvent(e) {const value = e.detail.data; // 获取子组件传递的值}
})
3)任意两个页面之间
1. 全局数据共享(Global Data)
通过 app.js
的 globalData
存储全局状态:
/ app.js
App({globalData: {sharedData: '初始值'}
});// 组件A.js 设置数据
const app = getApp();
app.globalData.sharedData = '新值';B组件获取data: {selectedGoodsInfo: null, // 存储选中的商品信息name:null,},onShow() {// 从全局数据中获取选中的商品信息const app = getApp();if (app.globalData && app.globalData.selectedGoodsInfo) {this.setData({selectedGoodsInfo: app.globalData.selectedGoodsInfo});// 清除全局数据,避免影响下次选择app.globalData.selectedGoodsInfo = null;}},
2.页面间数据传递
若组件位于不同页面,可通过路由参数或页面跳转传递:
//页面A传递比较复杂的参数时
// pages/pageA/pageA.js
Page({goToPageB() {const productId = 123;const productName = "手机";// 跳转并传递参数(参数拼接在 URL 中)wx.navigateTo({url: `/pages/pageB/pageB?id=${productId}&name=${encodeURIComponent(productName)}`});}
});// 页面A跳转时传递参数
wx.navigateTo({url: '/pages/pageB?param=123'
});// 页面B.js 获取参数
Page({/*** 页面的初始数据*/data: {name:null},/*** 生命周期函数--监听页面加载*/onLoad(options) {const name=options.namethis.setData({name//或者直接写为name:options.name})},
)}
3.使用 Storage(持久化存储)
// 组件A.js 存储数据
//wx.setStorageSync('key', 'value');
wx.setStorageSync('sharedData', newBalance);// 页面B的 JS
Page({data: {sharedValue: ''},// 生命周期:页面加载时读取数据onLoad() {this.loadStorageData();},// 生命周期:页面显示时重新读取(确保数据最新)onShow() {this.loadStorageData();},// 读取 Storage 数据loadStorageData() {// 同步读取try {const value = wx.getStorageSync('sharedData');if (value) {this.setData({ sharedValue: value });} else {this.setData({ sharedValue: '暂无数据' });}} catch (error) {console.error('读取失败:', error);}
各种任意两个组件信息传递方式的对比:
信方式 应用场景 典型示例 适用性 全局数据(Global Data) 多个组件需要共享全局状态,且数据更新频率较低。 用户登录状态、全局配置(如主题色)、应用级别设置(如语言)。 简单场景,数据共享范围广,但需注意避免滥用(频繁更新可能导致维护困难)。 事件总线(Event Bus) 跨组件实时通知,组件间需要解耦,一对多通信。 消息推送、状态同步(如购物车数量更新)、跨页面组件联动(如A组件操作触发B组件刷新)。 复杂场景,适合组件分散、交互频繁的情况,需注意及时销毁监听避免内存泄漏。 页面间数据传递 通过路由参数或页面跳转传递一次性数据。 商品详情页跳转时传递商品ID、表单提交后返回列表页携带状态参数。 简单数据传递,仅限于页面跳转场景,数据量不宜过大(URL长度限制)。 页面实例中转 同一页面内多个独立组件需要共享临时数据,且无需持久化。 页面内的筛选组件和列表组件同步筛选条件、表单组件和预览组件实时交互。 同一页面内组件间简单通信,依赖页面实例,不适合跨页面。 Storage(本地存储) 需要持久化存储的数据,且多个组件或页面需读取同一份数据。 用户偏好设置(如夜间模式)、缓存登录凭证、离线数据暂存。 数据需要长期保存,读写频繁时注意性能(同步接口可能阻塞渲染)。 状态管理库(如MobX) 复杂应用需要集中式状态管理,支持响应式更新,跨组件/页面高效同步数据。 大型电商应用(购物车、订单状态)、实时协作类应用(如多人编辑文档)。 超复杂场景,需引入第三方库,适合中大型项目,对代码结构有较高要求。
二、 data属性值之间如何互相使用
错误写法:
data: {sharedValue: '',xinxi:[{title:"this.sharedValue",subTitle:"我的余额",}]
}
在微信小程序中,data
内的字段在初始化时无法直接互相引用
正确解法:
有以下几种方案来解决:
一、在 onLoad
或 onShow
中动态赋值
Page({// ...其他代码...onShow(){// 从 data 中获取 sharedValue,设置到 xinxi[0].titlethis.setData({'xinxi[0].title': this.data.sharedValue});}
});
二、使用 observers
监听 sharedValue
变化(动态更新)
Page({data: {sharedValue: '默认值',xinxi: [{subTitle: '我的余额',title: '' // 初始为空,后续通过 observer 更新}]},// 监听 sharedValue 变化observers: {'sharedValue': function(newValue) {this.setData({'xinxi[0].title': newValue // 将新值赋给 title});}},// 示例:修改 sharedValue(如按钮点击事件)updateValue() {this.setData({sharedValue: '新值'});}
});
三、生命周期函数:
1.作用:
生命周期函数用于在 特定时机自动执行代码,例如:
- 页面加载时初始化数据
- 页面显示时刷新内容
- 页面隐藏时释放资源
- 应用启动时获取用户信息
2.核心函数及执行顺序
函数名 | 触发时机 | 执行次数 | 典型场景 | 注意事项 |
---|---|---|---|---|
onLoad | 页面 首次加载(创建)时触发 | 一次 | 接收页面参数、初始化数据 | 无法操作 DOM(视图未渲染) |
onShow | 页面 显示时 触发 | 多次 | 刷新数据、更新用户状态 | 可能频繁触发(如返回页面) |
onReady | 页面 初次渲染完成 时触发 | 一次 | 操作 DOM、初始化地图/图表等组件 | 确保视图已存在 |
onHide | 页面 隐藏时 触发(跳转/切后台) | 多次 | 暂停计时器、保存临时状态 | 不可见但未被销毁 |
onUnload | 页面 销毁时 触发(关闭或跳转) | 一次 | 释放资源(如 WebSocket 连接) | 页面实例将被回收 |
首次进入页面 → onLoad → onShow → onReady
离开页面(跳转/切后台) → onHide
重新进入页面 → onShow
关闭页面 → onUnload
类型 | 函数名 | 触发时机 | 执行次数 | 核心用途 |
---|---|---|---|---|
页面 | onLoad | 页面首次加载时 | 一次 | 初始化数据、获取参数 |
onShow | 页面显示时 | 多次 | 刷新动态数据 | |
onReady | 页面初次渲染完成时 | 一次 | 操作 DOM、初始化组件 | |
onHide | 页面隐藏时 | 多次 | 暂停操作、保存临时状态 | |
onUnload | 页面销毁时 | 一次 | 释放资源 | |
应用 | onLaunch | 小程序初始化完成时 | 一次 | 全局配置、检查更新 |
onShow | 小程序启动或从后台返回时 | 多次 | 恢复应用状态 | |
onHide | 小程序切到后台时 | 多次 | 保存全局状态 |
四、路由导航
目标页面类型 | 正确 API | 说明 |
---|---|---|
普通页面 | wx.navigateTo | 保留当前页面,新页面压入页面栈 |
普通页面(不可返回) | wx.redirectTo | 关闭当前页面,打开新页面 |
导航栏(tabBar)页面 | wx.switchTab | 切换到 tabBar 页面 |
所有页面关闭后跳转 | wx.reLaunch | 关闭所有页面,打开新页面 |
API | 说明 |
---|---|
wx.navigateTo | 保留当前页面,跳转到新页面(可返回) |
wx.redirectTo | 关闭当前页面,跳转到新页面(不可返回) |
wx.switchTab | 跳转到 tabBar 页面(需在 app.json 的 tabBar 中定义) |
wx.reLaunch | 关闭所有页面,打开新页面 |
wx.navigateBack | 返回上一页或多层页面(通过 delta 参数控制) |
关于navigateBack的使用:
// /pages/payment/payment.js
Page({onPaymentSuccess() {// 支付成功后返回原页面wx.navigateBack({ delta: 1 }); // 返回上一页}
});
五、点击图片或某个组件想要实现页面跳转
1)直接绑定点击事件(最基础)
<image src="/images/button.png" mode="widthFix" bindtap="navigateToPage" data-url="/pages/detail/detail?id=123"
/>
Page({navigateToPage(e) {const url = e.currentTarget.dataset.url; // 获取 data-url 参数wx.navigateTo({url: url});}
});
2)使用 navigator
组件包裹图片(推荐)
<navigator url="/pages/detail/detail?id=123"><image src="/images/button.png" mode="widthFix" />
</navigator>
3)结合前面所学组件信息传递,通过跳转实现数据传递
通过点击事件传递动态参数(如商品 ID、用户信息)。
<image src="/images/product.png" bindtap="goToDetail" data-product-id="456" data-type="promotion"
/>
Page({goToDetail(e) {const productId = e.currentTarget.dataset.productId;const type = e.currentTarget.dataset.type;wx.navigateTo({url: `/pages/detail/detail?id=${productId}&type=${type}`});}
});
目标页面获取参数:
// /pages/detail/detail.js
Page({onLoad(options) {console.log(options.id); // 456console.log(options.type); // promotion}
});
六、自定义组件的使用
细节1:自定义组件怎么在外部定义格式
使用 externalClasses
外部样式类
组件内部:
// 自定义组件 component.js
Component({externalClasses: ['container-class', 'title-class'], // 声明允许外部传入的类名
});
<!-- 组件 WXML -->
<view class="container-class"><text class="title-class">标题</text><slot></slot>
</view>
使用该组件的外部:
(注意:如果组件内部定义了样式,想要覆盖不要忘了加 !important)
<!-- 页面 WXML -->
<custom-componentcontainer-class="custom-container" title-class="custom-title"
/>
/* 页面 WXSS */
.custom-container {padding: 20rpx;background: #f5f5f5;
}.custom-title {color: #007bff;font-size: 32rpx;
}
细节2:自定义组件的生命周期
组件的生命周期函数分为两类:组件自身生命周期 和 所在页面触发的生命周期。以下是核心函数:
1.组件自身生命周期函数
生命周期函数 | 触发时机 | 执行次数 | 典型场景 |
---|---|---|---|
created | 组件实例 刚创建 时触发 | 一次 | 初始化非响应式数据(非 data 字段) |
attached | 组件 被添加到页面节点树 时触发 | 一次 | 访问父组件数据、初始化与页面相关的逻辑 |
ready | 组件 视图层布局完成 后触发 | 一次 | 操作 DOM、初始化第三方库(如地图) |
moved | 组件 被移动到节点树其他位置 时 | 多次 | 极少数场景(如动态调整节点顺序) |
detached | 组件 被从页面节点树移除 时触发 | 一次 | 清理定时器、取消事件监听 |
2. 所在页面触发的生命周期函数(需在 pageLifetimes
中定义)
生命周期函数 | 触发时机 | 典型场景 |
---|---|---|
show | 组件所在页面 显示时 触发 | 刷新数据、恢复动画 |
hide | 组件所在页面 隐藏时 触发 | 暂停动画、保存临时状态 |
resize | 组件所在页面 尺寸变化时 触发 | 调整布局(如屏幕旋转) |
书写方法:
// components/my-component/my-component.js
Component({// 组件自身生命周期lifetimes: {created() {console.log('组件实例刚创建,此时还不能调用 setData');this.internalData = '非响应式数据'; // 适合初始化非 data 字段},attached() {console.log('组件被添加到页面节点树');const parentData = this.getPageData(); // 获取父页面数据示例this.setData({ parentData });},ready() {console.log('组件视图层渲染完成');this.initMap(); // 初始化地图组件},detached() {console.log('组件被移除');clearInterval(this.timer); // 清理定时器}},// 页面触发的生命周期pageLifetimes: {show() {console.log('页面显示,组件恢复');this.resumeAnimation(); // 恢复动画},hide() {console.log('页面隐藏,组件暂停');this.pauseAnimation(); // 暂停动画},resize(size) {console.log('页面尺寸变化', size);this.adjustLayout(); // 调整布局}},methods: {// 自定义方法getPageData() {const pages = getCurrentPages();return pages[pages.length - 1].data;},initMap() {this.mapCtx = wx.createMapContext('map', this);}}
});
细节3.实例:
接下来以我封装的组件为例进行细节拆分
wxml
<!--components/goods.wxml-->
<view style="width: 95%;margin: 25rpx auto;background-color: white; border-radius: 5px;box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.08);height: 220rpx;position: relative;" class="custom-class" wx:for="{{goods}}" wx:key="index" use-slot><slot name="{{'good'+index}}"></slot><van-button round type="info" style="position: absolute;right: 20rpx;bottom: 20rpx;" wx:if="{{isShow}}" bind:tap="onSelectItem" data-index="{{index}}">{{btncont}}</van-button>
</view>
js
Component({externalClasses: ['custom-class', 'title-class', 'content-class'],options:{//设置当前自定义组件使用多插槽模式,默认为falsemultipleSlots:true},/*** 组件的属性列表*/properties: {goodsNum:{type:Number,value:4},isShow:{type:Boolean,value:true},btncont:{type:String,value:"去评价"}},/*** 组件的初始数据*/data: {goods:[]},lifetimes:{attached(){//代表该组件被初始化并且加载,此时完成数组的数据添加操作let items = [];for(let i = 0; i < this.properties.goodsNum; i++){items.push(i);}//setData微信小程序用来修改data数据的方法,该方法会将给定的对象和data原来的数据进行对象合并,合并之后强制页面进行动态刷新this.setData({goods: items});}},/*** 组件的方法列表*/methods: {// 处理选择按钮点击onSelectItem(e) {const index = e.currentTarget.dataset.index;// 触发自定义事件,将选中的index传递给父页面this.triggerEvent('selectItem', { index });}}
})
data和properties区别
特性 | **data ** | **properties ** |
---|---|---|
定义位置 | 组件内部的 data 字段 | 组件内部的 properties 字段 |
用途 | 存储组件 内部状态 | 接收 父组件传递的只读参数 |
初始化时机 | 组件创建时初始化 | 父组件传递数据时初始化(未传则用默认值) |
数据来源 | 组件内部定义 | 父组件通过属性传入 |
可修改性 | 组件内部可通过 setData 修改 | 不可直接修改(需父组件更新) |
类型校验 | 无 | 支持类型校验(type 字段) |
默认值设置 | 直接在 data 中赋值 | 通过 value 字段设置默认值 |
通过传入的goodsNum,结合wx:for的使用即实现了创建不同多个商品数量。
那么在外部当中怎么像插槽当中传入数据呢?
外部的使用
<Good goodsNum="{{2}}" btncont="去拼团" custom-class="my-custom-style" bind:selectItem="steptopintuan"><view wx:for="{{goods}}" wx:key="index" slot="{{'good'+index}}"><image src="{{item.imgUrl}}" mode="widthFix" style="width: 100%;"/><view style="text-align: center;font-size: large;font-weight: 550;">{{item.title}}</view><view style="text-align: center;margin-top: 10px;color: #ccc;">{{item.subtitle}}</view><view style="text-align: center;margin-top: 10px;"> {{item.time}}</view><view style="text-align: left;margin-top: 10px; display: flex;"><view style="color: red;padding-left: 25px;"> {{item.price}} </view><view style="color: #ccc;padding-left:25px;text-decoration: line-through"> {{item.oldprice}} </view><view style="color: #ccc; margin-left: 30px;">已团{{item.sales}}件</view></view></view>
</Good>
通过slot=“{{}}”实现对应的绑定即可。
后续想到再补充……