微信小程序详解
文章目录
- 介绍
- 项目目录结构
- 小程序与普通网页开发的区别
- 小程序代码构成
-
- JSON的格式:
- WXML 模板
- WXSS 样式
- JS 逻辑交互
- 程序与页面
- 小程序的配置
-
- 全局配置 app.json
-
- entryPagePath
- pages
- window
- tabBar
- style
- 页面配置
- 小程序框架
-
- 场景值
- 逻辑层 App Service
- 视图层 View
-
- WXML语法
-
- 数据绑定
- 列表渲染
- 条件渲染
- 模板和引用
-
- 定义模板和使用模板
- 模板的作用域
- WXSS
-
- 尺寸单位
- 样式导入
- 内联样式
- 选择器
- 全局样式与局部样式
- WXS语法
-
- WXS 模块
-
- `<wxs>` 标签
- .wxs 文件
- require函数
- 注意事项
- JS模块和WXS模块的区分
- 事件系统
-
- 什么是事件
- 事件的使用方式
- 事件详解
- 普通事件绑定
- 绑定并阻止事件冒泡
- 互斥事件绑定
- 事件的捕获阶段
- 事件对象
- 事件传参
-
- 使用dataset
- 使用mark
- dataset和mark的区别
- DOM、Vue、小程序绑定事件的区别
- 页面路由
-
- 页面栈
- 路由方式
- 注意事项
- 路由传参
- 自定义组件
-
- 创建自定义组件
- 使用自定义组件
- 注意事项
- slot
-
- 匿名插槽
- 具名插槽
- 组件样式
-
- 样式的注意点
- 组件样式隔离
- 外部样式类
- 引用页面或父组件的样式
- 组件间通信与事件
-
- 父传子 properties
- 子传父 事件
- 获取组件实例
- 全局共享的数据
- ==组件的生命周期函数==
-
- 组件生命周期
- 组件所在页面的生命周期
- behaviors
- 组件间关系
- 数据监听器
-
- observers
- 注意事项
- 扩展
-
- 小程序的计算属性
- mobx辅助库(小程序的状态管理)
介绍
微信小程序是运行在微信APP内部的子程序,它使用前端技术进行开发,但是又不同于手机APP中的内嵌网页webView。
APP内嵌页面的本质还是一个网页,在APP内部启动了一个简单的浏览器(webView)去打开并显示这个网页。而微信小程序是使用前端技术写成的程序代码,微信在启动小程序时,使用的是原生手机API去渲染小程序。所以小程序相比内嵌页面能够使用更多的手机API(功能)。例如蓝牙功能、地图功能、通讯录等。
网页开发者在开发网页的时候,只需要使用到浏览器,并且搭配上一些辅助工具或者编辑器即可。小程序的开发则有所不同,需要经过申请小程序帐号、安装小程序开发者工具、配置项目等等过程方可完成
开发指南:https://developers.weixin.qq.com/miniprogram/dev/framework/
项目目录结构
一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:
文件 | 必需 | 作用 |
---|---|---|
app.js | 是 | 小程序逻辑 |
app.json | 是 | 小程序公共配置 |
app.wxss | 否 | 小程序公共样式表 |
一个小程序页面由四个文件组成,分别是:
文件类型 | 必需 | 作用 |
---|---|---|
js | 是 | 页面逻辑 |
wxml | 是 | 页面结构 |
json | 否 | 页面配置 |
wxss | 否 | 页面样式表 |
注意:为了方便开发者减少配置项,描述页面的四个文件必须具有相同的路径与文件名。
-
project.config.json
开发工具的配置文件。 -
sitemap.json
文件用来配置小程序及其页面是否允许被微信索引。 -
app.js
应用程序入口文件,在这个文件中注册小程序实例对象(app)。 -
app.wxss
全局样式文件,定义的样式为全局样式,对所有页面都生效的样式文件。 -
app.json
是当前小程序的全局配置文件,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。例如在这个文件中设置页面背景色为红色,那么项目中所有的页面背景色都是红色。 -
pages
是页面目录,其中存放项目的所有页面。每个页面是一个文件夹。文件夹中包含4个文件,分别是 -
xxx.wxml
WeiXin Markup Language 微信标记语言。它和html类似,都用于编写页面内容,但是不能使用DOM标签,只能使用微信组件或自定义组件。 -
xxx.wxss
WeiXin Style Sheets 微信样式表,和css类似,它兼容大部分css3中的样式,并且增加了一些额外功能。作用域当前页面的样式文件。定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。 -
xxx.js
页面逻辑文件,在这个js文件中,编写本页面的逻辑和数据。 -
xxx.json
本页面的配置文件,页面中配置项会覆盖 app.json 的 window 中相同的配置项,在这个配置文件中,可以设置本页面的导航条,主体颜色,导入自定义组件等。
小程序与普通网页开发的区别
- 网页开发:
- 可以使用DOM API 和 BOM API
- 网页开发者需要面对的环境是各式各样的浏览器,PC 端需要面对 IE、Chrome、QQ浏览器等,在移动端需要面对Safari、Chrome以及 iOS、Android 系统中的各式 WebView 。
- 小程序开发:
- 不能使用DOM API 和 BOM API
- 而小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具
小程序代码构成
JSON的格式:
JSON的值只能是以下几种数据格式,其他任何格式都会触发报错,例如 JavaScript 中的 undefined。
- 数字,包含浮点数和整数
- 字符串,需要包裹在双引号中
- Bool值,true 或者 false
- 数组,需要包裹在方括号中 []
- 对象,需要包裹在大括号中 {}
- Null
WXML 模板
WXML
和 HTML
非常相似,WXML
由标签、属性等等构成。但是也有很多不一样的地方:
- 标签名字有点不一样
HTML
的的标签是div
,p
,span
WXML
用的标签是view
,button
,text
等等,这些标签就是小程序给开发者包装好的基本能力,我们还提供了地图、视频、音频等等组件能力。
- 多了一些
wx:if
这样的属性以及 {{ }} 这样的表达式
WXSS 样式
WXSS
具有 CSS
大部分的特性,小程序在 WXSS
也做了一些扩充和修改。
- 新增了尺寸单位。WXSS 在底层支持新的尺寸单位
rpx
; - 提供了全局的样式和局部样式。你可以写一个
app.wxss
作为全局样式,会作用于当前小程序的所有页面,局部页面样式page.wxss
仅对当前页面生效。 - 此外
WXSS
仅支持部分CSS
选择器
JS 逻辑交互
和用户做交互:响应用户的点击、获取用户的位置等等。
<button bindtap="tapClick">点击改变message</button>
Page({data: {message: 'Hello World',},tapClick(){this.setData({message:'小程序',});}
})
程序与页面
微信客户端在打开小程序之前,会把整个小程序的代码包下载到本地。
(开发小程序过程中启动小程序)紧接着通过 app.json
的 pages
字段就可以知道你当前小程序的所有页面路径:
{"pages":["pages/index/index","pages/user/user"]
}
写在 pages
字段的第一个页面就是这个小程序的首页(打开小程序看到的第一个页面)。
小程序的配置
全局配置 app.json
小程序根目录下的 app.json
文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
全局配置:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html
entryPagePath
指定小程序的默认启动路径(首页),常见情景是从微信聊天列表页下拉启动、小程序列表启动等。如果不填,将默认为 pages
列表的第一项。不支持带页面路径参数。
pages
用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息。文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json
, .js
, .wxml
, .wxss
四个文件进行处理。
-
未指定
entryPagePath
时,数组的第一项代表小程序的初始页面(首页)。 -
创建新的页面,会自动在pages字段中添加页面的路径。
"pages": ["pages/index/index","pages/user/user"],
window
用于设置小程序的状态栏、导航条、标题、窗口背景色。
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
navigationBarBackgroundColor | HexColor | #000000 | 导航栏背景颜色,如 #000000 |
navigationBarTextStyle | string | white | 导航栏标题颜色,仅支持 black / white |
navigationBarTitleText | string | 导航栏标题文字内容 | |
navigationStyle | string | default | 导航栏样式,仅支持以下值: default 默认样式 custom 自定义导航栏,只保留右上角胶囊按钮。参见注 2。 |
backgroundColor | HexColor | #ffffff | 窗口的背景色 |
backgroundTextStyle | string | dark | 下拉 loading 的样式,仅支持 dark / light |
backgroundColorTop | string | #ffffff | 顶部窗口的背景色,仅 iOS 支持 |
backgroundColorBottom | string | #ffffff | 底部窗口的背景色,仅 iOS 支持 |
enablePullDownRefresh | boolean | false | 是否开启全局的下拉刷新。 详见 Page.onPullDownRefresh |
onReachBottomDistance | number | 50 | 页面上拉触底事件触发时距页面底部距离,单位为 px。 详见 Page.onReachBottom |
pageOrientation | string | portrait | 屏幕旋转设置,支持 auto / portrait / landscape 详见 响应显示区域变化 |
restartStrategy | string | homePage | 重新启动策略配置 |
initialRenderingCache | string | 页面初始渲染缓存配置,支持 static / dynamic |
|
visualEffectInBackground | string | none | 切入系统后台时,隐藏页面内容,保护用户隐私。支持 hidden / none |
handleWebviewPreload | string | static | 控制预加载下个页面的时机。支持 static / manual / auto |
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,见详情 |
其中 list 接受一个数组,只能配置最少 2 个、最多 5 个 tab。tab 按数组的顺序排序,每个项都是一个对象,其属性值如下:
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
pagePath | string | 是 | 页面路径,必须在 pages 中先定义 |
text | string | 是 | tab 上按钮文字 |
iconPath | string | 否 | 图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。 当 position 为 top 时,不显示 icon。 |
selectedIconPath | string | 否 | 选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。 当 position 为 top 时,不显示 icon。 |
style
微信客户端 7.0 开始,UI 界面进行了大改版。小程序也进行了基础组件的样式升级。app.json 中配置 "style": "v2"
可表明启用新版的组件样式。
本次改动涉及的组件有 button icon radio checkbox switch slider
。可前往小程序示例进行体验
页面配置
每一个小程序页面也可以使用同名 .json
文件(比如:index页面下的 index.json)来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.json
的 window
中相同的配置项。
app.json 中的部分配置,也支持对单个页面进行配置,可以在页面对应的 .json
文件来对本页面的表现进行配置。
页面中配置项在当前页面会覆盖 app.json
中相同的配置项(样式相关的配置项属于 app.json
中的 window
属性,但这里不需要额外指定 window
字段。
页面配置:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
navigationBarBackgroundColor | HexColor | #000000 | 导航栏背景颜色,如 #000000 |
navigationBarTextStyle | string | white | 导航栏标题颜色,仅支持 black / white |
navigationBarTitleText | string | 导航栏标题文字内容 | |
navigationStyle | string | default | 导航栏样式,仅支持以下值: default 默认样式 custom 自定义导航栏,只保留右上角胶囊按钮。 |
backgroundColor | HexColor | #ffffff | 窗口的背景色 |
backgroundTextStyle | string | dark | 下拉 loading 的样式,仅支持 dark / light |
backgroundColorTop | string | #ffffff | 顶部窗口的背景色,仅 iOS 支持 |
backgroundColorBottom | string | #ffffff | 底部窗口的背景色,仅 iOS 支持 |
enablePullDownRefresh | boolean | false | 是否开启当前页面下拉刷新。 详见 Page.onPullDownRefresh |
onReachBottomDistance | number | 50 | 页面上拉触底事件触发时距页面底部距离,单位为px。 详见 Page.onReachBottom |
pageOrientation | string | portrait | 屏幕旋转设置,支持 auto / portrait / landscape 详见 响应显示区域变化 |
disableScroll | boolean | false | 设置为 true 则页面整体不能上下滚动。 只在页面配置中有效,无法在 app.json 中设置 |
usingComponents | Object | 否 | 页面自定义组件配置 |
initialRenderingCache | string | 页面初始渲染缓存配置,支持 static / dynamic |
|
style | string | default | 启用新版的组件样式 |
singlePage | Object | 否 | 单页模式相关配置 |
restartStrategy | string | homePage | 重新启动策略配置 |
handleWebviewPreload | string | static | 控制预加载下个页面的时机。支持 static / manual / auto |
- 注:并不是所有
app.json
中的配置都可以在页面覆盖或单独指定,仅限于本文档包含的选项。 - 注:iOS/Android 客户端 7.0.0 以下版本,
navigationStyle
只在app.json
中生效。
小程序框架
整个小程序框架系统分为两部分:逻辑层(App Service)和 视图层(View)。小程序提供了自己的视图层描述语言 WXML
和 WXSS
,以及基于 JavaScript
的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑。
场景值
场景值列表:https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html
逻辑层 App Service
小程序开发框架的逻辑层使用 JavaScript
引擎为小程序提供开发者 JavaScript
代码的运行环境以及微信小程序的特有功能。
逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。
开发者写的所有代码最终将会打包成一份 JavaScript
文件,并在小程序启动的时候运行,直到小程序销毁。这一行为类似 ServiceWorker,所以逻辑层也称之为 App Service。
在 JavaScript
的基础上,我们增加了一些功能,以方便小程序的开发:
- 增加
App
和Page
方法,进行程序注册和页面注册。 - 增加
getApp
和getCurrentPages
方法,分别用来获取App
实例和当前页面栈。 - 提供丰富的 API,如微信用户数据,扫一扫,支付等微信特有能力。
- 提供模块化能力,每个页面有独立的作用域。
注意:小程序框架的逻辑层并非运行在浏览器中,因此 JavaScript
在 web 中一些能力都无法使用,如 window
,document
等。
注册小程序
每个小程序都需要在 app.js
中调用 App
方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。
文档:https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html
用法:App(Object object)
:注册小程序。接受一个 Object
参数,其指定小程序的生命周期回调等。
App() 必须在 app.js
中调用,必须调用且只能调用一次。不然会出现无法预期的后果
小程序的生命周期函数
-
onLaunch(Object object):小程序初始化完成时触发,全局只触发一次。
-
onShow(Object object):小程序启动,或从后台进入前台显示时触发。
-
onHide():小程序从前台进入后台时触发。
小程序的生命周期函数在一个小程序中只存在app.js中。
getApp
整个小程序只有一个 App 实例,是全部页面共享的。开发者可以通过 getApp
方法获取到全局唯一的 App 实例,获取App上的数据或调用开发者注册在 App
上的函数。
用法:getApp()
获取到小程序全局唯一的 App
实例。
注意事项
- 不要在定义于
App()
内的函数中,或调用App
前调用getApp()
,使用this
就可以拿到 app 实例。 - 通过
getApp()
获取实例之后,不要私自调用生命周期函数。
App({onShow(options){// 指向app实例:小程序全局唯一的 App 实例。console.log(this);},// 添加全局共享的数据,是用户自定义的,名字可以是globalData,也可以是其他名字// 多个页面共享的数据,通过getApp()获取app实例,通过app实例获取 globalDataglobalData: {rootCount: 16,}
})
// index.jsconst app = getApp();
console.log(app.globalData.rootCount);
// find.jsconst app = getApp();
console.log(app.globalData.rootCount);
注册页面
对于小程序中的每个页面,都需要在页面对应的 js
文件中进行注册,指定页面的初始数据、生命周期回调、事件处理函数等。
使用 Page 构造器注册页面
简单的页面可以使用 Page()
进行构造。
用法:Page(Object object):注册小程序中的一个页面。接受一个 Object
类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。
页面的初始数据
- data: 页面的初始数据
页面的生命周期函数
- onLoad:生命周期回调—监听页面加载
- onShow:生命周期回调—监听页面显示
- onReady:生命周期回调—监听页面初次渲染完成
- onHide:生命周期回调—监听页面隐藏
- onUnload:生命周期回调—监听页面卸载
- onRouteDone:生命周期回调—路由动画完成时触发。
说明:
- 在每一个页面的js文件中都有页面自己的生命周期函数。
- 在生命周期函数中的this指向页面实例,所以生命周期函数不能使用箭头函数
页面事件处理函数
- onPullDownRefresh:监听用户下拉动作—-刷新页面
- 需要在app.json的window选项中或页面配置中开启enablePullDownRefresh。
- onReachBottom:页面上拉触底事件的处理函数—加载更多
- 可以在app.json的window选项中或页面配置中设置触发距离onReachBottomDistance。
- 在触发距离内滑动期间,本事件只会被触发一次。
- onPageScroll:页面滚动触发事件的处理函数
- onTabItemTap:点击 tab 时触发
- onSaveExitState:每当小程序可能被销毁之前,页面回调函数 onSaveExitState 会被调用,可以进行退出状态的保存。
组件事件处理函数
Page 中还可以定义组件事件处理函数。在渲染层的组件中加入事件绑定,当事件被触发时,就会执行 Page 中定义的事件处理函数。
- 在自定义的函数中,this也是指向页面实例,自定义函数不能使用箭头函数
<button bindtap="tapClick">点击改变message</button>
Page({data: {message: 'Hello World',},// tabClick是自定义的函数tapClick(){this.setData({message:'小程序',});this.add();// 调用自定义的函数},add(){console.log('add 函数');},
})
setData
在小程序中改变data中的数据,需要使用 setData函数,不能直接改变。
setData
函数用于将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data
的值(同步)。
用法:setData(Object data, Function callback)
参数说明
字段 | 类型 | 必填 | 描述 |
---|---|---|---|
data | Object | 是 | 这次要改变的数据 |
callback | Function | 否 | setData引起的界面更新渲染完毕后的回调函数 |
-
Object
以key: value
的形式表示,将this.data
中的key
对应的值改变成value
。 -
其中
key
可以以数据路径的形式给出,支持改变数组中的某一项或对象的某个属性,如array[2].message
,a.b.c.d
, -
key 并且不需要在 this.data 中预先定义,也可以在this.data中预先定义。
注意:
- 直接修改 this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致。
- 仅支持设置可 JSON 化的数据。
- 单次设置的数据不能超过1024kB,请尽量避免一次设置过多的数据。
- 请不要把 data 中任何一项的 value 设为
undefined
,否则这一项将不被设置并可能遗留一些潜在问题。
Page({data: {message: 'Hello World',num:1,arr:[1,2,3],obj:{name:'张三',age: 18,}},tapClick(){// 在小程序中改变data中的数据,需要使用 setData函数,不能直接改变this.datathis.setData({message:'小程序',// num: 100,num: this.data.num + 100,// arr:[3,4,5,6],// 改变整个数组'arr[1]': 66,// 支持路径的写法// obj:{name:'李四', age: 20},// 改变整个对象'obj.age': 22,// 支持路径的写法});})
使用 Component 构造器构造页面
Page
构造器适用于简单的页面。但对于复杂的页面, Page
构造器可能并不好用。
此时,可以使用 Component
构造器来构造页面。 Component
构造器的主要区别是:方法需要放在 methods: { }
里面。
-
一般的页面通常使用Page注册,一般页面功能复杂的时候,也可以使用Component注册
-
自定义组件需要使用Component注册
在页面中使用 behaviors
Page和Component注册的页面都可以引用 behaviors 。 behaviors 可以用来让多个页面有相同的数据字段和方法。
behaviors
是用于组件间代码共享的特性,类似于Vue中的 “mixins”。
每个 behavior
可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。 每个组件可以引用多个 behavior
,behavior
也可以引用其它 behavior
。
getCurrentPages()
获取当前页面栈。数组中第一个元素为首页,最后一个元素为当前页面。
注意事项
- 不要尝试修改页面栈,会导致路由以及页面状态错误。
- 不要在
App.onLaunch
的时候调用getCurrentPages()
,此时page
还没有生成。
模块化
可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports
才能对外暴露接口。
- 在小程序中使用的是CommonJS 的模块化规范
- 导出模块
module.exports = {}
- 导入模块
const xxx = require('');
- 导出模块
文件作用域
在 JavaScript 文件中声明的变量和函数只在该文件中有效;不同的文件中可以声明相同名字的变量和函数,不会互相影响。
通过全局函数 getApp
可以获取全局的应用实例,如果需要全局的数据可以在 App()
中设置。
其他
- console 向调试面板中打印日志。console 是一个全局对象,可以直接访问。
- 定时器用法不变:setTimeout、setInterval
视图层 View
框架的视图层由 WXML 与 WXSS 编写,由组件来进行展示。
将逻辑层的数据反映成视图,同时将视图层的事件发送给逻辑层。
WXML(WeiXin Markup language) 用于描述页面的结构。
WXS(WeiXin Script) 是小程序的一套脚本语言,结合 WXML
,可以构建出页面的结构。
WXSS(WeiXin Style Sheet) 用于描述页面的样式。
组件(Component)是视图的基本组成单元。
WXML语法
WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。
WXML和html类似,都用于编写页面内容,但是不能使用DOM标签,只能使用微信组件或自定义组件。
数据绑定
WXML 中的动态数据均来自对应 Page 的 data。
简单绑定:数据绑定使用 Mustache 语法(双大括号)将变量包起来。
<view>message = {{message}}</view>
文档:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/data.html
列表渲染
在组件上使用 wx:for
控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index
,数组当前项的变量名默认为 item
文档:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/list.html
条件渲染
在框架中,使用 wx:if=""
来判断是否需要渲染该代码块。
文档:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/conditional.html
模板和引用
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。
定义模板和使用模板
定义模板有两种方式:
方式一:直接在index.wxml中定义,并在index.wxml中使用,这种方式只能在当前页面中使用
- 定义模板:使用 name 属性,作为模板的名字。然后在
<template/>
内定义代码片段 - 使用模板:使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:
<!-- 定义模板 -->
<template name="myTem">{{name}}-{{age}}
</template><!-- 使用模板 -->
<template is="myTem" data="{{name:'张三',age: 12}}"></template>
方式二:使用外部.wxml文件模板
-
使用import导入模板,可以传递数据
-
引用模板使用import
-
使用独立的模板文件,可以在多个页面中使用import导入,复用模板,并且还可以给模板传递数据
-
<!-- header.wxml -->
<template name="header"><view class="header">{{title}}</view>
</template><!-- index.wxml -->
<import src="../../template/header.wxml"></import>
<template is="header" data="{{title:'首页'}}"></template>
- 使用include引用模板,不能传递数据
- include相当于是把模板中的内容赋值到页面中无法传递数据
- include会忽略模板文件中的
<template/> <wxs/>
<!-- footer.wxml -->
<view class="footer">底部</view><!-- index.wxml -->
<include src="../../template/footer.wxml"></include>
模板的作用域
模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的 <wxs />
模块。
WXSS
WXSS (WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。
WXSS 用来决定 WXML 的组件应该怎么显示。
为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。
与 CSS 相比,WXSS 扩展的特性有:
- 尺寸单位
- 样式导入
尺寸单位
- 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 作为视觉稿的标准。
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况
写项目以iPhone6为参考,尺寸:1px = 2rpx,换算尺寸比较简单。
样式导入
使用@import
语句可以导入外联样式表,@import
后跟需要导入的外联样式表的相对路径,用;
表示语句结束。
- 一般使用 @import导入一些外部的样式文件:字体图标库,样式的初始化文件
/** common.wxss **/
.small-p {padding:5px;
}/** app.wxss **/
@import "common.wxss";
.middle-p {padding:15px;
}
内联样式
框架组件上支持使用 style、class 属性来控制组件的样式。
- style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。
<view style="color:{{color}};" />
- class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上
.
,样式类名之间用空格分隔。
<view class="normal_view box" /><view class="box {{className}}">属性绑定,动态类名</view><view bindtap="itemClick" data-index="{{index}}" class="{{selectedIndex==index ? 'active' : ''}}"wx:for="{{arr}}"wx:key="index"
>属性绑定,动态类名 {{index}}</view>
选择器
目前支持的选择器有:
选择器 | 样例 | 样例描述 |
---|---|---|
.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 组件前边插入内容 |
全局样式与局部样式
定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。
WXS语法
WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML
,可以构建出页面的结构。
WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。
WXS 模块
WXS 代码可以编写在 wxml 文件中的 <wxs>
标签内,或以 .wxs
为后缀名的文件内。
- 每一个
.wxs
文件是一个单独的模块。 - 在 wxml 文件中的
<wxs>
标签也是一个单独的模块。
模块作用域:
每个wxs模块都有自己独立的作用域。在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见。
wxs模块导出数据:
一个模块要想对外暴露其内部的私有变量与函数,只能通过 module.exports
实现。
<wxs>
标签
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
module | String | 当前 <wxs> 标签的模块名。必填字段。 |
|
src | String | 引用 .wxs 文件的相对路径。仅当本标签为单闭合标签或标签的内容为空时有效。 |
<wxs>
标签的 module 属性是当前 <wxs>
标签的模块名。在单个 wxml 文件内,建议其值唯一。有重复模块名则按照先后顺序覆盖(后者覆盖前者)。不同文件之间的 wxs 模块名不会相互覆盖。
module 属性值的命名必须符合下面两个规则:
- 首字符必须是:字母(a-zA-Z),下划线(_)
- 剩余字符可以是:字母(a-zA-Z),下划线(_), 数字(0-9)
<wxs>
标签的 src 属性可以用来引用其他的 wxs
文件模块。引用的时候,要注意如下几点:
- 只能引用
.wxs
文件模块,且必须使用相对路径。 wxs
模块均为单例,wxs
模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个wxs
模块对象。- 如果一个
wxs
模块在定义之后,一直没有被引用,则该模块不会被解析与运行。
<wxs module="tool">var count = 1;var add = function(a, b){return a + b;}module.exports = {count: count,add: add,}
</wxs><view>{{tool.count}} -- {{tool.add(100, 200)}}
</view>
.wxs 文件
在一个.wxs文件可以定义一些功能代码,比如:保留两位小数等,在每一个页面中导入wxs文件,可以重复使用。
// utils/format.wxs// wxs 模块
var numFormat = function(vlaue){return Number(vlaue).toFixed(2);
}module.exports = {numFormat:numFormat,
}
<!-- index.wxml -->
<wxs src="../../utils/format.wxs" module="format"></wxs>
<view>{{format.numFormat(num)}}</view>
require函数
在.wxs
模块中引用其他 wxs
文件模块,可以使用 require
函数。引用的时候,要注意如下几点:
- 只能引用
.wxs
文件模块,且必须使用相对路径。 wxs
模块均为单例,wxs
模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个wxs
模块对象。- 如果一个
wxs
模块在定义之后,一直没有被引用,则该模块不会被解析与运行。
// utils/a.wxsvar count = 1;
var add = function(a, b){return a + b;
}
module.exports = {count: count,add: add,
}
// utils/format.wxs// 在一个wxs模块中引入其他的wxs模块
var a = require('./a.wxs');console.log(a.count);
console.log(a.add(200,300))// wxs 模块
var numFormat = function(vlaue){return Number(vlaue).toFixed(2);
}module.exports = {numFormat:numFormat,
}
注意事项
<wxs>
模块只能在定义模块的 WXML 文件中被访问到。使用<include>
或<import>
时,<wxs>
模块不会被引入到对应的 WXML 文件中。<template>
标签中,只能使用定义该<template>
的 WXML 文件中定义的<wxs>
模块。
JS模块和WXS模块的区分
在小程序中使用.js文件创建模块
-
可以使用ES6模块化
- 导出方式模块 export 或者 export default
- 导入模块使用 import from
-
commonJS模块化
- 导出模块 module.exports
- 导入模块 require
-
不管是使用ES6模块化,还是使用commonJS模块化,都可以在模块中使用ES6的语法
-
JS模块是在js文件中导入使用
在小程序中使用wxs模块
- 导出模块只能使用 module.exports
- 导入.wxs文件模块使用
<wxs>
配合src和module属性 - 在wxs模块中不能使用ES6语法
- wxs模块可以在wxs文件导入,也可以在wxml文件中导入使用
使用wxs模块在wxml文件中使用,可以实现在wxml 文件中调方法,进行数据处理。
除了WXS模块和JS模块不一样,在WXS模块中使用变量、注释、运算符、语句、数据类型、基础类库与ES5基本保持一致。
- 变量
- 注释
- 运算符
- 语句
- 数据类型
- 基础类库
事件系统
什么是事件
- 事件是视图层到逻辑层的通讯方式。
- 事件可以将用户的行为反馈到逻辑层进行处理。
- 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
- 事件对象可以携带额外信息,如 id, dataset, touches。
事件的使用方式
- 在组件中绑定一个事件处理函数。
如bindtap
,当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数。
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
- 在相应的Page定义中写上相应的事件处理函数,参数是event。
Page({tapName(event) {console.log(event)}
})
事件详解
事件分类:事件分为冒泡事件和非冒泡事件:
- 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
- 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
WXML的冒泡事件列表:
类型 | 触发条件 |
---|---|
touchstart | 手指触摸动作开始 |
touchmove | 手指触摸后移动 |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 |
touchend | 手指触摸动作结束 |
tap | 手指触摸后马上离开 |
longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 |
longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 |
animationstart | 会在一个 WXSS animation 动画开始时触发 |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 |
animationend | 会在一个 WXSS animation 动画完成时触发 |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 |
注:除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如 form 的submit
事件,input 的input
事件,scroll-view 的scroll
事件,(详见各个组件)
普通事件绑定
普通事件使用 bind绑定,事件会向上冒泡
<view bindtap="handleTap">Click here!</view>
自基础库版本 1.5.0 起,在大多数组件和自定义组件中, bind
后可以紧跟一个冒号,其含义不变,如 bind:tap
。基础库版本 2.8.1 起,在所有组件中开始提供这个支持。
<view bind:tap="handleTap">Click here!</view>
绑定并阻止事件冒泡
用 catch
来绑定事件。与 bind
不同, catch
会阻止事件向上冒泡。
在小程序中没有ev.stopPropagation();方法,想要阻止事件冒泡,使用catch绑定事件。
例如在下边这个例子中,点击 inner view 会先后调用handleTap3
和handleTap2
(因为tap事件会冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父节点传递),点击 middle view 会触发handleTap2
,点击 outer view 会触发handleTap1
。
<view id="outer" bindtap="handleTap1">outer view<view id="middle" catchtap="handleTap2">middle view<view id="inner" bindtap="handleTap3">inner view</view></view>
</view>
互斥事件绑定
用 mut-bind
来绑定事件。一个 mut-bind
触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind
绑定函数不会被触发,但 bind
绑定函数和 catch
绑定函数依旧会被触发。
换而言之,所有 mut-bind
是“互斥”的,只会有其中一个绑定函数被触发。同时,它完全不影响 bind
和 catch
的绑定效果。
例如在下边这个例子中,点击 inner view 会先后调用 handleTap3
和 handleTap2
,点击 middle view 会调用 handleTap2
和 handleTap1
。
<view id="outer" mut-bind:tap="handleTap1">outer view<view id="middle" bindtap="handleTap2">middle view<view id="inner" mut-bind:tap="handleTap3">inner view</view></view>
</view>
事件的捕获阶段
触摸类事件支持捕获阶段。捕获阶段位于冒泡阶段之前,且在捕获阶段中,事件到达节点的顺序与冒泡阶段恰好相反。需要在捕获阶段监听事件时,可以采用capture-bind
、capture-catch
关键字,后者将中断捕获阶段和取消冒泡阶段。
在下面的代码中,点击 inner view 会先后调用handleTap2
、handleTap4
、handleTap3
、handleTap1
。
<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">outer view<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">inner view</view>
</view>
如果将上面代码中的第一个capture-bind
改为capture-catch
,将只触发handleTap2
。
<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">outer view<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">inner view</view>
</view>
事件对象
文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#%E4%BA%8B%E4%BB%B6%E5%AF%B9%E8%B1%A1
事件传参
把数据从渲染层传递到逻辑层
使用dataset
在组件节点中可以附加一些自定义数据。这样,在事件中可以获取这些自定义的节点数据,用于事件的逻辑处理。
在 WXML 中,这些自定义数据以 data-
开头,多个单词由连字符 -
连接。这种写法中,连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符。如:
data-element-type
,最终会呈现为event.currentTarget.dataset.elementType
;data-elementType
,最终会呈现为event.currentTarget.dataset.elementtype
。
示例:
<view data-index="66" data-alpha-beta="1" data-alphaBeta="2" bindtap="bindViewTap"> DataSet Test </view>
Page({bindViewTap:function(event){event.currentTarget.dataset.alphaBeta === 1 // - 会转为驼峰写法event.currentTarget.dataset.alphabeta === 2 // 大写会转为小写event.currentTarget.dataset.index === 66 }
})
使用mark
在基础库版本 2.7.1 以上,可以使用 mark
来识别具体触发事件的 target 节点。此外, mark
还可以用于承载一些自定义数据(类似于 dataset
)。
当事件触发时,事件冒泡路径上所有的 mark
会被合并,并返回给事件回调函数。(即使事件不是冒泡事件,也会 mark
。)
<view mark:myMark="last" bindtap="bindViewTap"><button mark:anotherMark="leaf" bindtap="bindButtonTap">按钮</button>
</view>
Page({bindViewTap: function(e) {e.mark.myMark === "last" // truee.mark.anotherMark === "leaf" // true}
})
dataset和mark的区别
mark
和 dataset
很相似,主要区别在于: mark
会包含从触发事件的节点到根节点上所有的 mark:
属性值;而 dataset
仅包含一个节点的 data-
属性值。
细节注意事项:
- 如果存在同名的
mark
,父节点的mark
会被子节点覆盖。 - 在自定义组件中接收事件时,
mark
不包含自定义组件外的节点的mark
。 - 不同于
dataset
,节点的mark
不会做连字符和大小写转换。
其他内容见文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html
DOM、Vue、小程序绑定事件的区别
1、DOM中:<div onclick="fn()"></div >
- DOM中绑定事件需要添加圆括号,
- 传递参数:默认没有参数,使用圆括号可以传递参数,一个或者多个
- 传递 event 表示事件对象
- 传递 this 表示绑定事件的标签
- 传递 任意类型的值:数字、字符串、布尔等
2、Vue中:<div @click="fn()"></div >
- vue中绑定事件可以添加圆括号,也可以不加
- 传递参数:默认传递事件对象$event,使用圆括号以传递参数,一个或者多个
- 传递 $event 表示事件对象
- 传递 任意类型的值:数字、字符串、布尔等
3、小程序中:<view bindtap="boxTap"></view>
- 小程序中绑定事件不能添加圆括号
- 传递参数:默认传递的是事件对象,不能使用圆括号传参
- 使用dataset传参
- 使用mark传参
页面路由
在小程序中所有页面的路由全部由框架进行管理。
页面栈
框架以栈的形式维护了当前的所有页面。 当发生路由切换的时候,页面栈的表现如下:
- 开发者可以使用
getCurrentPages()
函数获取当前页面栈。
open-type | 路由方式 | 页面栈表现 |
---|---|---|
小程序打开的第一个页面 | 初始化 | 新页面入栈 |
navigate | 打开新页面 | 新页面入栈 |
redirect | 页面重定向 | 当前页面出栈,新页面入栈 |
navigateBack | 页面返回 | 页面不断出栈,直到目标返回页 |
switchTab | Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 |
reLaunch | 重加载 | 页面全部出栈,只留下新的页面 |
路由方式
路由方式有两种:
-
方式一:使用 navigator 组件
- https://developers.weixin.qq.com/miniprogram/dev/component/navigator.html
-
方式二:使用路由的API,比如 wx.navigateTo
- https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.switchTab.html
对于路由的触发方式:
路由方式 | 触发时机 |
---|---|
初始化 | 小程序打开的第一个页面 |
打开新页面 | 调用 API wx.navigateTo 使用组件 <navigator open-type="navigateTo"/> |
页面重定向 | 调用 API wx.redirectTo 使用组件 <navigator open-type="redirectTo"/> |
页面返回 | 调用 API wx.navigateBack 使用组件 <navigator open-type="navigateBack"> 用户按左上角返回按钮 |
Tab 切换 | 调用 API wx.switchTab 使用组件 <navigator open-type="switchTab"/> 用户切换 Tab |
重启动 | 调用 API wx.reLaunch 使用组件 <navigator open-type="reLaunch"/> |
注意事项
navigateTo
,redirectTo
只能打开非 tabBar 页面。switchTab
只能打开 tabBar 页面。reLaunch
可以打开任意页面。- 页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
- 调用页面路由带的参数可以在目标页面的
onLoad
中获取。
路由传参
路由传参使用 ?和& 在路页面路径后添加参数,类似GET请求传参。
调用页面路由带的参数可以在目标页面的onLoad
中获取。
<!--index.wxml-->
<navigator url="/pages/second/second?username=张三&age=12" open-type="navigate"><button>去第二页面</button>
</navigator>
// pages/second/second.js
Page({// 获取路由传递的参数onLoad(options){console.log('options=',options); // options= {username: "张三", age: "12"}},});
自定义组件
创建自定义组件
类似于页面,一个自定义组件由 json
wxml
wxss
js
4个文件组成。要编写一个自定义组件,首先需要在自定义组建的 json
文件中进行自定义组件声明(将 component
字段设为 true
可将这一组文件设为自定义组件):
{"component": true
}
同时,还要在 wxml
文件中编写组件模板,在 wxss
文件中加入组件样式,它们的写法与页面的写法类似。
注意:在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。
在自定义组件的 js
文件中,需要使用 Component()
来注册组件,并提供组件的属性定义、内部数据和自定义方法。
组件的属性值和内部数据将被用于组件 wxml
的渲染,其中,属性值是可由组件外部传入的。
使用自定义组件
使用已注册的自定义组件前,首先要在页面(使用自定义组件的页面)的 json
文件中进行引用声明。此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径:
{"usingComponents": {"m-c":"../../components/m-c/m-c"}
}
这样,在页面的 wxml
中就可以像使用基础组件一样使用自定义组件。节点名即自定义组件的标签名,节点属性即传递给组件的属性值。
<!--index.wxml-->
<m-c></m-c>
注意事项
一些需要注意的细节:
- 因为 WXML 节点标签名只能是小写字母、中划线和下划线的组合,所以自定义组件的标签名也只能包含这些字符。推荐使用小写字母和中划线,比如:
<m-c></m-c>
- 自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用
usingComponents
字段)。 - 自定义组件和页面所在项目根目录名不能以“wx-”为前缀,否则会报错。
注意,是否在页面文件中使用 usingComponents
会使得页面的 this
对象的原型稍有差异,包括:
- 使用
usingComponents
页面的原型与不使用时不一致,即Object.getPrototypeOf(this)
结果不同。 - 使用
usingComponents
时会多一些方法,如selectComponent
。 - 出于性能考虑,使用
usingComponents
时,setData
内容不会被直接深复制,即this.setData({ field: obj })
后this.data.field === obj
。(深复制会在这个值被组件间传递时发生。)
如果页面比较复杂,新增或删除 usingComponents
定义段时建议重新测试一下。
slot
在组件模板中可以提供一个 <slot>
节点,用于承载组件引用时提供的子节点。
匿名插槽
使用单个不带name属性的<slot>
,是匿名插槽
<view class="m-c"><slot></slot>
</view>
具名插槽
默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。
Component({options: {multipleSlots: true // 在组件定义时的选项中启用多slot支持},properties: { /* ... */ },methods: { /* ... */ }
})
此时,可以在这个组件的 wxml 中使用多个 slot ,以不同的 name
来区分。
<!-- 组件模板 components/m-c/m-c.wxml-->
<view class="m-c"><view class="header"><slot name="header"></slot></view><view class="main"><slot></slot></view><view class="footer"><slot name="footer"></slot></view>
</view>
<m-c><view slot="header">页面头部</view><view >这里是插入到组件slot中的内容</view><view slot="footer">页面底部</view>
</m-c>
组件样式
样式的注意点
组件对应 wxss
文件的样式,只对组件wxml内的节点生效。编写组件样式时,需要注意以下几点:
- 组件和引用组件的页面不能使用id选择器(
#a
)、属性选择器([a]
)和标签名选择器,请改用class选择器。 - 组件和引用组件的页面中使用后代选择器(
.a .b
)在一些极端情况下会有非预期的表现,如遇,请避免使用。 - 子元素选择器(
.a>.b
)只能用于view
组件与其子节点之间,用于其他组件可能导致非预期的情况。 - 继承样式,如
font
、color
,会从组件外继承到组件内。 - 除继承样式外,
app.wxss
中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)。
#a { } /* 在组件中不能使用 */
[a] { } /* 在组件中不能使用 */
button { } /* 在组件中不能使用 */
.a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */
除此以外,组件可以指定它所在节点的默认样式,使用 :host
选择器。
组件样式隔离
默认情况下,自定义组件的样式只受到自定义组件 wxss 的影响。除非以下两种情况:
app.wxss
或页面的wxss
中使用了标签名选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件。通常情况下这是不推荐的做法。- 指定特殊的样式隔离选项
styleIsolation
。
Component({options: {styleIsolation: 'isolated'}
})
styleIsolation
选项支持以下取值:
isolated
表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);apply-shared
表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;shared
表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了apply-shared
或shared
的自定义组件。
外部样式类
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#%E7%BB%84%E4%BB%B6%E6%A0%B7%E5%BC%8F
引用页面或父组件的样式
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#%E5%BC%95%E7%94%A8%E9%A1%B5%E9%9D%A2%E6%88%96%E7%88%B6%E7%BB%84%E4%BB%B6%E7%9A%84%E6%A0%B7%E5%BC%8F
组件间通信与事件
组件间的基本通信方式有以下几种。
- WXML 数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据。
- 事件:用于子组件向父组件传递数据,可以传递任意数据。
- 如果以上两种方式不足以满足需要,父组件还可以通过
this.selectComponent
方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。
父传子 properties
父组件(页面)向子组件传递数据,传递数字、字符串、布尔、数组、对象类型的数据。
<m-c count="{{num}}"></m-c>
Component({// 组件的属性列表,类似vue的props,接收页面向组件的传值(父组件向子组件传值)// properties中定义的属性会自动成为data的属性properties: {// count: Number,// 指定count的类型count:{// type: Number,// type指定为数字类型,type和vue的type不同,这里不仅仅是类型验证,为了规定接收的数据类型,不管传递的是什么类型的数据,最终都会转化为数字类型optionalTypes:[Number, String, Boolean],// 指定多个类型value: 6,// 默认值}},
})
子传父 事件
子组件使用 triggerEvent
发射自定义事件,指定事件名、detail对象和事件选项:
// components/m-c/m-c.js
Component({data: {m: 12,},methods: {btnClick: function(){// 发射自定义事件,事件名为 sendMthis.triggerEvent('sendM', {m:this.data.m}); }}
})
<!--index.wxml-->
<!-- 当自定义组件触发(发射)“sendM”事件时,调用“receiveM”方法 --><!-- 监听send有两种写法 -->
<m-c bindsendM="receiveM" />
<!-- 或者可以写成 -->
<m-c bind:sendM="receiveM" />
// index.jsPage({data: {num: 1,},receiveM(ev){console.log('监听到sendM事件', ev.detail);this.setData({num: ev.detail.m});}
})
获取组件实例
可在父组件里调用 this.selectComponent
,获取子组件的实例对象。
调用时需要传入一个匹配选择器 selector
,如:this.selectComponent(".my-component")
。
获取子组件实例分两种情况:
情况一:在自定义组件中不使用内置的 behavior:
wx://component-export
// index.js
// mc 是子组件实例
const mc = this.selectComponent('.m-c');
console.log(mc.data);
情况一:在自定义组件中使用内置的 behavior:
wx://component-export
// // components/m-c/m-c.js
Component({behaviors: ['wx://component-export'],// 导出组件的数据export() {return { mx: this.data.m,value: '导出的数据'}}})
// index.js
// mc 不是子组件实例,是导出的对象 { mx: 12,value: '导出的数据'}
const mc = this.selectComponent('.m-c');
console.log(mc);
全局共享的数据
多个页面共享的数据,可以存放在app.js的globalData属性中,在页面或者组件中使用 const app = getApp()
获取。
组件的生命周期函数
组件生命周期
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。
其中,最重要的生命周期是 created
attached
detached
,包含一个组件实例生命流程的最主要时间点。
- 组件实例刚刚被创建好时,
created
生命周期被触发。此时,组件数据this.data
就是在Component
构造器中定义的数据data
。 此时还不能调用setData
。 通常情况下,这个生命周期只应该用于给组件this
添加一些自定义属性字段。 - 在组件完全初始化完毕、进入页面节点树后,
attached
生命周期被触发。此时,this.data
已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。 - 在组件离开页面节点树后,
detached
生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则detached
会被触发。
定义生命周期方法:
-
生命周期方法可以直接定义在
Component
构造器的第一级参数中。 -
组件的的生命周期也可以在
lifetimes
字段内进行声明(这是推荐的方式,其优先级最高)。
组件的生命周期如下表所示。
生命周期 | 参数 | 描述 |
---|---|---|
created | 无 | 在组件实例刚刚被创建时执行 |
attached | 无 | 在组件实例进入页面节点树时执行 |
ready | 无 | 在组件在视图层布局完成后执行 |
moved | 无 | 在组件实例被移动到节点树另一个位置时执行 |
detached | 无 | 在组件实例被从页面节点树移除时执行 |
组件所在页面的生命周期
还有一些特殊的生命周期,它们并非与组件有很强的关联,但有时组件需要获知,以便组件内部处理。这样的生命周期称为“组件所在页面的生命周期”,在 pageLifetimes
定义段中定义。其中可用的生命周期包括:
生命周期 | 参数 | 描述 |
---|---|---|
show | 无 | 组件所在的页面被展示时执行 |
hide | 无 | 组件所在的页面被隐藏时执行 |
resize | Object Size |
组件所在的页面尺寸变化时执行 |
behaviors
behaviors
是用于组件间代码共享的特性,类似于一些编程语言中的 “mixins” 或 “traits”。
每个 behavior
可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。 每个组件可以引用多个 behavior
,behavior
也可以引用其它 behavior
。
同名字段的覆盖和组合规则:
组件和它引用的 behavior
中可以包含同名的字段,对这些字段的处理方法如下:
- 如果有同名的属性 (properties) 或方法 (methods):
- 若组件本身有这个属性或方法,则组件的属性或方法会覆盖
behavior
中的同名属性或方法; - 若组件本身无这个属性或方法,则在组件的
behaviors
字段中定义靠后的behavior
的属性或方法会覆盖靠前的同名属性或方法; - 在 2 的基础上,若存在嵌套引用
behavior
的情况,则规则为:引用者 behavior
覆盖被引用的 behavior
中的同名属性或方法。
- 若组件本身有这个属性或方法,则组件的属性或方法会覆盖
- 如果有同名的数据字段 (data):
- 若同名的数据字段都是对象类型,会进行对象合并;
- 其余情况会进行数据覆盖,覆盖规则为:
引用者 behavior
>被引用的 behavior
、靠后的 behavior
>靠前的 behavior
。(优先级高的覆盖优先级低的,最大的为优先级最高)
- 生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用:
- 对于不同的生命周期函数之间,遵循组件生命周期函数的执行顺序;
- 对于同种生命周期函数,遵循如下规则:
behavior
优先于组件执行;被引用的 behavior
优先于引用者 behavior
执行;靠前的 behavior
优先于靠后的 behavior
执行;
- 如果同一个
behavior
被一个组件多次引用,它定义的生命周期函数只会被执行一次
组件间关系
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/relations.html
数据监听器
observers
数据监听器可以用于监听和响应任何属性和数据字段的变化。
- 可以监听properties定义的属性
- 可以监听data定义的属性
Component({properties: {count: Number,// 指定count的类型},data: {message:'自定义组件mc',m: 12,arr:[1,2,3],obj:{name:'张三', age:12, weight: 100},},observers: {// 监听单个属性count(newVal){},// 监听多个属性'count, m'(newVal){},// 改变arr 或者 arr[1] 都会触发'arr[1]'(){},// 改变 obj 或者 obj.name 都会触发'obj.name'(){},// 监听所有子数据字段的变化,可以使用通配符 **'obj.**'(){},// 仅使用通配符 ** 可以监听全部属性'**'(){}}
})
注意事项
- 数据监听器监听的是 setData 涉及到的数据字段,即使这些数据字段的值没有发生变化,数据监听器依然会被触发。
- 如果在数据监听器函数中使用 setData 设置本身监听的数据字段,可能会导致死循环,需要特别留意。
- 数据监听器和属性的 observer 相比,数据监听器更强大且通常具有更好的性能。
扩展
小程序的计算属性
https://github.com/wechat-miniprogram/computed
mobx辅助库(小程序的状态管理)
https://github.com/wechat-miniprogram/mobx-miniprogram-bindings