参考:
1. 环境准备 1.1 运行环境
运行环境
逻辑层
渲染层
iOS
JavaScriptCore
WKWebView
安卓
V8
chromium定制内核
小程序开发者工具
NWJS
Chrome WebView
1.2 官方小程序功能体验 可以查看到微信小程序所有的功能。
1.3 开发流程
注册小程序:https://mp.weixin.qq.com/cgi-bin/wx
登陆小程序后台:https://mp.weixin.qq.com/
拿到 AppID(小程序ID):【开发与服务 】-【开发管理 】,eg: wx51c55a4653bba442
安装开发者工具(稳定版):https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
扫码 开发者工具登陆,新建 小程序项目,选择目录 (自动取最后一级目录作为项目名称-提前创建),填写 AppID ,不使用云服务,模版默认选择官方推荐第一个即可。
默认 JS Skyline 模板是微信小程序的「新一代性能版模板」,在兼容原有开发体验的前提下,实现了渲染性能的跨越式提升,是目前微信小程序开发的主流推荐选择 。
本文示例以 JS 基础模版 为准。
点击创建
多人开发设置:【管理 】-【成员管理 】添加项目成员(多人开发)、体验成员(测试人员)
预览、上传,小程序后台:【管理 】-【版本管理 】查看开发版本上传情况,按需 选为体验版本 或 提交审核 (上线)
1.4 快捷键映射 打开微信开发者工具 → 菜单栏「设置」→「通用设置」→「快捷键」;或按 Ctrl + , 直接打开设置面板。
手动配置:以下是 IDEA 高频快捷键与微信开发者工具的命令映射,直接修改即可适配开发习惯,避免冲突。
IDEA 常用快捷键
功能描述
微信开发者工具命令
修改方法
Ctrl + Alt + L(Win)/ Command + Option + L(Mac)
格式化代码
格式化文档
搜索「格式化文档」→ 右键「更改键绑定」→ 按下对应组合键 → 回车确认
Ctrl + D(Win/Mac)
复制行
复制行
搜索「复制行」→ 更改键绑定为 Ctrl + D / Command + D
Alt + Enter(Win)/ Option + Enter(Mac)
触发意图 / 快速修复
触发建议
搜索「触发建议」→ 更改键绑定为 Alt + Enter / Option + Enter
Ctrl + /(Win)/ Command + /(Mac)
单行注释
添加 / 移除行注释
搜索「添加 / 移除行注释」→ 确认或修改为对应组合键
Ctrl + Shift + /(Win)/ Command + Option + /(Mac)
块注释
添加 / 移除块注释
搜索「添加 / 移除块注释」→ 更改键绑定
搜索定位:在快捷键面板顶部搜索框输入命令关键词(如「格式化」「删除行」)快速找到目标。
修改方式:选中命令 → 右键「更改键绑定」→ 按下组合键(如 Ctrl + Alt + L)→ 回车保存。
冲突处理:若提示「已被占用」,可先修改冲突快捷键,或选择「添加额外键绑定」保留多组映射。
2. 全局配置 app.json
2.1 pages 官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#pages
页面组成:①.js-逻辑 ②.wxml-结构 ③.json-配置 ④.wxss-样式
新建页面-方式1:pages/ 目录下,新建一个目录如 demo/ 目录,在 demo/ 目录下右键【新建Page】输入页面名字,不需要输入后缀,默认生成页面组成4个文件。
新建页面-方式2:在 app.json 的 pages 数组中添加 "pages/页面名称/页面名称" 即可自动创建页面并生成文件。固定第 1 个是首页。
1 2 3 4 5 6 7 8 9 { "pages" : [ "pages/demo/demo" , "pages/home/home" , "pages/order/order" , "pages/center/center" ] , ...}
2.2 window 官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window
用于设置小程序的状态栏、导航条、标题、窗口背景色。
1 2 3 4 5 6 7 8 9 10 { ... "window" : { "navigationBarTextStyle" : "white" , "navigationBarTitleText" : "小布旅游助手" , "navigationBarBackgroundColor" : "#14c145" , "enablePullDownRefresh" : true } , ...}
2.3 tabbar 官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar
如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { ... "tabBar" : { "selectedColor" : "#f60" , "backgroundColor" : "#fff" , "borderStyle" : "white" , "position" : "bottom" , "list" : [ { "pagePath" : "pages/demo/demo" , "text" : "测试" , "iconPath" : "images/camera.png" , "selectedIconPath" : "images/camera_light.png" } , ... ] } , ...}
3. 页面配置 官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html
页面中配置项在当前页面会覆盖 app.json 中相同的配置项(样式相关的配置项属于 app.json 中的 window 属性,但这里不需要额外指定 window 字段)。
如 pages/home/home
1 2 3 4 5 { "usingComponents" : { } , "navigationBarTitleText" : "首页" , "enablePullDownRefresh" : true }
4. 基础语法 4.1 标签 <view>
定位 :通用块级布局容器 ,类比 Web 中的 <div>,是小程序布局的「基础骨架」。
底层 :渲染层基于原生视图容器实现,无文本专属优化,主要负责承载其他标签(<view>/<text>/<image> 等)、实现页面结构布局。
核心作用 :划分页面区域、实现 Flex/Grid 布局、承载非文本内容,是布局的核心载体。
<text>
定位 :专属行内文本标签 ,类比 Web 中的 <span>(但比 <span> 有更多文本专属能力)。
底层 :渲染层基于原生文本控件实现,做了文本渲染、选中文本、复制文本等专属优化,仅用于包裹纯文本内容;
核心作用 :渲染页面中的文字、实现文本的专属交互(选中、复制),是文本展示的「唯一标准标签」。
<view> 是通用块级容器,主打布局承载;<text> 是专属行内文本标签,主打文本渲染与交互。
demo.wxml
1 2 3 4 5 <text > {{ 10+20 }}</text > <view > {{ 10>20 ? 'aaa' : 'bbb' }}</view >
4.2 数据绑定 官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/data.html
demo.wxml
1 2 3 4 <view > {{myname}}</view > <view id ="my-{{ids[0]}}" > 动态id-{{ids[0]}}</view > <view id ="my-{{ids[1]}}" > 动态id-{{ids[1]}}</view > <view id ="my-{{ids[2]}}" > 动态id-{{ids[2]}}</view >
demo.js
1 2 3 4 5 6 7 8 9 10 11 Page ({ data : { myname : "Jerry" , ids : ["aaa" , "bbb" , "ccc" ] }, ... })
4.3 列表渲染 wx:for, wx:key 官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/list.html
wx:for 在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item。
wx:key 如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一 的标识符。
demo.wxml
1 2 3 4 5 6 7 8 <view wx:for ="{{list}}" wx:key ="index" > {{index}} : {{item}}</view > <view wx:for ="{{list}}" wx:for-item ="a_item" wx:for-index ="a_index" wx:key ="a_index" > {{a_index}} - {{a_item}}</view >
demo.js
1 2 3 4 5 6 Page ({ data : { list : ["jerry" , "tom" , "spike" ] }, ... })
4.4 条件渲染 wx:if 官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/conditional.html
wx:if wx:if 使用 wx:if 来判断是否需要渲染该代码块。
也可以用 wx:elif 和 wx:else 来添加一个 else 块
demo.wxml
1 2 3 4 5 <view wx:if ="{{isCreated}}" > 动态创建和删除</view > <view wx:if ="{{length > 5}}" > 1 </view > <view wx:elif ="{{length > 2}}" > 2 </view > <view wx:else > 3 </view >
demo.js
1 2 3 4 5 6 7 Page ({ data : { isCreated : true , length : 4 , }, ... })
hidden hidden 会创建出节点,在 hidden=”false” 的时候,设置 DOM 节点为 display: none 以隐藏,反之则显示。
1 <view hidden ="{{isHidden}}" > 动态隐藏和显示</view >
1 2 3 4 5 6 Page ({ data : { isHidden : false }, ... })
4.5 事件绑定 bindtap bindtap 官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html
1 2 3 4 5 6 7 8 9 10 11 <button type ="primary" bindtap ="handleTap" > click1</button > <view id ="outer" bindtap ="handleTap1" > outer view <view id ="middle" catchtap ="handleTap2" > middle view - 阻止冒泡,只会触发自己的事件 <view id ="inner" bindtap ="handleTap3" > inner view - 会冒泡到上一层,会触发 handleTap2和3 </view > </view > </view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Page ({ handleTap ( ) { console .log ("click" , this .data .myname ); this .setData ({ myname : "tom" , isCreated : !this .data .isCreated }) }, handleTap1 ( ) { console .log ("handleTap1" ); }, handleTap2 ( ) { console .log ("handleTap2" ); }, handleTap3 ( ) { console .log ("handleTap3" ); }, })
evt.target & evt.currentTarget evt.target:指向 实际触发事件的那个元素 (事件的「触发源」),是用户真实点击 / 操作的节点;
evt.currentTarget:指向 绑定了该事件的那个元素 (事件的「绑定者」),是写了bindtap/catchtap等事件的节点;
只有事件绑定元素本身被直接触发 时,二者才指向同一个元素,取值一致;若触发的是绑定元素的【子元素】,二者指向不同,取值可能不同。
1 2 3 4 <view bindtap ="getParam" data-num ="100" style ="padding:20rpx; background:#f0f0f0;" > <text > 点击我(子元素)</text > </view >
1 2 3 4 5 6 7 8 9 Page ({ getParam (evt ) { const targetParam = evt.target .dataset .num ; const currentTargetParam = evt.currentTarget .dataset .num ; console .log ('evt.target.dataset.num:' , targetParam); console .log ('evt.currentTarget.dataset.num:' , currentTargetParam); } });
eg:todolist - evt.target
bindinput="func" 绑定输入事件方法,通过 evt.detail.value 拿到输入值
value="{{xxx}}" 绑定 input 输入框中的值,以按需置空
data-x="{{v}}" 事件传参 方式使用 data- 拼接自定义变量名,该参数名纯小写
evt.target.dataset.x 事件接参 方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <view class ="box" > <input type ="text" bindinput ="handleInput" value ="{{mytext}}" /> <button type ="primary" size ="mini" bindtap ="handleTapAdd" > 添加</button > </view > <view wx:for ="{{datalist}}" wx:key ="index" class ="list" > <text > {{item}}</text > <button type ="warn" size ="mini" bind:tap ="handleDelete" data-myid ="{{index}}" > 删除</button > </view > <view wx:if ="{{datalist.length === 0}}" > 暂无待办事项</view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .box { display : flex; }input { border : 1px solid gray; height : 30px ; line-height : 30px ; border-radius : 30px ; padding-left : 10px ; flex : 1 ; }.list { display : flex; flex-direction : row; }.list text{ width : 300px ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 Page ({ data : { mytext : "" , datalist : ["aaa" , "bbb" , "ccc" ], }, handleInput (evt ) { this .setData ({ mytext : evt.detail .value }) }, handleTapAdd ( ) { console .log (this .data .mytext ); this .setData ({ datalist : [...this .data .datalist , this .data .mytext ], mytext : "" , }) }, handleDelete (evt ) { console .log ("delete:" , evt.target .dataset .myid ); this .data .datalist .splice (evt.target .dataset .myid , 1 ) this .setData ({ datalist : this .data .datalist }) }, ... })
eg:高亮切换 - evt.currentTarget
evt.currentTarget.dataset.x 拿到绑定事件的事件源,即接参,可用于高亮或其他
1 2 3 4 5 6 7 8 9 10 <view class ="box" > <view wx:for ="{{datalist}}" wx:key ="index" class ="item {{ current === index ? 'active' : ''}}" bindtap ="handleTap" data-currentid ="{{index}}" > <text > {{item}}</text > </view > </view >
1 2 3 4 5 6 7 8 9 10 11 12 .box { display : flex; flex-direction : row; }.box .item { flex : 1 ; text-align : center; }.active { color : red; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Page ({ data : { datalist : ['衣服' , '裤子' , '鞋帽' , '手套' ], current : 0 }, handleTap (evt ) { console .log (evt.currentTarget .dataset .currentid ); this .setData ({ current : evt.currentTarget .dataset .currentid }) }, ... })
5. wxss 样式 官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html
定义在 app.wxss 中的样式为全局样式 ,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
rpx(responsive pixel): 可以根据屏幕宽度进行自适应。1rpx = 0.5px = 1物理像素。
即按照量取的像素值,1:1 设置 rpx 值。
1 <view class ="box" > </view >
1 2 3 4 5 6 .box { width : 750 rpx; height : 400 rpx; background-color : yellow; }
6. wxs 语法 官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxs/
语法文档:https://developers.weixin.qq.com/miniprogram/dev/reference/wxs/
1 2 3 4 5 <wxs src ="./date.wxs" module ="handleDate" /> <text > {{handleDate(startTime)}}</text >
1 2 3 data : { startTime : 1664229867258 , },
1 2 3 4 5 6 7 8 <!--pages/4 -wxs/date.wxs -->function handleDate (time ) { console .log (time); var date = getDate (time) return date.getFullYear () + "-" + (date.getMonth () + 1 ) + "-" + date.getDate () }module .exports = handleDate
案例:
1 2 3 4 5 6 7 <wxs src ="./goodFilter.wxs" module ="goodFilter" /> <view > <input bindinput ="handleInput" value ="{{mytext}}" /> <view wx:for ="{{goodFilter(goodsList, mytext)}}" wx:key ="index" > {{item}} </view > </view >
1 2 3 4 data : { goodsList : ["aaa" , "bbb" , "ccc" , "abc" , "acc" , "bcc" , "abb" , "cbb" ], mytext : "" },
1 2 3 4 5 6 7 8 9 <!--pages/4 -wxs/goodFilter.wxs -->function goodFilter (list, text ) { return list.filter (function (item ) { return item.indexOf (text) > -1 }) }module .exports = goodFilter
7. 数据请求 7.1 前提条件
请求报错:https://xxx.com 不在以下 request 合法域名列表中,请参考文档:https://developers.weixin.qq.com/miniprogram/dev/framework/ability/network.html
方式一:用于开发
配置位置:在微信开发工具中【本地设置】-【☑不校验合法域名、web-view、TLS版本以及HTTPS证书】打开,但上线时记得关掉。
只能在微信开发工具中看到,无法手机预览 和 上线查看到。
方式二:用于上线
配置位置:服务器域名请在 「小程序后台-【开发与服务】-【开发设置】-【服务器域名】」 中进行配置 安全域名。
配置生效:检查项目配置中的合法域名、并重新编译项目。
7.2 wx.request({ })
wx.request({...}) 微信小程序中的请求方式,没有跨域限制,但需要配置安全域名 。
1 2 3 4 5 6 <button type ="primary" bindtap ="handleAjax" > ajax</button > <view wx:for ="{{datalist}}" wx:key ="id" > {{item.nm}}</view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Page ({ data : { datalist : [] }, handleAjax ( ) { wx.request ({ url : 'https://m.maoyan.com/ajax/movieOnInfoList' , method : 'get' , data : {}, success : (res ) => { console .log ("success:" , res.data .movieList ); this .setData ({ datalist : res.data .movieList }) }, fail : () => { console .log ("fail" ); } }) }, ... })
8. 组件 8.1 image 官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/image.html
image 标签组件,主要是 mode 属性。
1 2 3 4 5 6 7 <button type ="primary" bindtap ="handleAjax" > ajax</button > <view wx:for ="{{datalist}}" wx:key ="id" class ="list" > <image src ='{{item.img}}' mode ="widthFix" /> <view > {{item.nm}}</view > </view >
1 2 3 4 5 6 7 8 9 10 11 12 13 .list { display : flex; flex-direction : row; margin : 20 rpx; }.list image{ width : 200 rpx; }.list view{ flex : 1 ; margin-left : 10px ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Page ({ data : { datalist : [] }, handleAjax ( ) { wx.request ({ url : 'https://m.maoyan.com/ajax/movieOnInfoList' , method : 'get' , data : {}, success : (res ) => { console .log ("success:" , res.data .movieList ); this .setData ({ datalist : res.data .movieList }) }, fail : () => { console .log ("fail" ); } }) }, ... })
8.2 swiper 官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/swiper.html
swiper 滑块视图容器(轮播图)。其中只可放置swiper-item 组件,否则会导致未定义的行为。
swiper 组件会有默认的 150px 固定高度,需要单独给 swiper 进行高度 rpx 值。
1 2 3 swiper { height : 480 rpx; }
1 2 3 4 5 6 7 <button type ="primary" bindtap ="handleTap" > ajax-swiper</button > <swiper indicator-dots ="{{true}}" circular ="{{true}}" autoplay ="{{true}}" interval ="{{2000}}" > <swiper-item wx:for ="{{datalist}}" wx:key ="index" > <image src ="{{item.banner_pic}}" mode ="widthFix" style ="width:100%" > </image > </swiper-item > </swiper >
1 2 3 4 swiper { height : 480 rpx; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Page ({ data : { datalist : [] }, handleTap ( ) { wx.request ({ url : 'https://gw.juooo.com/gw/main/pageConfig?version=6.3.11&referer=2' , method : 'post' , success : (res ) => { console .log (res.data .data .result .banner_list .home_banner ); this .setData ({ datalist : res.data .data .result .banner_list .home_banner }) } }) }, ... })
组件内下拉刷新 官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html
scroll-view 可滚动视图区域。使用竖向滚动时,需要给scroll-view 一个固定高度,通过 WXSS 设置 height。组件属性的长度单位默认为px,2.4.0 起支持传入单位(rpx/px)。
可以实现非页面级别的 下拉刷新 。
水平方向滚动需要2个条件,标签组件上 enable-flex="{{true}}" 以及样式 flex 弹性盒布局中需要设置收缩属性为0 flex-shrink: 0;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <view > 水平方向</view > <scroll-view class ="box_shuiping" enable-flex ="{{true}}" scroll-x ="{{true}}" bindscrolltolower ="handleScrollToRight" > <view class ="item_shuiping" > aaaa</view > <view class ="item_shuiping" > bbbb</view > <view class ="item_shuiping" > cccc</view > </scroll-view > <view > 垂直方向</view > <scroll-view class ="box" scroll-y ="{{true}}" bindscrolltolower ="handleScrollToLower" refresher-enabled ="{{true}}" bindrefresherrefresh ="handleRefresh" refresher-triggered ="{{isRefresh}}" > <view class ="item" > 1111</view > <view class ="item" > 2222</view > <view class ="item" > 3333</view > </scroll-view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .box { height : 300 rpx; }.item { height : 300 rpx; background-color : yellow; }.box_shuiping { height : 200px ; width : 100vw ; display : flex; flex-direction : row; }.box_shuiping .item_shuiping { width : 300px ; background-color : blue; flex-shrink : 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Page ({ data : { isRefresh : false }, handleScrollToLower ( ) { console .log ("到底了..." ); }, handleScrollToRight ( ) { console .log ("到右边了..." ); }, handleRefresh ( ) { console .log ("容器级别下拉刷新" ); setTimeout (() => { this .setData ({ isRefresh : false }) }, 1000 ) }, ... })
全局下拉刷新 如 pages/home/home.json
"enablePullDownRefresh": true 放在具体的 page json 配置里,就对应的 page 支持下拉刷新
1 2 3 4 5 { "usingComponents" : { } , "navigationBarTitleText" : "首页" , "enablePullDownRefresh" : true }
app.json
"enablePullDownRefresh": true 放在 app.json 的 window 属性中,全局所有页面都支持下拉刷新
1 2 3 4 5 6 7 8 { "pages" : [ ...] , "window" : { "backgroundTextStyle" : "dark" , ... } , ...}
下拉刷新触发事件:都需要在具体的 page.js 中处理 onPullDownRefresh() 下拉逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Page ({ ... onPullDownRefresh ( ) { setTimeout (() => { console .log ("下拉更新数据了" ); wx.stopPullDownRefresh () }, 1000 ) }, ... })
8.4 其他组件 ifcon icon 组件:https://developers.weixin.qq.com/miniprogram/dev/component/icon.html
1 2 3 4 5 <icon class ="icon-box-img" type ="success" size ="50" > </icon > <icon class ="icon-box-img" type ="info" size ="50" > </icon > <icon class ="icon-box-img" type ="warn" size ="50" color ="#C9C9C9" > </icon > <icon class ="icon-box-img" type ="warn" size ="50" > </icon >
checkbox checkbox 组件:https://developers.weixin.qq.com/miniprogram/dev/component/checkbox.html
1 2 3 4 5 6 7 8 9 10 11 12 13 <wxs src ="./sum.wxs" module ="sum" /> <view wx:for ="{{checkList}}" wx:key ="index" style ="display: flex; justify-content: space-around; padding: 10px;" > <checkbox bind:tap ="handleCheckBoxTap" data-index ="{{index}}" checked ="{{item.isChecked}}" /> <view > <view > 商品:{{item.name}}</view > <view > 价格:¥{{item.price}}</view > </view > <view > {{item.number}}</view > </view > <view > 金额计算:{{sum(checkList)}}</view >
1 2 3 4 5 6 7 8 9 10 11 <!--pages/9 -checkbox/9 -checkbox.wxs -->function sum (list ) { var total = 0 for (var i = 0 ; i < list.length ; i++) { if (list[i].isChecked ) { total += list[i].number * list[i].price } } return total }module .exports = sum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 Page ({ data : { checkList : [ { id : 1 , name : "aaa" , price : 100 , number : 11 , isChecked : true }, { id : 2 , name : "bbb" , price : 200 , number : 12 , isChecked : false }, { id : 3 , name : "ccc" , price : 300 , number : 13 , isChecked : false }, ] }, handleCheckBoxTap (evt ) { console .log ("handleCheckBoxTap index=" , evt.target .dataset .index ); var index = evt.target .dataset .index this .data .checkList [index].isChecked = !this .data .checkList [index].isChecked console .log (this .data .checkList ); this .setData ({ checkList : [...this .data .checkList ] }) }, ... })
form 组件:https://developers.weixin.qq.com/miniprogram/dev/component/form.html
等等…
8.5 自定义组件 官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/
定义与使用 根目录下创建 components/,以 navbar 为例,在 components/ 目录下创建 navbar/ 目录,再右键 新建 Component,输入 navbar 即可生成4文件结构:
1 2 3 4 5 6 <view class ="box" > <view wx:for ="{{list}}" wx:key ="index" class ="item {{current === index ? 'active' : ''}}" bindtap ="handleTap" data-index ="{{index}}" > {{item}} </view > </view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .box { display : flex; flex-direction : row; justify-content : space-around; }.box .item { flex : 1 ; text-align : center; height : 50px ; line-height : 50px ; }.active { color : red; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Component ({ properties : {}, data : { list : ["正在热映" , "即将上映" ], current : 0 }, methods : { handleTap (evt ) { this .setData ({ current : evt.currentTarget .dataset .index }) } } })
其他组件引入/复用该组件:
1 2 3 4 5 6 <!--pages/10 -selfcomponent/10 -selfcomponent.json-->{ "usingComponents" : { "navbar" : "../../components/navbar/navbar" } }
1 2 3 <view > 父组件中使用自定义组件:</view > <navbar > </navbar >
父传子 父组件:
1 2 3 4 5 6 <!--pages/10 -selfcomponent/10 -selfcomponent.json-->{ "usingComponents" : { "navbar" : "../../components/navbar/navbar" } }
1 2 3 4 5 6 7 8 9 10 <view > 父组件中使用自定义组件(传递参数 list, current 给 navbar):</view > <navbar list ="{{cateItems}}" current ="{{current}}" > </navbar > <swiper bindchange ="handleSwiperChange" > <swiper-item wx:for ="{{cateList}}" wx:key ="index" > {{item}} </swiper-item > </swiper > <view > 当前轮播索引:{{current}}</view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Page ({ data : { cateItems : ['衣服' , '裤子' , '帽子' , '手套' ], cateList : ['衣服内容' , '裤子内容' , '帽子内容' , '手套内容' ], current : 0 }, handleSwiperChange (evt ) { console .log (evt.detail .current ); this .setData ({ current : evt.detail .current }) }, ... })
子组件:navbar
1 2 3 4 5 6 <view class ="box" > <view wx:for ="{{list}}" wx:key ="index" class ="item {{current === index ? 'active' : ''}}" bindtap ="handleTap" data-index ="{{index}}" > {{item}} </view > </view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .box { display : flex; flex-direction : row; justify-content : space-around; }.box .item { flex : 1 ; text-align : center; height : 50px ; line-height : 50px ; }.active { color : red; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Component ({ properties : { list : { type : Array , value : ["正在热映" , "即将上映" ] }, current : { type : Number , value : 0 } }, data : {}, methods : { handleTap (evt ) { this .setData ({ current : evt.currentTarget .dataset .index }) } } })
效果:当前是滑动 swiper 可以从父传子,给到 navbar 同步 current 索引值,高亮 navbar(但是点击 navbar 去切换 swiper 需要子传父)。
子传父 官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html
子传父使用的是 事件 方式。(父传子 代码基础上进行优化,子组件最好不要自行修改属性值)
父组件:
定义 bindParentEvent 事件回调函数 - 监听函数
swiper 组件自带的 current 属性控制滑块位置,父与子之间的 current 完全同步通信
1 2 3 4 5 6 7 8 9 10 11 <view > 父组件中使用自定义组件:</view > <navbar list ="{{cateItems}}" current ="{{current}}" bindParentEvent ="handleChangeEvent" > </navbar > <swiper bindchange ="handleSwiperChange" current ="{{current}}" > <swiper-item wx:for ="{{cateList}}" wx:key ="index" > {{item}} </swiper-item > </swiper > <view > 当前轮播索引:{{current}}</view >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Page ({ data : { cateItems : ['衣服' , '裤子' , '帽子' , '手套' ], cateList : ['衣服内容' , '裤子内容' , '帽子内容' , '手套内容' ], current : 0 }, handleSwiperChange (evt ) { console .log (evt.detail .current ); this .setData ({ current : evt.detail .current }) }, handleChangeEvent (evt ) { console .log ("父组件 handleChangeEvent evt=" , evt.detail ); this .setData ({ current : evt.detail }) }, ... })
子组件:
自定义组件触发事件时,需要使用 triggerEvent 方法,指定事件名、detail对象和事件选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Component ({ properties : { list : { type : Array , value : ["正在热映" , "即将上映" ] }, current : { type : Number , value : 0 } }, data : {}, methods : { handleTap (evt ) { console .log (evt.currentTarget .dataset .index ); this .triggerEvent ("ParentEvent" , evt.currentTarget .dataset .index ) } } })
效果:当前滑动 swiper 可以从父传子,给到 navbar 同步 current 索引值,高亮 navbar。而且点击 navbar 也可以同步切换 swiper 内容。
slot 插槽 官方文档:slot 与 组件wxml的slot
父:
1 2 3 4 5 6 { "usingComponents" : { "top-header" : "../../components/topheader/topheader" , "footer" : "../../components/footer/footer" } }
1 2 3 4 5 6 7 8 <top-header > <button slot ="left" > 返回</button > <button slot ="right" > 菜单</button > </top-header > <button bindtap ="handleShowFooter" > 显示Footer</button > <footer wx:if ="{{isShowFooter}}" > </footer >
1 2 3 4 5 6 7 8 9 10 11 12 Page ({ data : { isShowFooter : false }, handleShowFooter ( ) { this .setData ({ isShowFooter : !this .data .isShowFooter }) }, ... })
子:footer
1 2 <view class ="footer" > footer</view >
1 2 3 4 5 6 7 8 9 .footer { width : 100% ; height : 200 rpx; position : fixed; left : 0 ; bottom : 0 ; background-color : yellow; }
生命周期-自定义组件 官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/lifetimes.html
父:
1 2 3 4 5 { "usingComponents" : { "count" : "../../components/count/count" } }
1 2 3 <view > 抢购倒计时</view > <count wx:if ="{{isCreated}}" bindMyEvent ="handleMyEvent" > </count >
1 2 3 4 5 6 7 8 9 10 11 12 Page ({ data : { isCreated : true }, handleMyEvent ( ) { this .setData ({ isCreated : !this .data .isCreated }) }, ... })
子:components/count/count.xx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Component ({ lifetimes : { attached : function ( ) { console .log ("attached" ); this .intervalId = setInterval (() => { if (this .data .count === 0 ) { this .triggerEvent ("MyEvent" ) return } this .setData ({ count : --this .data .count }) }, 1000 ) }, detached : function ( ) { console .log ("detached" ); clearInterval (this .intervalId ) }, }, ... })
9. 页面生命周期 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 Page ({ data : { datalist : [] }, handleAjax ( ) { wx.request ({ url : 'https://m.maoyan.com/ajax/movieOnInfoList' , method : 'get' , data : {}, success : (res ) => { console .log ("success:" , res.data .movieList ); this .setData ({ datalist : res.data .movieList }) }, fail : () => { console .log ("fail" ); } }) }, onLoad (options ) { console .log ("onLoad" ); this .handleAjax () }, onReady ( ) { console .log ("onReady" ); }, onShow ( ) { console .log ("onShow" ); }, onHide ( ) { console .log ("onHide" ); }, onUnload ( ) { console .log ("onUnload" ); }, onPullDownRefresh ( ) { console .log ("onPullDownRefresh" ); }, onReachBottom ( ) { console .log ("onReachBottom" ); }, onShareAppMessage ( ) { console .log ("onShareAppMessage" ); } })