微信小程序基础
1.介绍及安装
1.1介绍
小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。它的主要开发语言是 JavaScrip。
1.2安装及使用
小程序进行开发时,需要先安装开发者工具,申请账号等操作,具体详见https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/getstart.html
2.小程序入门
2.1创建并预览
在安装完成微信开发者工具后,新建项目选择小程序项目,选择代码存放的硬盘路径,填入刚刚申请到的小程序的 AppID,点击顶部菜单编译在微信开发者工具中预览第一个小程序,预览图如左图,目录结构如右图:
除了可以在开发者工具中预览之外,还可以在手机上进行预览。点击菜单的真机调试,微信扫描二维码后即可查看。另外,在开发过程中,如果涉及到修改配置文件等信息时,推荐使用微信开发工具(有提示),而对于页面的开发推荐使用其他的开发工具。比如需要新建一个页面时,可在微信开发者工具中编辑app.json,在里面添加页面路径,那么就会自动创建页面相关的文件;再例如配置tarbar时都会出现提示。
2.2JSON 配置
JSON 是一种数据格式,并不是编程语言,在小程序中,JSON扮演的静态配置的角色。在小程序运行之前就决定了小程序一些表现,需要注意的是小程序是无法在运行过程中去动态更新JSON 配置文件从而发生对应的变化的。相比于XML ,JSON格式最大的优点是易于人的阅读和编写,通常不需要特殊的工具,就能读懂和修改,是一种轻量级的数据交换格式。
2.2.1 小程序配置app.json
app.json
是当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。其部分内容如下:
{ "pages":[ "pages/index/index", "pages/logs/logs" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "Weixin", "navigationBarTextStyle":"black" } }
除了以上属性外,还要其他的属性,说明如下:
属性 | 类型 | 必填 | 说明 |
pages | String[] | 是 | 描述当前小程序所有页面路径,也就是指定小程序页面在哪个目录 |
window | Object | 否 | 定义小程序所有页面的顶部背景颜色,文字颜色定义等 |
tarBar | Object | 否 | 底部tab栏的设计 |
entryPagePath
|
String | 否 | 小程序的默认启动路径,如果不填,将默认使用pages中的第一个页面 |
useExtendedLib
|
Object | 否 | 引入第三方的插件 |
(1)window常用的设置
1)backgroundTextStyle:下拉 loading 的样式,仅支持 dark / light。需要和enablePullDownRefresh共同使用才能生效
"window":{ "backgroundTextStyle":"dark", "enablePullDownRefresh": true }
2)navigationBarBackgroundColor:导航栏背景颜色,仅支持rgb(如#fff),不支持颜色变量(如red)
"navigationBarBackgroundColor": "#00BFFF",
效果图如下:
3)navigationBarTextStyle:导航栏标题颜色,仅支持 black / white,默认是black
"navigationBarTextStyle":"white"
4)navigationBarTitleText:导航栏的标题文字
"navigationBarTitleText": "我的微信小程序"
(2)tarBar常用设置
一般情况下,这个导航栏放在底部。tarBar的有一个list,其中 list 接受一个数组,只能配置最少 2 个、最多 5 个 tab。tab 按数组的顺序排序,每个项都是一个对象
"tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath":"img/icon/index.png", "selectedIconPath": "img/icon/index-active.png" }, { "pagePath": "pages/my/my", "text": "我的", "iconPath":"img/icon/my.png", "selectedIconPath": "img/icon/my-active.png" } ] }
效果图如下:
pagePath:指定页面的路径;text:显示的文字;iconPath:未激活时的图标,需自定义图片。图片存放路径img文件夹与pages文件夹必须同级;selectedIconPath:激活时的图标。
除了list属性外,还有其他的属性,如需要则可自行设置,列举如下:
属性 | 说明 |
color | tab 上的文字默认颜色,仅支持十六进制颜色 |
selectedColor | tab 上的文字选中时的颜色,仅支持十六进制颜色 |
backgroundColor | tab 的背景色,仅支持十六进制颜色 |
borderStyle | tabbar 上边框的颜色, 仅支持black/white,默认是black |
position | tabBar 的位置,仅支持bottom/top,默认是bottom |
2.2.2工具配置 project.config.json
小程序开发者工具在每个项目的根目录都会生成一个 project.config.json
,在工具上做的任何配置都会写入到这个文件,当重新安装工具或者换电脑工作时,只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时开发项目时的个性化配置。
2.2.3页面配置 page.json
2.3WXML 模板
WXML 全称是 WeiXin Markup Language,是小程序框架设计的一套标签语言,结合小程序的基础组件、事件系统,可以构建出页面的结构。WXML就相当于html页面,但是它里面只能写标签,不能包含js和css。WXML 要求标签必须是严格闭合的,没有闭合将会导致编译错误,其属性是大小写敏感的。打开pages/index/index.wxml,其内容如下:
<view class="container"> <view class="userinfo"> <button wx:if="{{!hasUserInfo && canIUse}}"> 获取头像昵称 </button> <block wx:else> <image src="{{userInfo.avatarUrl}}" background-size="cover"></image> <text class="userinfo-nickname">{{userInfo.nickName}}</text> </block> </view> <view class="usermotto"> <text class="user-motto">{{motto}}</text> </view> </view>
对比html页面可以看出,这里的标签是不一样的,小程序采用的标签是view、button等,这些都是已经包装过的。
2.4WXSS 样式
WXSS
具有 CSS
大部分的特性,但也有扩充。新增了尺寸单位rpx,提供了全局的样式和局部样式。可以写一个 app.wxss
作为全局样式,会作用于当前小程序的所有页面,局部页面样式 page.wxss
仅对当前页面生效,此外 WXSS
仅支持部分 CSS
选择器。
2.5JS 逻辑交互
编写 JS
脚本文件来处理用户的操作,请看下面的代码:
<view>{{ msg }}</view> <button bindtap="clickMe">点击我</button>
点击 button
按钮的时候,我们希望把界面上 msg
显示成 "Hello World"
,于是我们在 button
上声明一个属性: bindtap
,在 JS 文件里边声明了 clickMe
方法来响应这次点击操作:
Page({ clickMe: function() { this.setData({ msg: "Hello World" }) } })
以上先简单介绍,具体的语法见下章节。
2.6小程序宿主环境
微信客户端给小程序所提供的环境为宿主环境。小程序的运行环境分成渲染层和逻辑层,其中 WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。一个小程序存在多个界面,所以渲染层存在多个WebView线程。
3.WXML详解
3.1数据绑定
1)数据的呈现通过两个大括号包裹起来,数据的值在js的data中定义。下面是一个简单的例子:
pages/index/index.wxml的内容替换如下:
<text>当前时间:{{time}}</text>
pages/index/index.js的内容替换如下:
const app = getApp()
Page({
data: {
time: (new Date()).toString()
}
})
在js中定义的变量直接在wxml中显示。
2)除了直接使用data中的对象外,在大括号中还可以进行简单的逻辑运算,且看例子:
<text>{{ a === 10? "变量 a 等于10": "变量 a 不等于10"}}</text>
这是一个三元运算符,结果可想而知。
3)字符串拼接
<view>{{"hello " + name}}</view>
4)放置数字、字符串或者是数组
<text>{{[1,2,3]}}</text> <!-- 输出 1,2,3 --> <text>{{"hello world"}}</text> <!-- 输出 hello world -->
3.2属性绑定
属性值也可以动态的去改变,但属性值必须被包裹在双引号中。
<text data-test="{{test}}"> hello world</text>
需要注意,没有被定义的变量的或者是被设置为 undefined 的变量不会被同步到 wxml 中,即不会显示在页面上。
3.3条件逻辑
WXML 中,使用 wx:if="{{condition}}" 来判断是否需要渲染该代码块,使用wx:elif 和 wx:else 来添加一个 else 块
1)只用if
wxml内容
<view wx:if="{{sex==0}}">男生</view>
js内容
Page({ data: { sex:0 }, })
当把sex的值改为1时页面就不会显示任何的内容。
2)if和else都使用
计算逻辑是先判断是否符合if的条件,若不符合就else,或者继续判断是否符合elif的条件,若还不符合就直接进入else。
wxml内容else
<view wx:if="{{age<20}}">年龄小于20岁</view> <view wx:else>年龄不小于20岁</view>
wxml内容elif
<view wx:if="{{age<20}}">年龄小于20岁</view> <view wx:elif="{{age>20}}">年龄大于20岁</view> <view wx:else>年龄是20岁</view>
js内容
Page({ data: { age:20 }, })
3)block的使用
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/>
标签将多个组件包装起来,并在上边使用 wx:if 控制属性。
<block wx:if="{{id==1}}"> <view> view1 </view> <view> view2 </view> </block>
3.4列表渲染
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item。index和item均可省略。
wxml内容
<view wx:for="{{user}}" wx:key="key"> <text>用户名:{{item.name}}</text> <text>年龄:{{item.age}}</text> </view>
js内容
Page({ data: { user: [{ name: '张三', age: 20 }, { name: '李四', age: 30 }, { name: '王五', age: 22 }, { name: '赵柳', age: 20 }, { name: '李琦', age: 18 }, ] }, })
输入内容如下:
当然,如果不使用默认的item,也可自定义。使用 wx:for-item 指定数组当前元素的变量名,使用 wx:for-index 指定数组当前下标的变量名,把上述的wxml内容修改如下:
<view wx:for="{{user}}" wx:for-item="myuser" wx:for-index="i" wx:key="key"> <text>{{i+':'}}用户名:{{myuser.name}}</text> <text>年龄:{{myuser.age}}</text> </view>
如果需要指定数据的唯一标识,可以使用wx:key,但值必须是列表中唯一的字符串或数字,且不能动态改变。
3.5模板
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。使用 name 属性,作为模板的名字。然后在 <template/>
内定义代码片段。
wxml内容
<template name="msgItem"> <view> <text> 消息内容: {{msg}} </text> <text> 时间: {{time}} </text> </view> </template> <!-- 引用模板 --> <template is="msgItem" data="{{...item}}"/>
js内容
Page({ data: { item: { msg: '你吃饭了吗', time: '2020-11-18' } }, })
上述就是使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入。
对应is属性,还可以动态决定具体需要渲染哪个模板,wxml内容如下:
<template name="odd"> <view> odd </view> </template> <template name="even"> <view> even </view> </template> <block wx:for="{{[1, 2, 3, 4, 5]}}"> <template is="{{item % 2 == 0 ? 'even' : 'odd'}}"/> </block>
输出结果如图:
.
3.6引用
WXML 提供两种文件引用方式import和include。
3.6.1import引用
import 可以在该文件中使用目标文件定义的 template。
1)在 item.wxml 中定义了一个叫 item的 template
<template name="item"> <text>{{text}}</text> </template>
2)在 index.wxml 中引用item.wxml并使用模板
<import src="item.wxml"/> <template is="item" data="{{text: 'forbar'}}"/>
需要注意的是 import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件中 import 的 template,简言之就是 import 不具有递归的特性。且看下面的例子:
若C 引用 B,B 引用A,在C中可以使用B定义的 template,在B中可以使用A定义的 template ,但是C不能使用A定义的template,代码如下:
A.wxml
<!-- A.wxml --> <template name="A"> <text> A template </text> </template>
B.wxml
<!-- B.wxml --> <import src="a.wxml"/> <template name="B"> <text> B template </text> </template>
C.wxml
<!-- C.wxml --> <import src="b.wxml"/> <template is="A"/> <!-- --> <template is="B"/>
引用A模板时会触发一个警告,因为 b 中并没有定义模板 A。
3.6.2include引用
include 可以将目标文件中除了 <template/>
外的整个代码引入,相当于是拷贝到 include 位置。
test.wxml
<view> header </view>
index.wxml
<!-- index.wxml --> <include src="test.wxml"/> <view> body </view>
输出结果如下:
3.7共同的属性和事件绑定
1)hidden
组件是否显示,默认是false,如果不显示设置为true即可。
<view hidden="{{false}}">我是显示的</view> <view hidden="{{true}}">我是隐藏的</view>
2)data-*
自定义的组件属性,组件上触发的事件时,会把此属性的值发送给事件处理函数,事件处理函数通过e.target.dataset.name获取。
<view data-name="张三"></view>
需要注意的是,data-name不是固定的,可以自定义,如果是data-data,那么就通过e.target.dataset.data来获取,也就是通过data-后指定的名字来获取属性的值。具体的介绍见后面章节介绍。
3)bind*/catch*
组件的事件绑定,具体看下章节。
3.8组件-图片 cover-image
覆盖在原生组件之上的图片视图,类似于image,只不过它是绝对定位的,会悬浮在原生组件之上。
<cover-image src="/img/images/xy1.jpg"></cover-image>
3.9组件-视图 view
它是原生的视图,类似于div标签,但在小程序中就使用view,不建议使用div。
<view> <view>123</view> <view>456</view> </view>
3.10组件-图标 icon
图标有几种类型,使用时需指定type,而size默认值是23,可调整;color也是可设置。text是文本标签。
<view class="icon-box"> <view class="inline"> <icon type="success"></icon> </view> <view class="title">成功 <text class="title-info">success</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="download"></icon> </view> <view class="title">下载 <text class="title-info">download</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="info"></icon> </view> <view class="title">提示 <text class="title-info">info</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="warn" color="#C9C9C9"></icon> </view> <view class="title">普通警告 <text class="title-info">warn</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="warn"></icon> </view> <view class="title">强烈警告 <text class="title-info">warn</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="waiting"></icon> </view> <view class="title">等待 <text class="title-info">waiting</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="success_no_circle" size="33"></icon> </view> <view class="title">已选择 <text class="title-info">success_no_circle</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="circle"></icon> </view> <view class="title">未选择 <text class="title-info">circle</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="info_circle"></icon> </view> <view class="title">表单提示 <text class="title-info">info_circle</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="cancel"></icon> </view> <view class="title">停止或关闭 <text class="title-info">cancel</text> </view> </view> <view class="icon-box"> <view class="inline"> <icon type="search"></icon> </view> <view class="title">搜索 <text class="title-info">search</text> </view> </view>
样式:
.icon-box { text-align: center; margin-top: 20px; } .inline { margin-left: 50px; float: left; } .title-info { color: #9C9C9C; }
效果图:
3.11组件-按钮 button
按钮属性如下表:
属性 | 默认值 | 说明 |
type | default | 按钮类型,default白色、primary绿色、warn红色 |
size | default | 按钮尺寸大小,default默认,mini小尺寸 |
plain | false | 按钮是否透明,设置true即可透明 |
disabled | false | 是否禁用,true是禁用 |
loading | false | 名称前是否带 loading 图标 |
form-type | 用于 form 组件,点击分别会触发 form 组件的 submit/reset 事件 |
代码:
<view> <view class="my-button" > <button type="primary" disabled="{{disabled}}">按钮</button> </view> <view class="my-button" > <button type="primary" plain="{{plain}}" >透明按钮</button> </view> <view class="my-button" > <button size="mini" loading="{{loading}}">小按钮</button> </view> </view>
效果图:
3.12组件-多选框 checkbox
多选框分为默认样式和推荐样式:
1)默认样式
页面:
<view> <view class="page-section-title">默认样式</view> <label class="checkbox" wx:for="{{hobby}}" wx:key="key"> <checkbox value="{{item.value}}" checked="{{item.checked}}">{{item.label}}</checkbox> </label> </view>
js:
Page({ data: { hobby:[ {value:'1',label:'看书',checked:true}, {value:'2',label:'打游戏'}, {value:'3',label:'跑步'}, {value:'4',label:'骑自行车',checked:true} ] }, })
效果图:
2)推荐样式
页面:
<view> <view class="page-section"> <view class="page-section-title">默认样式</view> <view class="weui-cells weui-cells_after-title"> <checkbox-group bindchange="checkboxChange"> <label class="weui-cell weui-check__label" wx:for="{{hobby}}" wx:key="key"> <view class="weui-cell__hd"> <checkbox value="{{item.value}}" checked="{{item.checked}}" /> </view> <view class="weui-cell__bd">{{item.label}}</view> </label> </checkbox-group> </view> </view> </view>
js:
Page({ data: { hobby:[ {value:'1',label:'看书',checked:true}, {value:'2',label:'打游戏'}, {value:'3',label:'跑步'}, {value:'4',label:'骑自行车',checked:true} ] }, checkboxChange(e){ console.log('选中的值为:', e.detail.value) } })
效果图:
推荐样式样式更好看,但要注意一个坑。其样式采用的weui样式,因此必须引入此依赖:在app.json中引入扩展组件
"useExtendedLib": { "weui": true },
3.13组件-单选框 redio
只说明推荐样式。
页面:
<view> <view class="page-section"> <view class="page-section-title">推荐样式</view> <view class="weui-cells weui-cells_after-title"> <radio-group bindchange="radioChange"> <label class="weui-cell weui-check__label" wx:for="{{hobby}}" wx:key="key"> <view class="weui-cell__hd"> <radio value="{{item.value}}" checked="{{item.checked}}" /> </view> <view class="weui-cell__bd">{{item.label}}</view> </label> </radio-group> </view> </view> </view>
js:
Page({
data: {
hobby:[
{value:'1',label:'看书'},
{value:'2',label:'打游戏'},
{value:'3',label:'跑步'},
{value:'4',label:'骑自行车',checked:true}
]
},
radioChange(e){
console.log('选中的值为:', e.detail.value)
}
})
效果图:
3.14组件-输入框 input和textarea
文本框和多行输入框类型,其部分属性如下:
属性 | 默认值 | 说明 |
value | 文本框初始内容 | |
type | text | 文本框类型,text文本输入键盘、number数字输入键盘、idcard身份证输入键盘、digit小数输入键盘。textarea无此属性 |
password | false | 是否是密码框,true是。textarea无此属性 |
placeholder | 文本框占位符或提示文字 | |
disabled | false | 是否禁用,true禁用 |
maxlength | 140 | 最大输入长度,设置为 -1 的时候不限制最大长度,若限制了长度,当达到最大长度后就不能继续输入了 |
focus | false | 是否自动获取焦点,true是 |
bindinput | 输入事件触发,详见事件绑定 | |
bindblur | 文本框失去焦点时触发事件,详见事件绑定 |
input页面:
<view class="page-body"> <view class="page-section"> <view class="weui-cells__title">自动聚焦</view> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell weui-cell_input"> <input class="weui-input" focus placeholder="将会获取焦点" /> </view> </view> </view> <view class="page-section"> <view class="weui-cells__title">用户名:{{name}}</view> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell weui-cell_input"> <input class="weui-input" value="{{name}}" bindinput="bindKeyInput" /> </view> </view> </view> <view class="page-section"> <view class="weui-cells__title">密码</view> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell weui-cell_input"> <input class="weui-input" placeholder="密码" maxlength="10" /> </view> </view> </view> <view class="page-section"> <view class="weui-cells__title">数字输入</view> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell weui-cell_input"> <input class="weui-input" type="number" placeholder="数字输入" maxlength="5" /> </view> </view> </view> </view>
input的js:
Page({ data: { name: '张三' }, bindKeyInput(e) { this.setData({ name: e.detail.value }) } })
现象是页面打开时,光标自动在第一个输入框闪烁,输入用户名是,文本框上面的值也会发生变化,密码是隐藏不可见的,数字输入5位就不能输入了。这里的文本框包括后面的几乎都是为表单做铺垫。
textarea页面:
<view class="page-section"> <view class="weui-cells__title">备注</view> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell weui-cell_input"> <textarea class="weui-input" placeholder="备注"/> </view> </view> </view>
3.15组件-日期时间组件 picker
从底部弹起的滚动选择器。它最重要是属性是mode,用来指定选择器的类型,其值有selector、multiSelector、time、date和region,具体说明且看后续。
对于不同的mode,picker拥有不同的属性,请看介绍。
1)selector:普通选择器
属性 | 类型 | 默认值 | 描述 |
range | array/object array | [] | 选择的范围 |
range-key | string | 当 range 是一个 Object Array 时,通过 range-key 来指定 Object 中 key 的值作为选择器显示内容 | |
value | number | 0 | 表示选择了 range 中的第几个(下标从 0 开始) |
bindchange | value 改变时触发 change 事件 |
下面的页面使用了两种类型的数组呈现:
<picker mode="selector" bindchange="bindPickerChange" value="{{index}}" range="{{array}}"> <view class="picker"> 当前选择的是1:{{array[index]}} </view> </picker> <picker mode="selector" bindchange="bindPickerChange2" value="{{objectArray[objIndex].id}}" range="{{objectArray}}" range-key="name"> <view class="picker"> 当前选择的是2:{{objectArray[objIndex].name}} </view> </picker>
js:
Page({ data: { array: ['美国', '中国', '巴西', '日本'], index: 1, objectArray: [{ id: 0, name: '2016年' }, { id: 1, name: '2017年' }, { id: 2, name: '2018年' }, { id: 3, name: '2019年' } ], objIndex: 3 }, bindPickerChange(e) { console.log('picker发送选择改变,值为', e.detail.value) this.setData({ index: e.detail.value }) }, bindPickerChange2(e) { console.log('picker发送选择改变,值为', e.detail.value) this.setData({ objIndex: e.detail.value }) }, })
2)multiSelector:多列选择器
属性 | 类型 | 默认值 | 描述 |
range | array/object array | [] | 选择的范围 |
range-key | string | 当 range 是一个 Object Array 时,通过 range-key 来指定 Object 中 key 的值作为选择器显示内容 | |
value | number | 0 | 表示选择了 range 中的第几个(下标从 0 开始) |
bindchange | value 改变时触发 change 事件 | ||
bindcolumnchange | 列改变时触发 |
页面:
<picker mode="multiSelector" bindchange="bindMultiPickerChange" value="{{multiIndex}}" range="{{multiArray}}"> <view class="picker"> 当前选择:{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}} </view> </picker>
js:
Page({ data: { multiArray: [ ['无脊柱动物', '脊柱动物'], ['扁性动物', '线形动物', '环节动物', '软体动物', '节肢动物'], ['猪肉绦虫', '吸血虫'] ], multiIndex: [0, 0, 0], }, bindMultiPickerChange: function (e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ multiIndex: e.detail.value }) }, })
3)time:时间选择器
属性 | 类型 | 默认值 | 描述 |
value | string | 选中的时间,格式为"hh:mm" | |
start | string | 有效时间范围的开始,字符串格式为"hh:mm" | |
end | string | 0 | 有效时间范围的结束,字符串格式为"hh:mm" |
bindchange | value 改变时触发 change 事件 |
对于时间选择器,只能选择到分钟,若不指定有效开始时间和结束时间,那么默认从00:00到23:59可选。且看实例:
页面:
<picker mode="time" bindchange="bindPickerChange" value="{{time}}" start="{{start}}" end="{{end}}"> <view class="picker"> 当前选择时间:{{time}} </view> </picker>
js:
Page({ data: { time:'14:20', start:'10:30', end:'20:59' }, bindPickerChange: function (e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ multiIndex: e.detail.value }) }, })
4)date:日期选择器
属性 | 类型 | 默认值 | 描述 |
value | string | 当天 | 选中的日期,格式为"YYYY-MM-DD" |
start | string | 有效日期范围的开始,字符串格式为"YYYY-MM-DD" | |
end | string | 有效日期范围的结束,字符串格式为"YYYY-MM-DD" | |
fields | string | day | 有效值 year,month,day,表示选择器到哪一粒度,如指定month则只选择到月份 |
bindchange | value 改变时触发 change 事件 |
有两个示例,一个使用默认值,一个指定了fields到月份,具体如下:
页面:
<picker mode="date" bindchange="bindDateChange" value="{{date}}" start="{{start}}" end="{{end}}"> <view class="picker"> 当前选择日期:{{date}} </view> </picker> <picker mode="date" bindchange="bindDateChange2" value="{{date1}}" fields="month"> <view class="picker"> 当前选择日期:{{date1}} </view> </picker>
js:
Page({ data: { date: '2020-12-08', start: '2020-01-01', end: '2020-12-30', date1: '2020-12', }, bindTimeChange: function(e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ time: e.detail.value }) }, bindDateChange: function(e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ date: e.detail.value }) }, bindDateChange2: function(e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ date1: e.detail.value }) }, })
5)region:省市区选择器
属性 | 类型 | 默认值 | 描述 |
value | array | [] | 选中的省市区,默认选中每一列的第一个值 |
bindchange | value 改变时触发 change 事件 |
省市区选择时使用微信的数据,只需存储文字即可。
页面:
<picker mode="region" bindchange="bindRegionChange" value="{{region}}" > <view class="picker"> 当前选择区:{{region[0]}}{{region[1]}}{{region[2]}} </view> </picker>
js:
Page({ data: { region: ['广东省', '深圳市', '龙华区'] }, bindRegionChange(e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ region: e.detail.value }) } })
3.16组件-开关 switch
开关主要说明一下类型,有switch和checkbox类型。
页面:
<view class="section__title">type="switch"</view> <view class="body-view"> <switch checked="{{switch1Checked}}" bindchange="switch1Change" /> </view> <view class="section__title">type="checkbox"</view> <view class="body-view"> <switch type="checkbox" checked="{{switch2Checked}}" bindchange="switch2Change" /> </view>
js:
Page({ data: { switch1Checked: true, switch2Checked: false, }, switch1Change(e){ console.log('按钮1改变,值是:'+e.detail.value) this.setData({ switch1Checked:e.detail.value }) }, switch2Change(e){ console.log('按钮2改变,值是:'+e.detail.value) this.setData({ switch2Checked:e.detail.value }) } })
效果:
3.17组件-表单 form
对于表单,主要就是表单提交事件bindsubmit,详见事件章节。另外,需要给每个输入框(包括单选框、多选框等)添加name属性,原因是在表单提交时会根据name属性去获取值。提交按钮只需要指定formType类型是submit即可。示例如下:
页面:
<view class="container"> <form bindsubmit="formSubmit" bindrest="formRest"> <view class="inbox"> <label class="label">姓名<span style="color: red;">*</span></label> <input placeholder="姓名" value="{{form.name}}" name="name" maxlength="50"/> </view> <view class="inbox"> <label class="label">密码<span style="color: red;">*</span></label> <input placeholder="密码" value="{{form.password}}" password="true" name="password" maxlength="50"/> </view> <view class="inbox"> <label class="label">性别<span style="color: red;">*</span></label> <radio-group bindchange="radioChange" name="sex"> <radio class="radio-group-col" value="{{item.value}}" wx:for="{{sexOption}}" wx:key="key" checked="{{item.checked}}"> <text>{{item.label}}</text> </radio> </radio-group> </view> <view class="inbox"> <label class="label">爱好</label> <checkbox-group bindchange="checkboxChange" name="hobby"> <checkbox class="checkbox-group-col" wx:for="{{hobbyOption}}" wx:key="key" value="{{item.value}}" checked="{{item.checked}}"> <text>{{item.label}}</text> </checkbox> </checkbox-group> </view> <view class="inbox"> <label class="label">出生日期<span style="color: red;">*</span></label> <picker mode="date" bindchange="bindDateChange" value="{{form.brith}}" end="{{brithEnd}}"> <input placeholder="出生日期" name="brith" value="{{form.brith}}" /> </picker> </view> <view class="inbox"> <label class="label">输入时间<span style="color: red;">*</span></label> <picker mode="time" bindchange="bindTimeChange" value="{{form.nowTime}}"> <input placeholder="输入时间" name="nowTime" value="{{form.nowTime}}" /> </picker> </view> <view class="inbox"> <label class="label">所在地区<span style="color: red;">*</span></label> <picker mode="region" bindchange="bindRegionChange" value="{{region}}"> <input placeholder="所在地区" name="region" value="{{form.region}}" /> </picker> </view> <view class="inbox"> <label class="label">详细地址</label> <textarea class="textarea" placeholder="详细地址" name="addr" value="{{form.addr}}" maxlength="2000"/> </view> <view class="btn-group"> <button type="primary" formType="submit">提交</button> <button formType="reset">取消</button> </view> </form> </view>
js:
const now = new Date() //导入工具类 const util = require('../../utils/util.js'); Page({ data: { form: {}, sexOption: [{ label: '男', value: 0 }, { label: '女', value: 1 } ], hobbyOption: [{ value: '1', label: '看书', checked: true }, { value: '2', label: '打游戏' }, { value: '3', label: '跑步' }, { value: '4', label: '骑自行车', checked: true } ], brithEnd: now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate(), region: [] }, //日期发生变化 bindDateChange(e) { this.setData({ 'form.brith': e.detail.value }) }, //时间发生变化 bindTimeChange(e) { this.setData({ 'form.nowTime': e.detail.value }) }, //地区发生变化 bindRegionChange(e) { const region = e.detail.value this.setData({ 'region': region, 'form.region': region[0] + region[1] + region[2] }) }, //多选按钮发生变化 checkboxChange(e) { this.setData({ 'form.hobby': e.detail.value }) }, //单选按钮发生变化 radioChange(e) { this.setData({ 'form.sex': e.detail.value }) }, //表单提交 formSubmit(e) { const form = e.detail.value let isChecking = false if (!util.isNotBlank(form.name)) { isChecking = true } else if (!util.isNotBlank(form.password)) { isChecking = true } else if (!util.isNotBlank(form.sex)) { isChecking = true } else if (!util.isNotBlank(form.brith)) { isChecking = true } else if (!util.isNotBlank(form.nowTime)) { isChecking = true } else if (!util.isNotBlank(form.region)) { isChecking = true } if (isChecking) { wx.showToast({ title: "带*号为必填项", icon: 'none', duration: 2000 }) } else { this.data.form = form wx.showModal({ title:'表单内容' , icon: 'none', content:JSON.stringify(form) }) } }, })
页面样式:
.checkbox-group-col { padding-left: 15px; } .radio-group-col{ padding-right: 30px; }
全局样式:
/* *app.wxss* */ .container { height: 100%; flex-direction: column; align-items: center; justify-content: space-between; padding: 10rpx 0 0 0; box-sizing: border-box; } .inbox { display: flex; margin: 20px 0 0 20px; padding-bottom: 10px; border-bottom: 1px solid var(--weui-FG-3); font-size: 18px; } .inbox .label { width: 30%; } .textarea { height: 80rpx; margin-left: 20rpx; } .btn-group { display: flex; margin: 10px; /* 样式居中 */ align-items: center; justify-content: center; } button { width: 30% !important; }
工具类方法:
//是否为空 const isNotBlank = val => { return val != null && val != '' } module.exports = { isNotBlank: isNotBlank }
效果图:
对于picker组件,当里面存放的input标签时,作为表单的输入,需要使用disabled把输入框设置为禁用,只能通过选择时间来输入,不能手动输入。原因是如果不禁用那么在选择时会同时弹出选择框和输入框,界面很不友好。
4.js详解
4.1App.js
1)文件包裹
app中,使用App构造器把整个文件包裹起来,所有的方法和变量都写在里面。对于方法,可直接按照ES6语法定义即可,而对于变量,需要放到data的属性中。
App({ data: { msg:'今天天气真好' }, test(){ console.log(123) } })
2)系统方法
另外在app.js中有一些默认的方法提供使用,如onLaunch方法,此方法是在页面加载完成会自动调用的,可以在里面进行数据的初始化。
App({
data: {
onLoad: function (option){
},
})
其方法中有一个参数,是一个对象,当进行页面跳转进行参数拼接传递时,就可以使用对应的参数来获取。示例如下:
--index.js wx.navigateTo({ url:'/pages/test/test?name=zys&age=20' }) --test.js Page({ onLoad:function(option){ console.log(option.name) console.log(option.age) } })
3)全局参数
有些参数需要很多地方使用,就可以设置为全局的,需要在App.js中设置。其中有个对象globalData,里面是用来存放全局参数的。设置参数后在其他页面文件通过导入的方法,就可以使用其中的变量了。
App.js设置全局参数:
App({ globalData: { userInfo: null } })
index.js导入并使用参数:
//获取应用实例 const app = getApp() Page({ onLoad: function (option){ app.globalData.userInfo = 'admin' //console.log(app.globalData.userInfo) }, })
不仅可以直接使用这个值,还可以修改这个值。
4.2页面js
页面js就拿index.js为例说明。与app.js不同的是,它是通过Page构造器包裹的,也没有globalData参数。page构造器用来注册一个小程序页面。
Page({
onLoad: function (option){
...
}
})
4.2.1使用setData设置值
在页面js中获取变量的值,使用this.data来获取。但是要修改值,不能全部使用this.data,在很多情况下需要使用this.setData来设置值,this.setData将数据从逻辑层发送到视图层,同时改变对应的data的值。简单的说,就是保证js和界面的数据是同步的。
Page({ data: { name: 'admin' }, onLoad: function() { //获取值 console.log(this.data.name) //设置值:方法一 this.data.name = '张三' //设置值:方法二 this.setData({ name:'李四' }) }, })
若设置值,推荐使用第二种方法。
需要注意的是,小程序中,当使用setData对对象或属性赋值时,用法是不一样的:
(1)给对象直接赋值
const myuser = {name:'张三',age:20}
this.setData({
user: myuser
})
(2)给对象的某个属性赋值
const myuser = {name:'张三',age:20}
this.setData({
'user.name': myuser.name
})
注意区别就是给对象的某个属性赋值时,需要用引号括起来
4.2.2系统内置的方法
onLoad:页面加载完成时调用。页面初次加载的时候,微信客户端就会给Page实例派发onLoad事件,Page构造器参数所定义的onLoad方法会被调用,onLoad在页面没被销毁之前只会触发1次。
onShow:小程序启动或切前台时调用。
5.事件
事件绑定分为两种,bind绑定和catch绑定,区别是catch能阻止事件冒泡。具体事件绑定以bind绑定为例,在bind后加上对应的事件名即可,也可以通过冒号的方式进行绑定,bindtap和bind:tap是一样的效果。以下只介绍常用的事件。
5.1事件对象
当事件回调触发的时候,会收到一个事件对象,对象的部分属性如下:
type:事件类型 target:触发事件的组件的一些属性值集合 currentTarget:当前组件的一些属性值集合 detail:额外的信息
额外的信息主要用于事件获取参数值,一般使用event.detail.value来获取。
5.1.1target和currTarget的区别
对于target和currTarget,它们作用是不一样的,currentTarget为当前事件所绑定的组件,而target则是触发该事件的源头组件。下面的例子就是很好的说明:
<!-- wxml --> <view catchtap="handleTap" data-name="outer"> <view data-name="inner">点击我</view> </view> <!-- js --> Page({ handleTap(e){ console.log(e.target.dataset.name)//inner console.log(e.currentTarget.dataset.name)//outer } })
5.1.2target和currentTarget的属性
一般通过dataset来获取data-*的属性值,且看后续的章节介绍。推荐使用currentTarget,它在小程序中使用的很多,原因是小程序中事件的方法不能传递参数,只能通过属性值的方式传递并获取。
5.2 tap
绑定点击事件,点击时会触发指定的方法,会给方法传递一个事件对象。
<!--wxml-->
<view>
<button bindtap="myTest">我是点击事件,点我触发</button>
</view>
<!--js-->
Page({
data: {},
myTest(e){
console.log(e)
console.log(e.currentTarget.dataset)
}
})
5.3 input
绑定文本框事件,结合detail属性用于在逻辑层获取文本框输入的值。
<!-- wxml --> <input type="text" bindinput="input" data-name="username" placeholder="请输入用户名"/> <input password='true' bindinput="input" data-name="password" placeholder="请输入密码"/> <button bindtap="getValue" type="primary">点我获取内容</button> <!-- js --> Page({ data: { user:{ username:'', password:'' } }, input(e){ //获取data-name的值 const key = e.currentTarget.dataset.name //获取文本框的值 const value = e.detail.value this.data.user[key] = value }, getValue(e){ console.log(this.data.user) } })
当输入数据后,点击按钮即可在输出对应的内容。输入结果如下:
5.4 longpress
手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发,如果不超过350ms就会触发tap事件,一般情况下只使用tap事件进行点击触发。
<!-- wxml --> <button bindtap="tapHandle" bindlongpress="longpressHandle" type="primary">点我获取内容</button> <!-- js --> Page({ tapHandle(e){ console.log(111) }, longpressHandle(e){ console.log(222) } })
5.5 submit
绑定表单,绑定后通过detail来获取表单的具体数据。
<!-- wxml --> <form bindsubmit="formSubmit"> <text>账号:</text> <input type="text" placeholder='请输入账号' name="username" /> <text>密码:</text> <input password='true' placeholder='请输入密码' name="password" /> <button type='primary' form-type='submit'>登录</button> </form> <!-- js --> Page({ formSubmit(e) { //获取表单数据的值 const obj = e.detail.value console.log(obj) }, })
输入数据点击按钮即可看到数据。
5.6confirm
回车事件,点击回车按钮时触发。可用于输入框或按钮。
<!-- wxml --> <input type="text" bindinput="input" bindconfirm="searchSubmit" placeholder="搜索"/> <!-- js --> Page({ data: { username: '' }, onLoad() {}, input(e) { this.setData({ username: e.detail.value }) }, searchSubmit(e) { console.log(this.data.username) } })
输入数据后回车就可以看到打印的结果。
6.WXSS
WXSS全称是WeiXin Style Sheets,它是一套用于小程序的样式语言,用于描述WXML的组件样式的文件。
6.1WXSS尺寸和位置
上图是wxss的存放位置,其中公共样式放在app.wxss中,页面自己的也是放到页面目录下。
在WXSS中,引入了rpx尺寸单位。目的是,适配不同宽度的屏幕,解决同一px在不同屏幕下的占比问题。小程序编译后,rpx会做一次px换算。换算是以375个物理像素为基准,也就是在一个宽度为375物理像素的屏幕下,1rpx = 1px。
6.2WXSS引用
在小程序中,需要引入其他的样式文件,引入方式是@import 样式文件路径,例子:
引入当前目录下的test样式文件
@import './test.wxss'
7.常用API
7.1路由
路由的几种类型对比如下表:
方法 | 说明 | 使用 |
wx.reLaunch | 关闭所有页面,打开到应用内的某个页面 | wx.reLaunch({url:'url'}) |
wx.redirectTo |
关闭当前页面,跳转到应用内的某个页面, 但是不允许跳转到 tabbar 页面 |
wx.redirectTo({url:'url'}) |
wx.navigateTo |
保留当前页面,跳转到应用内的某个页面, 但是不能跳到 tabbar 页面。 页面栈最多十层,使用navigateBack返回上一层 |
wx.navigateTo({url:'url'}) |
wx.navigateBack | 关闭当前页面,返回上一页面或多级页面 | wx.navigateBack() |
前3个方法都可以传递参数,在跳转后的页面onload方法中获取参数的值,以navigateTo为例说明:
index.wxml:
<button type="primary" bindtap="goNext">进入下一页</button>
index.js:
Page({ goNext(e) { wx.navigateTo({ url: '/pages/test2/test2?name=zss&age=20' }) } })
test.wxml:
<view> 传递的参数:{{user.name+","+user.age}} </view>
test.js:
Page({ data: { user: {} }, onLoad: function(options) { console.log(options) this.setData({ user: options }) }, })
7.2消息框
先看下表的分类:
类型 | 消息提示框 | 模态对话框 | loading 提示框 | 操作菜单 |
方法 | wx.showToast | wx.showModal | wx.showLoading | wx.showActionSheet |
且看详细说明:
1)wx.showToast
属性 | 类型 | 说明 |
title | string | 提示的内容 |
icon | string |
默认值是success。提示的图标。 success:成功图标,此时 title 文本最多显示 7 个汉字长度 none:不显示图标,此时 title 文本最多可显示两行 |
duration | number | 默认值是number,提示显示的时间,但是是毫秒。 |
下面包含有图标和无图标的例子:
wx.showToast({ title:'成功提示', duration:2000 })
wx.showToast({ title:'无图标提示', icon:'none' })
2)wx.showModal
属性 | 类型 | 说明 |
title | string | 提示的标题 |
context | string |
提示的内容 |
showCancel | boolean |
默认值是true。是否显示取消按钮。 |
cancelText | string | 默认值是'取消',取消按钮的文字,最多 4 个字符。 |
confirmText | string | 默认值是'确定',取消按钮的文字,最多 4 个字符. |
success | function |
接口调用成功的回调函数。函数的参数有两个属性: confirm为true时表示用户点击了确定按钮,cancel为true时表示用户点击了取消按钮 |
下面包含有删除按钮和无删除按钮的例子:
wx.showModal({ title:'消息提示', content:'您确定要删除吗?', success:function(res){ if(res.confirm){ console.log('点击了确定') }else if(res.cancel){ console.log('点击了取消') } }, }) wx.showModal({ title: '消息提示', content: '操作成功', showCancel: false, success: function(res) { console.log('点击了确定') }, })
3)wx.showLoading
其最重要的属性就是title,加载时提示的文字。如果不关闭则会一直加载,关闭使用wx.hideLoading()。
下面例子是加载弹框显示5秒后自动关闭,在实际情况中是数据加载完成后关闭。
wx.showLoading({ title: '加载中', }) setTimeout(function() { //关闭加载框 wx.hideLoading() }, 5000)
4)wx.showActionSheet
属性 | 类型 | 说明 |
itemList | Array,元素是string | 按钮的文字数组,数组长度最大为 6 |
success | function |
接口调用成功的回调函数。函数的参数有属性tapIndex: 表示用户点击的按钮序号,从上到下的顺序,从0开始 |
下面例子是选择了不同的菜单就显示不同的值
const arr = ['A', 'B', 'C'] wx.showActionSheet({ itemList: arr, success(res) { console.log("您选择了:" + arr[res.tapIndex]) }, })
7.3网络
小程序请求后台时,必须要联网,没有网络是请求不到后台的。另外,部分域名需要先通国小程序后台设置为信任域名才能去请求访问。需要注意是,如果需要在请求后使用this,那么必须在请求之前先定义一个变量存储this,然后在请求后使用定义的变量,不能直接使用this,此时this的null。请求的方式如下表:
属性 | 说明 |
url | 请求的路径 |
method | 请求的方法,包括GET(默认)、POST、DELETE、PUT |
data | 传递的参数,必须是一个对象 |
header |
请求头信息,属性content-type的默认值是application/json。可在其中添加token等认证信息 |
success |
接口调用成功的回调函数,有参数response,数据是其中的data,即response.data |
1)get请求
wx.request({ url:'https://autumnfish.cn/api/joke/list', data:{ num: 5 }, success(res){ console.log(res.data) } }),
2)post请求
post直接传递对象,后台通过@RequestBody接收;
先看第一种:
wx.request({ url:'https://autumnfish.cn/api/user/reg', data:{ username:'123456556' }, method:'POST', success(res){ console.log(res.data) } })
3)请求的封装
post请求的另一种方式是把参数拼接在请求路径后面,后台通过@RequestParam接收。
const app = getApp(); function get(url, callback) { wx.request({ method: 'GET', url: url, header: { 'token': app.globalData.token }, success(res) { callback(res.data); } }) } function post(url, data, callback) { wx.request({ method: 'POST', url: url, data: data, header: { 'token': app.globalData.token }, success(res) { callback(res.data); } }) } function postParam(url, data, callback) { const keys = Object.keys(data) let params = '?' for (let i = 0; i < keys.length; i++) { const key = keys[i] let value = data[key] if (value != null && value != '') { if (i != 0) { params += '&' } //对特殊字符进行转义 value = encodeURIComponent(value) params += key + '=' + value } } wx.request({ method: 'POST', url:url + params, header: { 'token': app.globalData.token }, success(res) { callback(res.data); } }) }
7.4数据缓存
数据缓存分为存数据、取数据和删除数据。缓存就是把数据存到本地,允许存储的最大长度是1M,最大数据是10M。主要介绍同步操作。
1)setStorageSync:对指定的key存储value
wx.setStorageSync("username","zys")
2)getStorageSync:获取指定key的value
const username = wx.getStorageSync("username")
3)removeStorageSync:删除指定key的value
wx.removeStorageSync("username")
7.5用户登录
小程序登录时需要先给微信发送请求,获取此用户唯一的openid,后期用户的唯一性验证就需要它,详细的配置请看官网或参考demo,代码地址https://github.com/zhongyushi-git/mini-program.git。
7.6图片的下载
图片下载时,一般需要保存到系统相册中。实例如下:
wxml:
<view> <button type="warn" bindtap="download">下载图片</button> </view>
js:
Page({ download(){ const url ='http://192.168.43.123:8887/image/5.jpg' //下载文件到本地 wx.downloadFile({ url:url, success(res){ //把图片保存到系统相册 wx.saveImageToPhotosAlbum({ filePath:res.tempFilePath, success(resp){ wx.showToast({ title:'保存成功', icon:'success', duration:2000 }) }, fail(err){ console.log('保存错误',err) } }) }, fail(err){ console.log('下载错误',err) } }) }, })
点击按钮就可以把图片保存到本地相册了,不过需要注意的是,url必须是图片的地址,不能是字节流。
8.自定义组件
8.1创建组件
组件的结构和普通页面的结构类似,但也不完全相同,在js中,它使用的是Component构造器,其中是属性也有所不同;另外在json文件中需要指定是组件。
1)在项目根目录下新建一个component目录,再创建子目录mycon目录
2)在微信开发者工具中选择mycon目录,然后右键新建component,就会自动生成组件的相关配置
3)mycon.json的内容:指定是组件
{ "component": true, "usingComponents": {} }
4)编辑mycon.js内容:
Component({ /** * 组件的属性列表 */ properties: { titles:{ type:Array, value:[] } }, /** * 组件的方法列表 */ methods: { handleChange(e) { const index = e.currentTarget.dataset.index const { titles } = this.data titles.forEach((item, i) => i == index ? item.isActive = true : item.isActive = false) this.setData({ titles }) //子组件调用父组件的方法,可传递参数 this.triggerEvent('itemChange',{index}) } } })
在组件中,使用了slot标签,他相当于一个占位符,当传值时会将其替换为传递的内容。
5)wxss样式:
.title{ display: flex; padding: 10px; } .title-item{ flex:1; display: flex; /*垂直水平居中*/ justify-content: center; align-items: center; } .active{ color: red; border-bottom: 1px solid currentColor; }
8.2声明组件
在需要使用组件的页面的json中声明组件
{ "usingComponents": { "mycon":"../../components/mycon/index" } }
是以键值对的形式声明的,key是指定的组件的名称,value是组件的路径,一般使用相对路径。
8.3使用组件
声明之后,在wxml中直接使用即可。
wxml内容:
<mycon titles="{{titles}}" binditemChange="handleItemChange"> <view> <block wx:if="{{titles[0].isActive}}">我是首页内容</block> <block wx:elif="{{titles[1].isActive}}">我是分类内容</block> <block wx:else>我是原创内容</block> </view> </mycon>
js内容:
Page({ data: { titles: [{ name: '首页', isActive: true }, { name: '分类', }, { name: '原创', } ] }, handleItemChange(e){ const { titles } = this.data titles.forEach((item, i) => i == e.detail.index ? item.isActive = true : item.isActive = false) this.setData({ titles }) } })
效果图如下:
8.4组件传值说明
在上述自定义组件中,存在父子组件之间的传值,在此讲解。
1)父组件向子组件传值
父组件向子组件传递时,只需要在子组件的标签上添加对应的属性,然后在子组件中获取即可。在此实例中,父组件给子组件传递了标题数组titles,子组件在properties中接收并指定类型和默认值。
2)子组件向父组件传递方法
子组件给父组件传值时,需要通过方法的方式传递,即调用父组件的方法,把值放到参数中。在此实例中,子组件通过triggerEvent方式调用父组件的方法,并传递了参数,父组件在标签上定义了方法。
图片上传
<view> <mp-uploader title="提示 " tips="图片上传提示" delete="false" max-size="{{10*1024*1024}}" max-count="5" files="{{files}}" upload="{{uplaodFile}}" bindfail="uploadError" bindsuccess="uploadSuccess"></mp-uploader> <button bindtap="saveImg">保存</button> </view>
Page({ data: { files: [], filePath: [] }, onLoad() { this.setData({ uplaodFile: this.uplaodFile.bind(this) }) }, //文件上传的函数,返回一个promise uplaodFile(files) { this.data.files = this.data.files.concat(files.tempFilePaths); return new Promise((resolve, reject) => { setTimeout(() => { resolve({ urls: files.tempFilePaths }) }, 1000) }) }, //上传失败的回调 uploadError(e) { console.log('图片上传失败', e.detail) }, //上传成功的回调 uploadSuccess(e) { console.log('图片上传成功', e.detail) }, //把图片上传到服务器 saveImg() { //一次上传一张图片 const files = this.data.files for (var i = 0; i < files.length; i++) { wx.uploadFile({ url: 'http://localhost:8080/file/upload', filePath: files[i], name: 'file', complete: (res) => { this.data.filePath.push(res.data) if (i === files.length) { wx.showToast({ title: '上传成功' }) console.log('图片路径:' + this.data.filePath) } } }) } } })
8.5组件异步传值
如果在父组件中传给子组件的数据,是异步获取的,比如是从接口获取的,一般在子组件的ready里面是不能直接拿到值的。直接从ready里面打印的值要么是空对象或者空数组或者nul。想要取到值,就得在计算属性observers里面去获取,就是监听到值变化的时候再去取值。
父组件
<child-component list="{{data}}"/>
子组件
Component({ /** * 组件的属性列表 */ properties: { list: { type: Array, value: [] } }, lifetimes: { //组件初始化 ready: function() { console.log(this.data.list) //取不到值 }, }, //使用计算属性监听数据变化 observers: { 'list': function() { console.log(this.data.list) //可取到异步的值 } } }
这个计算属性其实和vue的计算属性很相似。
8.6父组件调用子组件方法
上述已说明子组件如何调用父组件的方法,再补充父组件调用子组件方法
parent.xml
<child-component id="child134"/>
child.js
Component({ methods: { tttt() { console.log('我是子组件方法'); }, } })
parent.js
// 页面获取自定义组件实例 let child = this.selectComponent('#child134'); // 通过实例调用组件事件 child.tttt();
通过这种方式还能进行值的传递,父组件将值动态传递给子组件。