微信小程序从零入门的文章实暂时就到这里了,还有一个生命周期的内容,感觉当初整理的不是很好就不写了,主要最近时间有点紧张,没时间改....
学完了 WXML、WXSS、一些常用组件,还有事件绑定,其实一个较为不错的页面已经能自己搭建出来了,如果你还想继续往后面研究,就可以开始看 与后台交互的内容了,发送异步请求,对数据进行回显等,回显内容时,你就又可以开始考虑用什么别的组件,可以让页面的数据加载或者观看更加顺滑,美观,当然有很多小伙伴都是后端,所以,可以自己搭个后台,试着弄用户登录授权这块,这块算是小程序一个重要的点,要好好研究一下,以后要做的事情,就和传统的后端基本一样了。我的文章都是根据官网写的原生写法,当然你也可以看一下一些小程序框架,或者看一看 ES6 的内容,毕竟我也是个后端,也没细细研究前端的内容
再接着就可以自己动手写点东西了,不会的组件或者内容就去查官方文档,查别人的博客,写着写着,就发现又认识了 N 个 东西,或许你就会用到 模板、swiper,然后自然而然的对小程序的生命周期有了一定的认识等等
可能在一定意义上,能自己手写一个不算太简陋的项目,才算真的入门了是不,啊哈哈哈
【微信小程序出发】
(一) 准备工作
(1) 登录注册
注册账号:这就不谈了,只需要注意使用一个全新的邮箱,别之前注册过公众号小程序等就可以了
https://mp.weixin.qq.com/wxopen/waregister?action=step1
登录账号:通过邮箱密码登录,亦或者绑定微信后使用扫码也是可以的
https://mp.weixin.qq.com/
(2) 获取 APPID
登录后,在开发入门的阶段有一个比较重要的内容需要了解,那就是 APPID,很好理解,就是这个小程序的唯一标识,就类似我们的身份证,登录后首页左侧栏选择【开发】,跳转后,选择【开发设置】就可以看到我们的 APPID
这个 APPID 要记好哈,在刚开始学习的时候,就会使用到
(3) 下载工具
在官网选择一个合适的版本进行下载,这就是我们小程序的编译器,这里我选择的是开发版,不过选择稳定版也是可以的,针对入门并没有太大的区别,不过稳定版可能出现的小毛病会少一些
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
安装好后,直接提示扫码登录,接着就是点击左侧【小程序】,然后点击右侧【新建】(一个大加号)填写一些基本的信息
- 项目名称和目录:自己看着弄就可以了
- APPID:之前页面中找到的 APPID 在这个时候就可以用的上了,虽然你使用测试号也是可以进入的,到底后面还是要用自己的 ID 的。
- 开发模式:小程序
- 后端服务:不使用云服务,前面还是以学习小程序本身为主,不使用此选项
- 语言:JavaScript 和 Typescript,根据自身的熟悉程序选择即可
新建后,第一个小程序就完事了哈~
(二) 初识小程序
(1) 界面总览
下面的区域就是一个初始化新建好的项目界面了,图片中标的很清楚,每一块的具体内容,第一次创建项目时,也可以在设置的通用设置中将默认的的工作区以及主题(浅色还是深色)根据自己的需要修改
说明:直接在小程序官方的工具中编写代码也可以,不过我个人选择配合 vscode 或 sublime 进行代码的编辑,在微信官方开发工具或者那个查看结果以及控制台的一些信息
(2) 文件类型
与传统的网页开发有一丝小不同,微信小程序重新定义了它的描述语言,例如 wxml、wxss 等,同时还额外提供了一层 JSON 的配置文件
wxml(页面结构文件)
- 用来书写、构建页面,类似传统网页开发的 HTML
wxss(样式表文件)
- 用于制定页面样式,从而美化页面,类似传统网页开发的 CSS
js(脚本文件)
- 用于指定一定页面交互逻辑,就是 Javascript
json(静态数据配置文件)
- JSON 格式的配置文件,设置程序的一些配置效果
(3) 结构目录
A:总体介绍
通过官方开发工具或者vscode等其他编辑器,你可以看到,新建一个项目后,会默认生成一个初始化的项目结构,里面含有很多文件,其后缀格式都是我们上面介绍过的
下面我们针对一些主要的内容进行一个基本的介绍
├── pages // 页面文件夹
| ├── index // 首页
│ | ├── index.js // 首页逻辑脚本文件
│ | ├── index.json // 首页配置文件
│ | ├── index.wxml // 首页页面结构文件
│ | ├── index.wxss // 首页样式文件
| ├── logs // 日志页面
│ | ├── logs.js // 日志页面逻辑脚本文件
│ | ├── logs.json // 日志页面配置文件
│ | ├── logs.wxml // 日志页面页面结构文件
│ | ├── logs.wxss // 日志页面样式文件
| ├── utils // 工具js文件(第三方,可删除)
│ | ├── util.js // 日志页面逻辑脚本文件
│ ├── app.js // 项目的全局脚本文件
│ ├── app.json // 项目的全局配置文件
│ ├── app.wxss // 项目的全局配置文件
│ ├── project.config.json // 项目的开发者工具的配置
│ ├── sitemap.json // 索引配置文件
具体的一些例如 wxml wxss 等的用法,会在后面提到,这里我们还要提一下两个内容:
A:针对说明
app.js:项目的入口文件,用来创建应用程序的对象,处理程序的生命周期
app.json:项目全局的配置文件,涉及到了页面的路径,界面/窗口 的表现时间,网格超时的时间,还有小程序底部的 tab 等等,还是非常重要的,初始化新建项目后,可以看到如下pages 字段和 windows 字段
{
"pages":[
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
}
}
- pages 字段,是关于页面路径的,也就是给客户端说明出你小程序页面到底在哪里
- window 字段,对应 页面的颜色、标题等等
- tabbar 字段,底部 tab 栏(切换页面)
贴一段官网关于tabbar的说明(官网的说明确实很可!):
如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
属性 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
color | HexColor | 是 | tab 上的文字默认颜色,仅支持十六进制颜色 | |
selectedColor | HexColor | 是 | tab 上的文字选中时的颜色,仅支持十六进制颜色 | |
backgroundColor | HexColor | 是 | tab 的背景色,仅支持十六进制颜色 | |
borderStyle | string | 否 | black | tabbar 上边框的颜色, 仅支持 black / white |
list | Array | 是 | tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab |
|
position | string | 否 | bottom | tabBar 的位置,仅支持 bottom / top |
custom | boolean | 否 | false | 自定义 tabBar,最低需要 2.5 版本 |
而一般我们想要设置出常见的效果就会选择使用 list 进行配置
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
pagePath | string | 是 | 页面路径,必须在 pages 中先定义 |
text | string | 是 | tab 上按钮文字 |
iconPath | string | 否 | 图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。 当 position 为 top 时,不显示 icon。 |
selectedIconPath | string | 否 | 选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px 81px,不支持网络图片。 当 position 为 top 时,不显示 icon。* |
给大家举个例子,书写的格式就是这样的,别忘了制定了 tabbar 一定要有在pages中设置对应的页面哦
"tabBar": {
"color": "#999",
"selectedColor": "#ff2d4a",
"backgroundColor": "#fafafa",
"position": "bottom",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "icons/home.png",
"selectedIconPath": "icons/home.png"
},
{
"pagePath": "pages/user/index",
"text": "我的",
"iconPath": "icons/my.png",
"selectedIconPath": "icons/my.png"
}
]
},
每一个小程序页面也可以使用 .json
文件来对本页面的窗口表现进行配置。页面中配置项在当前页面会覆盖 app.json
的 window
中相同的配置项。同样的,也有很多配置内容,看一下官网就可以了
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html
—————分割线—————
【WXML 常用语法】
(一) WXML 是什么
官方说明:WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构
在前面我们就已经提过,WXML,就可以理解为我们传统页面中的HTML,它是微信为我们提供的一套标签语言,可以说它就是我们小程序的脸面(虽然没经过CSS装饰前不一定光鲜亮丽),开发中 WXML 页面就作为我们一些逻辑行为的入口,以及效果展示的承载者。
再大白话一点:你所看到的小程序页面长什么样(不涉及背后做了什么行为,只说表面),就是 WXML(主要) + WXSS (美化) 实现的
这一篇,我们主要涉及到的是 WXML 中例如数据绑定、或者运算等等,但是学习之前,很显然,我们需要认识几个常见的标签,后面我们会总结一些常用的标签,下面会用到的有:
、
官网——WXML语法文档
https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/
官网——组件文档
https://developers.weixin.qq.com/miniprogram/dev/component/
这是text标签1
这是text标签2
这是div标签1
这是div标签2
看一下效果
(二) 数据绑定
虽然还不涉及到什么页面的美化,以及复杂的标签,不过一个极为简单的静态页面现在已经可以构造出来了,归根结底,我们最后都是要进行前后台数据的交互的,而微信小程序就为我们提供了很多很好用的用法,能很快的进行数据的绑定操作
有一个前提条件,我们先模拟一些数据,我们只需要在页面的 js 文件中的 data对象中定义小程序初始化的数据,例如下面代码,我们随便拿一些常见的数据类型来模拟一下
Page({
/**
* 页面的初始数据
*/
data: {
msg: "你好,微信小程序",
status: 100,
isLogin: true,
person:{
name: "张三",
age: 22,
profession: "student"
},
isChecked: true
},
})
(1) 常见类型普通写法
如何在 WXML 标签组件中进行数据的绑定其实是非常简单的,微信小程序中通过 {{}}
来解析刚才在 JS 中模拟的变量
要注意:直接通过 {{}}
解析的变量都是 页面 js 文件中 Page --> data 下的
A:字符串
字符串内容直接用两个大括号括住接收就可以了,在上面我们有这样的定义:
msg: "你好,微信小程序",
所以直接括住 msg 就可以获取到后面的值了
{{msg}}
B:数值
数值也是一样直接可以获取
{{status}}
C:布尔类型
布尔类型第一个代码是直接打印出其布尔类型 true 或者 false
{{isLogin}}
而下面配合 checkbox 就可以实现是否选中的效果
D:对象
对象这块如果你直接打印 person 就会输出一个 Object 类型,所以如果想要拿到对象的属性值,一定要指定到具体的属性
{{person.name}}
{{person.age}}
{{person.profession}}
看一下上述所有的绑定效果
(2) 组件属性中
例如我们 view 组件的 id 值前缀是固定的 user-
后面就是用户的序号,这时候就可以通过变量来进行巧妙的解析到属性中了
注:不要轻易的乱加空格,否则可能会读取失败例如:
看一下 Wxml 的源码 id 这个属性中已经进行了拼接,这种方式同样还会常用在 class 或者 style 中,配合三元运行能不错的实现一些需求,下面我们会提到
(三) 运算
首先我们依旧定义一些数据,三个整数,和两个字符串
Page({
data: {
a: 11,
b: 22,
c: 33,
msg: "姓名",
name: "张三"
},
})
(1) 算数运算
直接在 {{}}
中进行 加减乘除等的运算,直接就可以得到结算的结果,例如:
{{a + b + c}}
{{a + b}} + {{c}}
(2) 字符串运算
如果是字符串类型的数据,利用 +
进行运算,结果是一个拼接的效果
{{'a' + 'b' + 'c'}}
{{'11' + '22' + '33'}}
{{msg + ':' +name}}
(3) 逻辑判断
这个就是常见的 if 判断,例如使用 wx:if
这个属性,那么就只有在后面的表达式为 true 的情况下才会显示文本 a 大于 0
a 大于 0
(4) 三元运算
三元运算的应用场景还是很多的(等式 ? : true情况,false的情况)
{{11 + 22 === 33 ? true : false}}
{{11 + 22 === 33 ? '正确' : '错误'}}
{{a + b === c ? '等式成立' : '等式不成立'}}
三元补充:
通过在属性中解析变量的方式,可以达到根据变量的值,来指定不同的 class名,以显示不同的样式
例如我们的 css 样式是这样的,iconfont 是我们共用的,所以直接写在属性中就可以了,而究竟是用 icon-back 还是 icon-remove 就可以通过一个变量来操纵,例如我们下面的 isClick 就是在 js 里 data 中定义的一个 布尔类型的变量
.iconfont {......}
.icon-back:before {......}
.icon-remove:before {......}
当 isClick 为 true 就执行 class 就是这样的 class="iconfont icon-remove"
为 false 就是用另一个,我们就可以通过 控制变量值来进行不同的显示
看一下上述所有运算的执行结果(不含补充的结果)
(四) 列表循环(列表渲染)
(1) 模拟数据
依旧我们给一些模拟数据,一个是 person 这样的对象内部有一些属性,还有一个就是 studentList 学生集合,其中有三个学生的数据
Page({
data: {
person:{
pid: 1,
name: "张三",
age: 22,
profession: "student"
},
studentList:[{
sid:1,
name:"汤姆",
gender:"男"
},{
sid:2,
name:"杰克",
gender:"男"
},{
sid:3,
name:"玛丽",
gender:"女"
}
]
},
})
如果我们接受到了后台的一些集合或者数组等内容,循环遍历是一个非常常用的操作
(2) 正式使用
组件(标签)上使用 wx:for
就可以绑定一个数组或集合内容,就可用数组中各项的数据重复渲染该组件
先举个例子
{{index}} --- {{item.name}}
我们分别来解释一下:
wx:for
:数组或者对象wx:for-item
:循环项(数组或集合)的变量名称,同时一般默认为item
wx:for-index
:循环项(数组或集合)的索引(下标),同时一般默认为index
wx:key
:绑定一个唯一的值,可以提高列表渲染的性能,可以简单理解为主键的概念,例如这里我传入了 studentList 中不可能重复的值 sid- 如果你的数组只是一个普通的数组,例如
[11,22,65,23,3,6]
没有所谓唯一的值,就可以使用wx:key="*this"
即表示你的数组是一个普通的数组,*this
表示的是 item 本身
- 如果你的数组只是一个普通的数组,例如
如果传这个 key 值,会有一个警告弹出
(3) 嵌套用法的补充说明
- 如果你的循环只有一层,那么
wx:for-item="item"
wx:for-index="index"
这两个内容实际上是可以省略的,小程序会自动把这两个内容设置为 item 和 index - 如果你的循环是一个嵌套的效果,那么切记一定 item 和 index 的值一定不要重复...
嵌套的正确写法
{{i}} * {{j}} = {{i * j}}
再举一个例子(遍历对象的所有属性):
{{key}} --- {{value}}
看一下执行效果
(4) 循环(渲染)block标签
如果我们使用两种不同的组件(标签)看看最终渲染出来的结果有什么区别呢?
方法一(view 标签)
{{key}} --- {{value}}
- 结果一
pid --- 1
name --- 张三
age --- 22
profession --- student
方法二(block 标签)
{{key}} --- {{value}}
- 结果二
pid --- 1 name --- 张三 age --- 22 profession --- studen
通过控制台中 Wxml 界面,可以看到,只有在 view 组件下遍历内容时才真正的生成了 dom 结构,而直接使用 block 则只是将内容重复写了几次,不会变成真正的dom元素
(五) 逻辑判断(条件渲染)
(1) 用法
这块理解没什么难点,无非就是关于逻辑的几种判断,看一个例子就清楚了
说明:isLogin 是在 js 中模拟的一个数据,分别赋予 true false 或者其他的 例如 null,会根据逻辑值显示不同的内容
已经登录,逻辑为 true
未登录,逻辑为 false
不确定逻辑
(2) 补充 hidden
补充用法(hidden):
通过上面的 if else 等可以控制组件例如 view 的显示,而有一个属性 hidden 同样可以实现根据逻辑值控制组件的显示
例如给 hidden 传入一个 false 就会显示出来
不隐藏
(3) wx:if 和 hidden 选择哪个
那么既然一定程度上都可以达到这种效果,我们选择那个呢?
先来看一下
---- 分界线 -----
if 隐藏
隐藏
看一下渲染出来的结构,关于 wx:if
的那块直接就没有渲染出来了,只有 hidden 的那个,由此可以得出:
wx:if
是直接把标签从页面结构中移除掉了hidden
是通过添加样式的方式的隐藏,标签结构还在
所以,当标签不总是切换显示的时候,可以考虑先用 wx:if
,标签切换频繁的时候用 hidden
(六) 模板
(1) 创建模板
模板的字面意思就是,一个可以应用在多处,通用的一个版块,如何去用呢?
使用 name 属性,作为模板的名字。然后在
内定义代码片段
这是一个 template 模块
{{id}}: {{name}}
(2) 引入模板
我们需要在想引用模板的页面中引入模板,WXML 提供两种文件引用方式 import
和 include
如果没有效果,可以看一下是不是路径写错了,要根据自己定义的来写哦
这两者的区别就是 import 有作用域的问题,官网这里写的挺清楚,我直接贴一下:
import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。
如:C import B,B import A,在C中可以使用B定义的template
,在B中可以使用A定义的template
,但是C不能使用A定义的template
。
(3) 调用模板
使用 is 属性,声明需要的使用的模板,也就是与上面的 name 一致就可以了,然后将模板所需要的 data 传入,如(直接传入):
赋值方式还有一种常用的
它的意思就是将调用这个模板页面中的 student 对象变量赋值给这个模板,三个点就是一个扩展运算符,作用就是将这个student 对象展开
例如
Page({
data: {
student: {
id: 0,
name: '张三',
}
}
})
—————分割线—————
【事件绑定】
一 通过实例来认识
(一) 给出代码
我们直接通过一个实例来引入我们想要讲解的内容:
你输入的是:{{number}}
上述代码就四行,首先是一个 input 输入框,目的是用来输入一些值,同时下面标签就会显示,接着是两个 button 用来分别执行 +1 或者 -1 的操作,最后一个 view 标签就是为了 进行数据输入或变化的回显
Page({
data: {
number:0
},
handleInput(e){
this.setData({
number:e.detail.value
})
},
handletap(e){
const operation = e.currentTarget.dataset.operation
this.setData({
number:this.data.number + operation
// number:this.data.number * 1 + e.currentTarget.dataset.operation
})
}
})
这里给出的就是对应的 js 代码,涉及到了对于输入以及 +1 或者 -1 操作的一个具体逻辑处理,核心就是围绕 data 中定义的 number 变量进行处理(具体逻辑接着会提到),结合前面的 wxml 代码分析一下:
(二) 分析代码
分析标签中属性中的部分:
bindinput="handleInput"
bindtap="handletap" data-operation="{{-1}}"
- 绑定事件的关键字是
bind
例如上面用到的bindinput
和bindtap
就是分别对于输入和点击事件的一个绑定 - 而后面的一个名称例如 handleInput 就是自定义的事件名称,我们在 js 中书写方法也是与这个后面的名称相对应
- 注:绑定关键字为 bind 仅针对此例,并非只是 bind
补充几个 input 中相对常用的事件绑定属性
属性 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
bindinput | eventhandle | 是 | 键盘输入时触发,event.detail = {value, cursor, keyCode},keyCode 为键值,2.1.0 起支持,处理函数可以直接 return 一个字符串,将替换输入框的内容。 | 1.0.0 |
bindfocus | eventhandle | 是 | 输入框聚焦时触发,event.detail = { value, height },height 为键盘高度,在基础库 1.9.90 起支持 | 1.0.0 |
bindblur | eventhandle | 是 | 输入框失去焦点时触发,event.detail = {value: value} |
如果想要获取到输入框的值,通过时间源对象来获取 e.detail.value
(1) JS 中赋值问题
输入框中输入的值赋值给 data 中的 number,如果按惯性思维直接赋值是有问题的
- 不能使用 this.data.numer = e.detail.value
- 不能使用 this.number = e.detail.value
- 需要使用:
this.setData({
number:e.detail.value
})
(2) JS 中按钮传参问题
添加按钮点击事件:关键字是 bindtap
我们按钮想要达到的目的是,点击按钮进行 number 的 +1 或者 -1 ,通过根据我们上面的 js 代码可以看出,这里所采用实现的方式是根据页面属性中传来的参数,进行相加,例如 number + 1 或者 number + (-1) 达到加减效果
但是,直接传参是有问题的!!!只能通过属性赋值
正解:bindtap="handletap" data-operation="{{-1}}"
(3) 忘记想要的值对应在哪里
说明:如果记不住例如:e.detail.value、e.currentTarget.dataset.operation 则可以使用如下的方式,先把事件打印出来
handleInput(e){
console.log(e);
},
在找到对应的层级
二 事件绑定类别
(一) 分类
我们上面的例子使用了 bind 这个事件绑定关键字,但是它会发生冒泡事件
- 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递
- 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递
我们还有一些别的选择,我们下面在 (3) (4) 中会一个一个进行分析
- bind:普通绑定(会发生冒泡事件)
- catch:可以阻止事件冒泡
- capture-bind: 捕获阶段绑定(后面的捕获流程和冒泡流程还会继续执行)
- capture-catch:中断捕获阶段和取消冒泡阶段,在捕获阶段阻止事件的传递
(二) 冒泡事件列表
在分析前,我们还要补充一个点,那就是 WXML 的冒泡事件列表:
我们起码现在知道了 bind 和 catch 的作用,但是正例如我们上面用到的 bindinput 或者 bindtap ,bind 后面的内容又是什么呢?是固定的还是自定义的呢?这一段我直接贴一段官网的文档说明
类型 | 触发条件 | 最低版本 |
---|---|---|
touchstart | 手指触摸动作开始 | |
touchmove | 手指触摸后移动 | |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | |
touchend | 手指触摸动作结束 | |
tap | 手指触摸后马上离开 | |
longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 | 1.5.0 |
longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) | |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 | |
animationstart | 会在一个 WXSS animation 动画开始时触发 | |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 | |
animationend | 会在一个 WXSS animation 动画完成时触发 | |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 | 1.9.90 |
注:除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如 form 的submit事件,input 的input事件,scroll-view 的scroll事件,(详见各个组件)
看完上面的表格,应该就比较清楚了,正因为我们很多事件都是通过手机点一下某个组件等进行,所以 tap 是比较常用的
(三) bind 和 catch
说明:代码在下面自取
(1) bind
前面我们提到了,使用 bind 会发生冒泡事件,我们来模拟一下
首先我们写了三个嵌套的 view 标签,然后接着使用 bindtap 进行事件绑定,进行一个基本的打印逻辑,看看会有什么情况发生
当点击中间层后,首先执行了中间层的事件效果,但是最外层的事件效果也被执行了,这也就是冒泡事件发生了
冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递
(3) catch
这一次我们仍然点击中间这一层,如果在上面的基础上,我们将中间层的事件绑定使用 catch,而不是 bind ,结果会是怎样呢?
结果就是冒泡事件被阻止了,点击后只显示中间层的事件
(四) capture-bind 和 capture-catch
前面提及到这两个内容的时候,我们提到了一个概念也就是事件的捕获阶段,简单说一下:
自基础库版本 1.5.0 起,触摸类事件支持捕获阶段。捕获阶段位于冒泡阶段之前,且在捕获阶段中,事件到达节点的顺序与冒泡阶段恰好相反。需要在捕获阶段监听事件时,可以采用capture-bind、capture-catch关键字,后者将中断捕获阶段和取消冒泡阶段。
(1) capture-bind:tap
依旧是刚才的例子,将三层的属性都修改为 capture-bind:tap="handleTap1"
这种形式的
当我们点击最里层的内容时,执行结果如下:
即它与包裹它的两层都执行了,且是从外至内
(2) capture-catch:tap
将三层属性修改为 capture-catch:tap="handleTap1"
这种形式,点击任意一层:
结果都是只执行最外层的
(五) 简单总结
- bind:点击会触发它和包裹它的所有事件,且从内向外执行(冒泡事件)
- catch:点击哪个就触发哪个,独立的(阻止冒泡事件)
- capture-bind: 点击会触发它和包裹它的所有事件,且从外向内执行
- capture-catch:如何点击都只会触发最外层的事件
(六) 代码提取
代码给出的是 capture-bind:tap 的情况, bindtap 或者 catchtap 以及 capture-catch 只需要将 capture-bind:tap 替换就行了
wxml
这是最外层
这是中间层
这是最里层
wxss
.outer {
text-align: center;
background-color: red;
height: 300rpx;
}
.middle {
background-color: orange;
width: 60%;
height: 200rpx;
}
.inner {
background-color: yellow;
width: 60%;
height: 100rpx;
}
—————分割线—————
【WXSS 浅谈】
(一) 尺寸问题
WXSS (WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。
其实和原来的 CSS 没差特别多,不过先说一个挺重要的点,就是微信小程序官方提供的一种,解决尺寸问题的方案 rpx
例如设计师给你的设计稿是px为单位的,但是如果你直接使用这样固定的写法,就会导致在不同的机型上,出现内容尺寸比例不一的现象,当然我们也可以使用百分比来解决这个问题,而今天我们简单说一说 rpx
(1) 存在的问题
首先我们,随便写一个 200px * 200px 的方框
view{
width: 200px;
height: 200px;
background-color: deeppink;
}
在不同的机型下,实际上大小可以说写死了,在其他机型下,很清晰就可以看到,右边的留白,发生了很大的变化
(下图为 750 * 550 自定义的)
(2) 尺寸单位
- rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
设备 | rpx换算px (屏幕宽度/750) | px换算rpx (750/屏幕宽度) |
---|---|---|
iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况
知道了这些,接着看上面的问题,首先,1px=rpx*(750/设备屏幕宽度),因为原型图我们以iphone6为标准,所以公式就是1px=rpx*(750/375),所以1px=2rpx,这也对应了上面的表格,所以我们只需要将原来固定的 200px 修改一下,成为 400rpx,这样就会发现方框就会根据不同屏幕的尺寸来变化了
(3) 总结
使⽤步骤:
- 确定设计稿宽度 pageWidth
- 计算⽐例 750rpx = pageWidth px ,因此 1px=750rpx/pageWidth
- 在less⽂件中,只要把设计稿中的 px => 750/pageWidth rpx 即可
(二) 引入样式
刚才,我们简单说了一下比例尺寸的问题,在同一个页面下的 样式文件,是不需要引入的,也就是说 例如 test 文件夹下的 test.wxml 中就可以自动匹配上 test.wxss 中的样式
但是,如果想自定义一些样式供多个页面使用,怎么做呢,例如我们在根目录下创建一个文件夹,在其中创建一个名为 commom.wxss 的样式文件,为了效果显著一些,把字体改一下样式
view{
color: red;
font-size: 30px;
}
我们在需要页面的 WXSS 中引入一下
注:路径只能写相对路径哈
@import"../../styles/common.wxss";
效果一下就显出来了
(三) 选择器
(1) 支持的选择器:
选择器 | 样例 | 样例描述 |
---|---|---|
.class | .intro |
选择所有拥有 class="intro" 的组件 |
#id | #firstname |
选择拥有 id="firstname" 的组件 |
element | view |
选择所有 view 组件 |
element, element | view, checkbox |
选择所有文档的 view 组件和所有的 checkbox 组件 |
::after | view::after |
在 view 组件后边插入内容 |
::before | view::before |
在 view 组件前边插入内容 |
注意:通配符 *
是不支持的,即下列代码是无效的
*{
margin:0;
padding:0;
box-sizing:border-box;
}
(2) 全局样式与局部样式
定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。
(四) 原生小程序使用 less 的方法
原生的小程序是不支持使用 less 的,当然了,你可以选择直接写样式,如果想要使用 less 的话,可以在 vscode 中安装例如 Easy LESS
插件(此插件需要在 vscode 配置中加入)
"less.compile": {
"outExt": ".wxss"
},
在编写样式的地方,直接新建 less 文件,例如 test.less 编辑保存后,会生成对应的WXSS文件
—————分割线—————
【常用组件及自定义组件】
(一) 常用标签
组件你可以理解为传统页面开发时候的各种标签,例如 div span 等等,我这里只说一些常用的,这样就能能搭建出一个基本的页面了,但是如果想要更加美观以及拥有更好的体验,就需要 XSS 和 别的一些强大的组件了,如果有额外的需求,可以去官方文档查阅一下 (同时不常用的属性,也就不提了)
https://developers.weixin.qq.com/miniprogram/dev/component/
(1) view
view 可以理解为传统页面开发中的 div 块级元素,使用 view 会换行
关于 view 标签,还有一些额外的属性,说的也很清楚,但是前期的话,其实不考虑这个也是可以的,就单纯的当做一个布局的 div 来用
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
hover-class | string | none | 否 | 指定按下去的样式类。当 hover-class="none" 时,没有点击态效果 |
1.0.0 |
hover-stop-propagation | boolean | false | 否 | 指定是否阻止本节点的祖先节点出现点击态 | 1.5.0 |
hover-start-time | number | 50 | 否 | 按住后多久出现点击态,单位毫秒 | 1.0.0 |
hover-stay-time | number | 400 | 否 | 手指松开后点击态保留时间,单位毫秒 | 1.0.0 |
(2) text
text 可以理解为传统页面中的 span 行内元素,text 不会换行
text 涉及的一些标签
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
selectable | boolean | false | 否 | 文本是否可选 | 1.1.0 |
space | string | 否 | 显示连续空格 | 1.4.0 | |
decode | boolean | false | 否 | 是否解码 | 1.4.0 |
space 的合法值
值 | 说明 |
---|---|
ensp | 中文字符空格一半大小 |
emsp | 中文字符空格大小 |
nbsp | 根据字体设置的空格大小 |
decode可以解析的有
< > & '
简单测试一下其中两个,可以看到,后者长按可以选择文字,同时编码被解析成空格,前者反之
测 试
测 试
(3) image
image 就是图片相关的一个组件,这个组件默认宽度320px、⾼度240px,同时支持懒加载
我摘了三个比较常用的属性出来
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
src | string | 否 | 图片资源地址 | 1.0.0 | |
mode | string | scaleToFill | 否 | 图片裁剪、缩放的模式 | 1.0.0 |
lazy-load | boolean | false | 否 | 图片懒加载,在即将进入一定范围(上下三屏)时才开始加载 | 1.5.0 |
他作为图片的一个承载物,我们重点多说一下关于 mode 的问题,也就是图片的显示形式
所以先来看一下,mode 的取值范围(节选了相对常用的,其余的取值都属于裁剪类型)
值 | 说明 | 最低版本 |
---|---|---|
scaleToFill | 缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素 | |
aspectFit | 缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。 | |
aspectFill | 缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。 | |
widthFix | 缩放模式,宽度不变,高度自动变化,保持原图宽高比不变 | |
heightFix | 缩放模式,高度不变,宽度自动变化,保持原图宽高比不变 | 2.10.3 |
比较常用是 widthFix,在这几个其中,aspectFill 相对还是用的比较少的
(4) swiper
这个组件,是小程序页面中的一个轮播图的效果
swiper
是轮播图的一个总的容器,swiper-item
代表其中的每一个内容,配合其属性,能很方便的达到需要的样式
同样摘了几个常见的属性
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
indicator-dots | boolean | false | 否 | 是否显示面板指示点 | 1.0.0 |
indicator-color | color | rgba(0, 0, 0, .3) | 否 | 指示点颜色 | 1.1.0 |
indicator-active-color | color | #000000 | 否 | 当前选中的指示点颜色 | 1.1.0 |
autoplay | boolean | false | 否 | 是否自动切换 | 1.0.0 |
interval | number | 5000 | 否 | 自动切换时间间隔 | 1.0.0 |
duration | number | 500 | 否 | 滑动动画时长 | 1.0.0 |
circular | boolean | false | 否 | 是否采用衔接滑动 | 1.0.0 |
vertical | boolean | false | 否 | 滑动方向是否为纵向 | 1.0.0 |
显示比例问题
首先说明一下,swiper存在一些默认的样式
- width: 100%
- height 150px
image 默认宽高
- width: 320px
- height: 240px
如果,swiper 的高度出现了问题,给出一个解决方式
先根据素材图片的宽高比例,等比计算 swiper 的宽高,这样高度就换算出来了
swiper 高度 = swiper 宽度 * 素材高度 / 素材宽度
即:height: 750rpx * 素材高度 / 素材宽度
来看个综合一些的例子
WXML
图片随便自己做了两张
WXSS
swiper{
width: 100%;
height: calc(750rpx * 275 / 1000);
}
image{
width: 100%;
}
看一下效果,现在就实现了轮播图的效果,同时会5秒自动循环轮播,自己可以对照属文档进行定制修改
(5) navigator
导航组件,这块可以理解为传统页面开发的超链接标签
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
target | string | self | 否 | 在哪个目标上发生跳转,默认当前小程序 | 2.0.7 |
url | string | 否 | 当前小程序内的跳转链接 | 1.0.0 | |
open-type | string | navigate | 否 | 跳转方式 | 1.0.0 |
open-type 的合法值
值 | 说明 | 最低版本 |
---|---|---|
navigate | 保留当前页面,跳转到应用内的某个页面,但是不能跳到tabbar 页面 | |
redirect | 关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到tabbar 页面 | |
switchTab | 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 | |
reLaunch | 关闭所有页面,打开到应用内的某个页面 | 1.1.0 |
navigateBack | 关闭当前页面,返回上⼀页面或多级页面。可通过 getCurrentPages() 获取当 | 1.1.0 |
exit | 退出小程序,target=miniProgram时⽣效 | 2.1.0 |
下面给出一个测试的代码,默认不添加 open-type
的写法会有一个返回上一层的
默认跳转到test06
redirect跳转到test06
switchTab跳转到主页
reLaunch跳转到主页
(6) rich-text
这是一个富文本标签,作用就是把字符串中的对应标签解析出来,其主要的一个属性就是 nodes
,我们先简单看一下 nodes的属性有哪些
现支持两种节点,通过type来区分,分别是元素节点和文本节点,默认是元素节点,在富文本区域里显示的HTML节点 元素节点:type = node
属性 | 说明 | 类型 | 必填 | 备注 |
---|---|---|---|---|
name | 标签名 | string | 是 | 支持部分受信任的 HTML 节点 |
attrs | 属性 | object | 否 | 支持部分受信任的属性,遵循 Pascal 命名法 |
children | 子节点列表 | array | 否 | 结构和 nodes 一致 |
来看一个例子,这个nodes 的值,我们去 js 中定义一个
有两种方式赋值,一种就是我注释掉的那一行,直接使用一个含有标签的字符串,直接赋值就会解析,另一种就是通过后面这样 JSON 的一种格式赋值,效果是一样的
Page({
data: {
// receive:'理想二旬不止
'
receive:[{
name:'div',
attrs:{
class:'div_class'
},
children:[{
name:'h3',
attrs:{},
children:[{
type:'text',
text:'理想二旬不止'
}]
}]
}]
},
})
(7) button
按钮标签,也算是非常常用的内容了,同时相比较传统页面开发中的按钮,微信小程序中又有很多开放式的功能,首先看一些涉及的一些属性(更多内容可以看官网文档):
https://developers.weixin.qq.com/miniprogram/dev/component/button.html
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
size | string | default | 否 | 按钮的大小 | 1.0.0 |
type | string | default | 否 | 按钮的样式类型 | 1.0.0 |
plain | boolean | false | 否 | 按钮是否镂空,背景色透明 | 1.0.0 |
disabled | boolean | false | 否 | 是否禁用 | 1.0.0 |
loading | boolean | false | 否 | 名称前是否带 loading 图标 | 1.0.0 |
form-type | string | 否 | 用于 form 组件,点击分别会触发 form 组件的 submit/reset 事件 | 1.0.0 | |
open-type | string | 否 | 微信开放能力 | 1.10 |
size 的合法值
值 | 说明 |
---|---|
default | 默认大小 |
mini | 小尺寸 |
type 的合法值
值 | 说明 |
---|---|
primary | 绿色 |
default | 白色 |
warn | 红色 |
form-type 的合法值
值 | 说明 | 最低版本 |
---|---|---|
submit | 提交表单 | |
reset | 重置表单 |
open-type 的合法值
值 | 说明 | 最低版本 |
---|---|---|
contact | 打开客服会话,如果用户在会话中点击消息卡片后返回小程序,可以从 bindcontact 回调中获得具体信息 | 1.1.0 |
share | 触发用户转发,使用前建议先阅读 | 1.2.0 |
getPhoneNumber | 获取用户手机号,可以从bindgetphonenumber回调中获取到用户信息 | 1.2.0 |
getUserInfo | 获取用户信息,可以从bindgetuserinfo回调中获取到用户信息 | 1.3.0 |
launchApp | 打开APP,可以通过app-parameter属性设定向APP传的参数 | 1.9.5 |
openSetting | 打开授权设置页 | 2.0.7 |
feedback | 打开“意见反馈”页面,用户可提交反馈内容并上传日志,开发者可以登录小程序管理后台后进入左侧菜单“客服反馈”页面获取到反馈内容 | 2.1.0 |
这几个代码就是通过 size
、type
进行基本的大小或者说类型实现一个样式
下面就是一些开放的功能
需要说明的几个点:
- 联系客服这个功能只能在真机调试,需要先在后台绑定一个客服的微信号码,接着在开发工具中选择真机调试,然后扫描二维码就可以了
- 获取电话号码,以及用户信息,需要结合事件来做,例如:
Page({
// 获取用户的手机号码信息
getPhoneNumber(e){
console.log(e);
},
// 获取用户个人信息
getUserInfo(e){
console.log(e);
}
})
例如获取用户信息
但是电话号码,不是企业的小程序账号 没有权限来获取用户的手机号码
- 打开App,是在 app 中 通过 app 的某个链接打开小程序,然后在小程序 再通过这个功 重新打开 app
- 当前版本的微信小程序,在.wxss文件里设置Button宽度无效,网络上的一种解决方案就是把 app.json里的 style: v2语句删掉,还有一种就是像我代码中一样,直接加 style,暂时推荐后者吧,此处未深究
(8) icon
微信小程序默认封装了一些图标,也很简单,只有三个属性
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
type | string | 是 | icon的类型,有效值:success, success_no_circle, info, warn, waiting, cancel, download, search, clear | 1.0.0 | |
size | number/string | 23 | 否 | icon的大小 | 1.0.0 |
color | string | 否 | icon的颜色,同css的color | 1.0.0 |
简单用一下
如果不指定颜色,其默认都是有一定颜色样式的,如果指定了 color 就会覆盖掉原来的颜色
(9) radio
单选框组件,需要配合 radio-group 使用,下面看代码就明白了
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
value | string | 否 | radio标识。当该radio选中时,radio-group 的 change 事件会携带 radio 的 value | 1.0.0 | |
checked | boolean | false | 否 | 当前是否选中 | 1.0.0 |
disabled | boolean | false | 否 | 是否禁用 | 1.0.0 |
color | string | #09BB07 | 否 | radio的颜色,同css的color | 1.0.0 |
简单用一下,
男
女
你选择的性别是:{{gender}}
js 内容,至于 e.detail.value 如何来的,可以通过 console.log(e) 打印看到
Page({
data: {
gender: ""
},
handleChange(e){
// 获取单选框中的值
let gender=e.detail.value;
// 把值 赋值给 data中的数据
this.setData({
gender
})
}
})
(10) checkbox
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
value | string | 否 | checkbox 标识,选中时触发 checkbox-group 的 change 事件,并携带 checkbox 的 value | 1.0.0 | |
disabled | boolean | false | 否 | 是否禁用 | 1.0.0 |
checked | boolean | false | 否 | 当前是否选中,可用来设置默认选中 | 1.0.0 |
color | string | #09BB07 | 否 | checkbox的颜色,同css的color | 1.0.0 |
直接用一下
{{item.name}}
选中的内容:{{checkedList}}
Page({
data: {
list:[
{
id:0,
name:"️",
value:"太阳"
},
{
id:1,
name:"",
value:"月亮"
},
{
id:2,
name:"⭐️",
value:"星星"
}
],
checkedList:[]
},
// 复选框的选中事件
handleItemChange(e){
// 获取被选中的复选框的值
const checkedList=e.detail.value;
// 进行赋值
this.setData({
checkedList
})
}
})
运行结果:
(二) 自定义组件(标签)
(1) 快速体验
如果我们想要自定义一些组件,也就是说将一些代码抽离出来,可以达到复用等的效果
我们一步一步举个例子:
首先创建文件夹目录 components/header
接着右键创建组件 header,点击新建 Component
结构就是这样的
一般点击创建组件的方式会自动将组件的 json 文件中声明组件,如果没有需要自己手动修改component 为 true
{
"component": true,
}
接着在组件WXML中随便写点东西,然后打开想要引用组件的页面,首先在 json 中说明引用组件
{
"usingComponents": {
"header":"/../../components/header/header"
}
}
然后直接引用就可以了,效果就出来了
(2) 组件传参
组件传参有两个方向,一个是父组件 --> 子组件 ,还有就是反过来。注:父组件是页面,子组件是自定义组件
- ⽗组件通过属性的⽅式给⼦组件传递参数
- 组件通过事件的⽅式向⽗组件传递参数
通过一个例子来演示
在上面结构上自己写一个自定义的组件,一个导航条的效果
自定义组件的页面代码
{{item.name}}
自定义组件的样式文件如下
/* components/header/header.wxss */
.header_tabs_title{
display: flex;
padding: 10px;
}
.header_tabs_title_item{
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.active{
color:blue;
border-bottom: 5rpx solid currentColor;
}
自定义组件的 js文件,在 properties
中的内容,就是接收到父(页面)的数据,也就是一个关于导航的数组,其中包括首页测试关于等等导航文字内容
- headerTabs的位置:要接受的名称,自己定
- type:要接收的数据的类型
- value:默认值
而下面的方法就是关于父传数据到子组件的内容,其代表触发父组件中的自定义事件,同时传递数据给 父组件
// components/header/header.js
Component({
/**
* 组件的属性列表
*/
properties: {
headerTabs:{
type:Array,
value:[]
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
hanldeItemTap(e){
const {index}=e.currentTarget.dataset;
// 触发父组件中的自定义事件 同时传递数据给
this.triggerEvent("itemChange",{index});
}
}
})
补充:e.currentTarget.dataset; 是怎么来的,还是老办法,console 打印一下
父页面
绑定一个事件,同时把等会再 js 中的定义数据,传递到自定义组件中去,名称就是刚才接收的 headerTabs
父页面的 js
说明:let { headerTabs } = this.data;
这是 ES6 的写法,也可以写成
let headerTabs = this.data.headerTabs;
遍历数组的时候 修改了 v ,就把源数组也修改了
目的就是通过点击修改定义导航中的 isActive 为 true 或 false
// pages/test08/test08.js
Page({
/**
* 页面的初始数据
*/
data: {
headerTabs:[
{
id:0,
name:"首页",
isActive:true
},
{
id:1,
name:"测试",
isActive:false
},
{
id:2,
name:"测试",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
},
]
},
// 自定义事件 用来接收子组件传递的数据的
handleItemChange(e) {
// 接收传递过来的参数
const { index } = e.detail;
let { headerTabs } = this.data;
headerTabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
this.setData({
headerTabs
})
}
})
结尾
如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!
如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号
在这里的我们素不相识,却都在为了自己的梦而努力 ❤一个坚持推送原创开发技术文章的公众号:理想二旬不止