也是黑马教程中学习的视频
入门
创建小程序
打开网站 https://mp.weixin.qq.com/进行注册。
获取AppID;
打开开发者工具:
-
代理方式推荐改为不适用任何代理,勾选后直连网络 。 -
选择新建项目,填写项目名称、目录和AppID,后端服务选择不适用云服务。 -
点击编译后,可以在左侧查看运行效果;也可以点击预览,然后用手机微信扫码查看
开发者文档可以点击帮助中开发者文档;设置中进行通用设置;
结构
pages:存放所有小程序的页面
utils: 存放工具性质的模块
app.js: 小程序项目的入口文件
app.json: 小程序项目的全局配置文件
app.wxss: 小程序项目的全局样式
project.config.json: 项目的配置文件
sitemap.json:用来配置小程序及其页面是否允许被微信索引
建议每个页面单独放在一个文件夹中
js文件:页面的脚本文件,存放页面的数据、事件处理函数等
json文件:当前页面的配置文件,配置窗口的外观、表现等
wxml文件:页面的模板结构文件
wxss文件:当前页面的样式表文件
JSON配置文件
项目根目录中的app.json
? 小程序的所有页面路径、窗口外观、界面表现、底部tab等。
? pages:所有页面的路径。
? window:全局定义所有页面的背景色、文字等。
? style:所使用的样式,v2是其中的一个较新版本
? sitemapLocation:指明sitemap.json的位置。
**项目根目录中的project.config.json **
? 小程序的开发工具 个性化设置
? setting:和编译相关的配置(和详情中的本地设置一致)
? projectname:项目名称(不等于小程序名称,小程序的名称在详情中查看)
? appid:小程序的账号ID
?
**项目根目录中的sitemap.json **
? 配置小程序页面是否允许微信索引
? action:allow,允许索引;disallow,不允许索引(警告修改setting中checkSitMap为false可不再弹出)
每个页面文件夹中的json
? 本页面中的窗口外观设置,覆盖全局配置(项目根目录中的app.json)。
? 配置内容同项目根目录中的app.json的window中的字段。
新建小程序页面
? app.json中 pages 中新增页面的存放路径 即可,工具自动创建对应页面。
修改小程序项目首页
? 调整 app.json 中 pages 数组中页面路径的先后顺序即可,默认把排在第一位的页面当做首页进行渲染。
wxml
? 类似于html,区别
- 标签名称不同
- html(div,span,img,a)
- wxml(view, text, image, navigator)
- 属性节点不同
- <a href="#">连接</a>
- <navigator url="/page/home/home"></navigator>
- 类似于vue中的模板语法
- 数据绑定
- 列表渲染
- 条件渲染
wxss
? 类似于css,区别
- 新增rpx尺寸单位
- css需要手动进行像素单位换算
- wxss在低层支持rpx,在不同大小屏幕上会自动进行换算
- 提供了全局的样式和局部样式
- 如app.wxss和 页面中的 wxss
- wxss仅支持部分css选择器
- .class 和 #id
- element
- 并集选择器、后代选择器
- ::after 和 ::before 等伪类选择器
js逻辑交互
- app.js
- 小程序入口,调用app()函数启动整个小程序
- 页面的js
- 页面的入口文件,通过调用Page()函数来创建并运行界面
- 普通的js
- 普通的功能模块文件,用来封装公共函数和属性
宿主环境
概念
宿主环境是指程序运行所必须的依赖环境。例如:android和iOS是两个不同的宿主环境。
手机微信是小程序的宿主环境,基于微信完成功能。
小程序宿主环境包含的内容包括:
- 通信模型
- 运行机制
- 组件
- API
通信模型
通信主体
- 渲染层:wxml模板和wxss样式工作在渲染层
- 逻辑层:js脚本工作在逻辑层
通信模型分为两部分:
- 渲染层和逻辑层之间的通信:由微信客户端进行转发
- 逻辑层和第三方服务器之间的通信: 由微信客户端进行转发
运行机制
小程序启动过程
- 将小程序的代码包下载到本地
- 解析app.json全局配置文件
- 执行app.js小程序入口文件,调用App()创建小程序实例
- 渲染小程序首页
- 启动完成
页面渲染过程
- 加载解析页面的json配置文件
- 加载页面的wxml模板和wxss样式
- 执行页面的js文件,调用page()创建页面实例
- 渲染完成
组件
组件由宿主环境提供,共分为九类
-
视图容器
- view:视图区域组件,类似于div,实现页面布局效果
- scroll-view: 可滚动的视图区域;实现滚动列表的效果
- swiper和swiper-item:轮播图容器组件,和轮播图item组件
-
基础内容
- text:文本组件,类似于span
- rich-text:富文本
-
表单组件 -
导航组件 -
媒体组件 -
map地图组件 -
canvas画布组件 -
开放能力 -
无障碍访问
view
横向布局,类名使用container貌似有问题
<view class="container1">
<view>A</view>
<view>b</view>
<view>c</view>
</view>
.container1 view{
width:50px;
height:50px;
text-align:center;
line-height:50px;
}
.container1 view:nth-child(1){
background-color:lightgreen;
}
.container1 view:nth-child(2){
background-color:lightskyblue;
}
.container1 view:nth-child(3){
background-color:lightcoral;
}
.container1{
display: flex;
justify-content: space-around;
}
scroll-view
实现纵向滚动效果,横向滚动使用scroll-x
<scroll-view class="container1" scroll-y>
<view>A</view>
<view>b</view>
<view>c</view>
</scroll-view>
.container1 view{
width:100px;
height:100px;
text-align:center;
line-height:100px;
}
.container1 view:nth-child(1){
background-color:lightgreen;
}
.container1 view:nth-child(2){
background-color:lightskyblue;
}
.container1 view:nth-child(3){
background-color:lightcoral;
}
.container1{
border: 1px solid red;
height:120px;
width:100px;
}
swiper和swiper-item
<swiper class="swiper-container" indicator-dots>
<swiper-item>
<view class="item">A</view>
</swiper-item>
<swiper-item>
<view class="item">b</view>
</swiper-item>
<swiper-item>
<view class="item">c</view>
</swiper-item>
</swiper>
.swiper-container{
height: 150px;
}
.item{
height: 100%;
line-height: 150px;
text-align: center;
}
swiper-item:nth-child(1) .item{
background-color: lightgreen;
}
swiper-item:nth-child(2) .item{
background-color: lightskyblue;
}
swiper-item:nth-child(3) .item{
background-color: lightcoral;
}
组件常用属性:
- indicator-dots : 默认false,是否显示面板指示点
- indicator-color: 默认rgba(0,0,0,.3),指示点颜色
- indicator-active-color : 默认#000000,当前选中指示点颜色
- autoplay : 默认false,是否自动切换
- interval : 默认5000,自动切换间隔,单位ms
- circular: 默认false,是否采用衔接滑动
text
通过text的selectable属性,实现长按选中文本内容,只有text支持长按选中。
<view>
手机号可以长按选中
<text selectable>13131311313</text>
</view>
rich-text
通过组件的nodes属性节点,可以把html字符串渲染为对应的UI结构:
<rich-text nodes="<h1 style='color:red;'>标题</h1>"></rich-text>
button
按钮组件,通过open-type属性可以调用微信提供的各种功能(客服、转发、获取用户授权、获取用户信息等)
<button>黑色按钮</button>
<button type="primary">绿色按钮</button>
<button type="warn">红色按钮</button>
<button size="mini">小尺寸按钮</button>
<button type="primary" size="mini">绿色按钮</button>
<button type="warn" size="mini">红色按钮</button>
<button size="mini" plain>小尺寸镂空按钮</button>
<button type="primary" size="mini" plain>绿色按钮</button>
<button type="warn" size="mini" plain>红色按钮</button>
image
图片组件,默认宽度300px,高240px
<image></image>
<image src="/images/1.png"></image>
<image src="/images/1.png" mode="aspectFit"></image>
image{
border:1px solid red;
}
image组件的mode属性用来指定图片的裁剪和缩放模式,常见属性如下:
- scaleToFill:缩放模式(默认值),不保持纵横比缩放图片,是图片的宽高完全拉伸到填满image元素
- aspectFit:缩放模式,保持纵横比缩放图片,使图片的长边完全能够显示出来,可以完整的显示图片
- aspectFill:缩放模式,保持纵横比缩放图片,只保证图片的短边完全显示,也就是图片只在一个方向完整。
- widthFix:缩放模式,image宽度不变,高度自动变化(image的高度),保持原图宽高比不变
- heightFix:缩放模式,image高度不变,宽度自动变化(image的宽度),保持原图宽高比不变
API
小程序的API是宿主环境提供的,包括获取用户信息、本地存储、支付功能等。
事件监听
特点:以on开头,用来监听某些事件的触发
举例:wx.onWindowResize(function callback)监听窗口尺寸的变化
wx可以理解为js中的window全局对象。
同步API
特点:以sync结尾
? 同步API的执行结果,可以通过函数返回值直接获取,失败则抛出异常
举例:wx.setStorageSync(‘key’,‘value’)向本地存储写入内容。
异步API
特点:类似于ajax(option),需要通过success、fail、complete等接受调用结果
举例:wx.request()发起网络请求,通过success回调函数接收数据。
?
发布
上传代码:点击开发工具栏中的上传,写版本号和项目备注。
查看版本:小程序管理后台,管理,版本管理,开发版本中查看
提交审核
发布
二维码获取:小程序管理后台,设置,基本设置,小程序码和线下物料下载
基础
数据绑定
在data中定义数据
在页面对应的js中
Page({
data:{
info:'hello',
imgsrc:'http://www.itheima.com/images/logo.png',
randomNum:Math.random() * 10,
randomNum2:Math.random().toFixed(2),
}
})
在wxml中使用数据
mustache语法,插值表达式
动态绑定内容
<view>{{info}}</view>
动态绑定属性
<image src="{{imgsrc}}"></image>
三元运算,算数运行
<view>{{randomNum >= 5 ? '大于5' : '小于5'}}</view>
<view>{{randomNum * 100}}</view>
可以在调试工具的AppData中查看data中的数据。
data赋值
通过this.setData 设置,通过this.data 访问
Page({
data:{
count:0
},
changeCount(){
this.setData({
count:this.data.count + 1
})
}
})
事件绑定
事件是渲染层到逻辑层的通讯方式,可以通过事件将用户在渲染层发生的行为,反馈到逻辑层处理。
常用事件:
类型 | 绑定方式 | 事件描述 |
---|
tap | bindtap,bind:tap | 类似于点击事件,手指点完离开 | input | bindinput | 文本框输入 | change | bindchange | 状态改变,如checkbox |
当事件回调触发时,会收到一个事件对象event,他的属性如下
属性 | 类型 | 说明 |
---|
type | string | 事件类型 | timeStamp | integer | 页面打开到触发事件所经过的毫秒数 | target | object | 触发事件的组件的一些属性值集合 | currentTarget | object | 当前组件的一些属性值集合 | detail | object | 额外的信息 | touches | array | 触摸事件,当前停留在屏幕中的触摸点信息的数组 | changedTouches | array | 触摸事件,当前变化的触摸点信息的数组 |
target是触发事件的源头组件,currentTarget是当前事件所绑定的组件。
<view bindtap="handle">
<button>
按钮
</button>
</view>
此时如果按钮上有点击事件,view上进行事件监听,那么target指向的是按钮,currentTarget指向的是view。
以上点击事件处理函数如下:
Page({
handle(e){
console.log(e)
}
})
传参
不能在绑定事件的同时为事件处理函数传递参数,即以下写完不合规
<button type="primary" bindtap="handle(123)">事件传参</button>
需要通过’data-'传参,如下进行:
<button type="primary" bindtap="handle" data-info="{{2}}">
事件传参
</button>
最终,info会被解析为参数的名字,数字2被解析为参数值。2需要放在{{}}中,否则会被解析为字符串2.
Page({
handle(event){
console.log(event.target.dataset)
console.log(event.target.dataset.info)
}
})
获取参数时通过event.target.dataset获取,dataset是一个对象,包含了所有通过data-*传递过来的参数项;通过dataset可以访问具体参数的值。
bindinput
绑定事件
<input bindinput="handle"></input>
事件处理
handle(e){
console.log(e.detail.value)
}
e.detail.value是变化后,文本框最新的值。
文本框和data之间的数据同步
<input value="{{msg}}" bindinput="handle"></input>
Page({
data:{
msg:'hello'
},
handle(e){
this.setData({
msg:e.detail.value
})
}
})
条件渲染
通过wx:if="{{condition}}" 来判断是否需要渲染代码块。
也可以使用wx:elif 和wx:else 进行判断
<view wx:if="{{condition}}">ok1</view>
<view wx:elif="{{condition2}}">ok2</view>
<view wx:else>okelse</view>
结合block
如果一次性控制多个组件的显示和隐藏,可以使用block标签上加wx:if控制。block并不是一个组件,只是一个包裹性容器,不会在页面中做任何渲染。(为啥不用view呢?只是少渲染一个view)
<block wx:if="{{condition}}">
<view>view1</view>
<view>view2</view>
</block>
hidden
可以直接使用hidden="{{condition}}" 进行控制显示隐藏。
<block hidden="{{condition}}">
<view>view1</view>
<view>view2</view>
</block>
不同:
- 运行方式
- wx:if 以动态创建和移除元素的方式控制 显示 隐藏
- hidden 以切换样式的方式(display:none/block),控制 显示 隐藏
- 建议
- 切换频繁,建议使用hidden
- 控制条件复杂,可以使用wx:if搭配wx:elif、wx:else
列表渲染
wx:for
? 通过wx:for可以根据指定的数组,循环渲染重复的组件结构,如下:
<view wx:for="{{array1}}">
索引:{{index}} 当前项是:{{item}}
</view>
Page({
data:{
array1 :["apple","banana"]
}
})
手动指定索引和当前项的变量名
修改index和item的变量名,可以使用其他名称指代
<view wx:for="{{array1}}" wx:for-index="idx" wx:for-item="itm">
索引:{{idx}} 当前项是:{{itm}}
</view>
key
建议指定唯一key值,提高渲染效率
Page({
data:{
array1 :[
{id:1,name:'h'},
{id:2,name:'e'},
{id:3,name:'l'},
]
}
})
<view wx:for="{{array1}}" wx:key="id">
索引:{{index}} 当前项是:{{item}}
</view>
没有key时 可以使用index作为key值
进阶
wxss
相较于css,扩展了 rpx 尺寸 和 @import ;也有一些选择器和样式不可使用。 rpx,解决小程序尺寸适配问题,rpx将屏幕横向上分为750份,rpx自动进行单位换算。一般建议iphone6作为视觉参考,1rpx=0.5px。 @import,后接需要导入的外联样式表的相对路径,用分号表示语句结束,如@import "common.wxss"; ,common.wxss在当前路径下,在wxss中使用。 全局样式,定义在app.wxss页面样式,作用于所有页面。 局部样式,定义在 页面.wxss 的样式,只作用于当前页面。局部样式会覆盖全局样式,只有局部页面样式权重大于等于全局样式才会覆盖。
全局配置
根目录中的app.json文件。
- pages, 记录当前小程序页面的存放路径
- window,全局设置窗口的外观
- tabBar,底部tabBar的效果
- style,是否启用新版的组件样式,值如‘v2’
window节点常用配置:
- navigationBarTitleText:导航栏标题文本
- navigationBarBackgroundColor:导航栏背景颜色,只支持"#121212"格式
- navigationBarTextStyle:导航栏标题文本颜色,只支持“white”和“black”
- enablePullDownRefresh:全局开启下拉刷新,true;会作用于每个页面,效果可能在开发工具中表现的有差异。
- backgroundColor:下拉刷新时的背景颜色,只能是"#121212"格式
- backgroundTextStyle:下拉刷新时的loading样式,小圆点样式,只能时light和dark。
- onReachBottomDistance:上拉触底的距离,默认距离50px,一般不更改。
tabBar
用于实现多页面切换,分为顶部tabBar和底部tabBar。 只能配置做少2个,最多5个;当渲染顶部tabBar,只显示文本。
- backgroundColor:背景色
- selectedIconPath:选中时的图片路径
- borderStyle:边框的颜色
- iconPath:未选中时的图片路径
- selectedColor:文字选中时的颜色
- color:文字未选中的颜色
app.json中tabBar节点配置项
- position:bottom或者top
- borderColor:边框的颜色
- color:文字未选中的颜色
- selectedColor:文字选中时的颜色
- backgroundColor:背景色
- list:必选,tab页列表,大于等于2个,小于等于五个
其中list项对象包括的配置选项如下:
- pagePath: 必填,页面路径,同Pages中的内容
- text: tab显示的文本呢
- iconPath: 未选中时的图片路径
- selectedIconPath:选中时的图片路径
注意:tabBar涉及到的页面必须放在pages数组中的前面。
页面配置
页面下的 .json文件,对单独页面窗口外观进行配置。 如,增加节点 navigationBarBackgroundColor:"#ff0000",则对当前页面导航栏进行了修改。 节点内容同app.json中window下各个键:navigationBarTitleText ,navigationBarBackgroundColor , navigationBarTextStyle , enablePullDownRefresh , backgroundColor ,backgroundTextStyle , onReachBottomDistance 。
网络数据请求
限制:
- 只能请求https类型的接口
- 必须将接口的域名添加到信任列表,(可以在开发工具中的详情?项目配置?域名信息 中查看 request合法域名)
- 所以地址必须时域名,不可以时ip的格式
配置合法域名: 登录微信小程序管理后台?开发?开发配置?服务器域名?需改request合法域名。
get请求
使用wx.request() 方法
wx.request({
url:'https://www.escook.cn/api/get',
method:'GET',
data:{
name:'zs',
age:'30'
},
success:(res)=>{
console.log(res);
}
})
post请求
使用wx.request() 方法
wx.request({
url:'https://www.escook.cn/api/post',
method:'POST',
data:{
name:'zs',
age:'30'
},
success:(res)=>{
console.log(res);
}
})
在页面加载时请求数据
可以在页面的 onLoad 中进行请求。
onLoad:function(options){
this.getData();
}
跳过request合法请求
可以在开发者工具中临时开启(详情?本地设置) 开发环境不校验请求域名、TLS版本及https证书跳过域名校验。 只能在开发调试中使用。
跨域和ajax
- 小程序中不存在跨域的问题。
- ajax依赖于浏览器的XMLHttpRequest,但是小程序不是浏览器,所以只能叫做发起网络数据请求。
页面导航
即页面跳转
- 声明式导航:通过点击 <navigator> 实现条状。
- 编程时导航:调用api,
导航到tabBar页面(声明式)
需要指定url 和 open-type 属性,其中
- url 表示要跳转到的页面地址,必须是 / 开头
- open-type 表示跳转的方式,必须为 switchTab
如:
<navigator url="pages/message/message" open-type="switchTab">导航到消息页面</navigator>
导航到非tabBar页面(声明式)
- url 表示要跳转到的页面地址,必须是 / 开头
- open-type 表示跳转的方式,必须为 navigate(此属性可以省略,效果相同)
如:
<navigator url="pages/message/message" open-type="navigate">导航到非tab面</navigator>
后退导航(声明式)
- delta值必须是数字,表示要后退的层级,默认为1,表示后退一步。
- open-type 为 navigateBack,表示后退导航
导航到tabBar页面(编程式)
调用 wx.switchTab(Object object) 方法,可以跳转到tabBar,其中object属性:
- url 字符串,需要跳转的tabBar路径,路径不能带参数
- success 函数,成功的回调
- fail 函数,失败的回调
- complete 函数,完成的回调
wx.switchTab({
url:'/pages/message/message'
})
导航到非tabBar页面(编程式)
调用 wx.navigateTo(Object object) 方法,可以跳转到非tabBar,其中object属性:
- url 字符串,需要跳转的非tabBar路径,路径不能带参数
- success 函数,成功的回调
- fail 函数,失败的回调
- complete 函数,完成的回调
wx.navigateTo({
url:'/pages/message/message'
})
后退导航(编程式)
调用navigateBack,返回上一页面或多级页面,可选参数如下:
- delta字符串,表示要后退的层级,大于现有页面则返回首页
- success 函数,成功的回调
- fail 函数,失败的回调
- complete 函数,调用结束完成返回的回调
wx.navigateBack() 默认返回上一页吗。
导航传参(声明式)
url路径后面可以携带参数:
- 参数与路径之间使用?分割
- 参数键与参数值用=相连
- 不用参数用&分割
<navigator url="/pages/info/info?name=tom&age=30">跳转</navigator>
可以再模拟器左下角的页面参数查看。
导航传参(编程式)
调用wx.navigateTo(Object object) 跳转时,可以携带参数,如下
wx.navigateTo({
url:"pages/info/info?name=ls&age=32"
})
接受导航传参
在onLoad中直接获取(在options),假设存储到data中的query:
onLoad:function(options){
console.log(options)
this.setData({
query:options
})
}
下拉刷新和上拉触底
启用下拉刷新
- 全局开启,app.json ? window ? enablePullDownRefresh 设置true
- 局部开启,页面json ? enablePullDownRefresh 设置true(推荐)
下拉窗口样式
全局或者页面json中
- backgroundColor:背景颜色 16进制格式
- backgroundTextStyle:loading的样式,dark 或者light
监听下拉刷新事件
在页面.js 中 onPullDownRefresh() 函数中监听。
onPullDownRefresh:function(){
console.log("下拉刷新")
}
停止下拉刷新效果
通过wx.stopPullDownRefresh 实现:
onPullDownRefresh:function(){
console.log("下拉刷新")
wx.stopPullDownRefresh()
}
监听上拉触底
在页面的js文件中,通过 onReachBottom 函数监听:
onReachBottom:function(){
console.log("上拉触底")
}
例子:
<view wx:for="{{colorList}}" wx:key="index" class="num-item" sytle="background-color:rgba({{item}})">{{item}}</view>
data:{
colorList:[],
isloading:false
},
getColors(){
this.setData({
isloading:true
})
wx.showLoading({title:'数据加载中...'})
wx.request({
url:"https://www.essss.cn/api/getcolor",
method:'GET',
success:({data:res})=>{
this.setData({
colorList:[...this.data.colorList,...res.data]
})
},
complete:()=>{
wx.hideLoading()
this.setData({
isloading:false
})
}
})
},
onLoad:function(options){
this.getColors()
},
onReachBottom:function(){
if(this.data.isloading) return
this.getColors()
}
.num-item{
border:1rpx solid #efefef;
border-radius:8rpx;
line-height:200rpx;
margin:15rpx;
text-align:center;
text-shadow:0rpx 0rpx 5rpx #fff;
box-shadow:1rpx 1rpx 7rpx #aaa;
}
... 展开运算符,用于对数组或者对象的操作,也可以将几个数组进行连接,参考,
let arr=[1,2,3]
console.log(...arr)
console.log("-----------");
let arr2=[...arr]
arr2.push(4);
console.log("arr",arr);
console.log("arr2",arr2);
console.log("-----------");
let arr3=[...arr,...arr2]
console.log(arr3);
var obj1 = { foo: 'yuan', x: 42 };
var obj2 = { foo: 'li', y: 13 };
var clonedObj = { ...obj1 };
console.log("克隆后的对象",clonedObj);
console.log("-----------");
clonedObj.foo="ss"
console.log("clonedObj",clonedObj);
console.log("obj1",clonedObj);
console.log("-----------");
var mergedObj = { ...obj1, ...obj2}
console.log("合并后的对象",mergedObj);
console.log("-----------");
var mergedObj = { ...obj1, ...obj2,address:"上海"}
console.log("合并后的对象",mergedObj)
原文链接:https://blog.csdn.net/qq_44259670/article/details/116397831
自定义编译模式
每次重新编译后,模拟器自动跳转到首页 普通编译 下拉?添加编译模式?启动页面 的值进行修改,启动参数也可以自动添加,如name=zs。 删除时,可以点击 添加编译模式 中的 自定义编译模式 边的修改按钮,删除。
生命周期
- 应用生命周期:小程序从启动?运行?销毁的过程
- 页面生命周期:页面的加载?渲染?销毁的过程
应用的生命周期函数
需要再app.js中进行声明。
App({
onLaunch:function(options){ },
onShow:function(options){ },
onHide:function(){ }
})
页面的生命周期函数
需要再页面.js中进行声明。
Page({
onLoad:function(options){ },
onReady:function(){ },
onShow:function(){ },
onHide:function(){ },
onUnload:function(){ },
})
wxs脚本
wxs是微信小程序独有的脚本语言,结合wxml可以构建出页面的结构。 可以应用为过滤器。
- wxs有自己的数据类型,同js
- wxs不支持类似es6以上的语法,如let 箭头函数 展开运算符
- wxs遵循commonjs规范,如module对象,require()函数,module.exports
使用wxs脚本
wxs代码可以编写在wxml文件中的<wxs>标签内。 wxml文件中的每个<wxs>标签,必须提供module属性,用来指定当前wxs的模块名称,方便wxml中访问模块的成员。
<view>{{m1.toUpper(username)}}</view>
<wxs module="m1">
module.exports.toUpper = function(str){
return str.toUpperCase()
}
</wxs>
外联wxs脚本 wxs代码还可以编写在 .wxs后缀的文件内,如: //tools.wxs
function toLower(str){
return str.toLowerCase()
}
module.exports = {
toLower:toLower
}
应用外联wxs脚本时需要添加module和src属性(相对路径)
<wxs module="m2" src="../../utils/tools.wxs"></wxs>
<view>{{m2.toLower(username)}}</view>
注意:
- wxs的应用一般时作为过滤器,配合mustache语法使用,如上两个例子。
不能进行作为组件的事件回调函数,例如不能以下写法:
<button bindtap="m2.toLower">按钮</button>
- wxs不能调用js中的函数和小程序中的API。
组件和样式
创建
- 项目根目录 右键 新建文件夹 创建 components 文件夹,下面创建 test 文件夹
- test 文件夹上 右键 新建Component
- 输入组件名称后回车,自动生成四个文件,js json wxml wxss
建议每个组件单独创建一个文件夹存放。
引用
- 局部引用:组件只能被当前页面引用
- 全局引用:组件可以在每个小程序页面中引用
局部应用: 页面.json 文件中引用
{
"usingComponents":{
"my-test1":"/component/test1/test1"
}
}
页面给中使用组件
<my-test1></my-test1>
全局引用: 在app.json文件中,引入组件
{
"pages":[],
"usingComponents":{
"my-test1":"/component/test1/test1"
}
}
页面给中使用组件
<my-test1></my-test1>
组件文件
- 组件中的json文件中需要声明"component":true属性
- 组件的js文件中调用的是Component() 函数
- 组件的事件处理函数需要定义到methods节点中
组件样式隔离
组件间的样式不会影响到小程序和其他组件的样式,同样小程序和其他组件的样式也不影响到组件里的样式。 即组件内不可以使用app.wxss里面定义的样式类。 只用类选择器会被影响,id、标签、属性选择器不受影响,所以建议在组件中使用类选择器。
修改组件样式隔离
通过styleIsolation修改,其可选值:
- isolated,默认值,启用样式隔离
- apply-shared 页面wxss样式将影响到组件,但组件wxss中的样式不会影响到页面。
- shared 页面wxss会影响到组件,组件wxss中的样式也会影响到页面和其他设置了shared和apply-shared的组件。
组件的js文件中增加配置:
Component({
options:{
styleIsolation:'isolated'
}
})
或者在组件的json文件中增加配置:
{
"styleIsolation":"isolated"
}
数据
需要定义到data节点中
Component({
data:{
count:0
}
})
方法
组件中方法需要定义到methods节点中
Component({
methods:{
addCount(){
},
_showCount(){
}
}
})
属性
properties 是组件对外的属性,用来接受外界传递到组件中的数据,示例如下:
Component({
properties:{
max:{
type:Number,
value:10
},
max:Number
},
methods:{
getMax(){
return this.properties.max
}
}
})
使用:
<my-test max="10"></my-test>
data和properties区别
- 都是可读可写的
- data倾向于存储私有数据
- properties倾向于存储传递过来的数据
Component({
properties:{
max:{
type:Number,
value:9
},
max:Number
},
methods:{
printf(){
console.log(this.data)
console.log(this.properties)
console.log(this.data === this.properties)
}
},
data:{
count:0
}
})
调用printf,结果为true;证明data数据和properties在本质上是一样的,都是可读可写的。因此properties属性的值也可以用于页面渲染,或使用setData为properties中是属性重新赋值。
修改properties的值
Component({
properties:{max:Number},
methods:{
addCount(){
this.setData({max:this.properties.max + 1})
}
}
})
<view>properties{{max}}</view>
数据监听器
数据监听器用于监听和响应任何属性和数据字段的变化,执行特定的操作。类似与vue中的watch。
Component({
data:{d1:0,d2:0},
methods:{
chg(){
this.setData({
d1:1
})
}
},
observers:{
'd1,d2':function(newd1,newd2){
}
}
})
也可以监听对象的单个或多个属性,多个属性中如果其中一个变化则会触发监听器。
Component({
data:{
o:{a:o,b:1}
},
methods:{
chg(){
this.setData({
o:{a:11,b:22}
})
}
},
observers:{
'o.a,o.b':function(newd1,newd2){
}
}
})
案例:
Component({
data:{
rgb:{
r:0,
g:0,
b:0
},
fullColor:'0,0,0'
},
properties:{
},
methods:{
changeR(){
this.setData({
'rgb.r':this.data.rgb.r + 5 > 255 ? 255 :this.data.rgb.r + 5
})
},
changeG(){
this.setData({
'rgb.g':this.data.rgb.g + 5 > 255 ? 255 :this.data.rgb.g + 5
})
},
changeB(){
this.setData({
'rgb.b':this.data.rgb.b + 5 > 255 ? 255 :this.data.rgb.b + 5
})
},
},
observers:{
'rgb.r, rgb.g, rgb.b':function(r,g,b){
this.setData({
fullColor: `${r},${g},${b}`
})
}
}
})
<view style="background-color:rgb({{fullColor}});" class="colorBox">颜色值:{{fullColor}}</view>
<button size="mini" bindtap="changeR" type="default">R</button>
<button size="mini" bindtap="changeG" type="primary">G</button>
<button size="mini" bindtap="changeB" type="warn">B</button>
.colorBox{
line-height:200rpx;
font-size:24rpx;
color:white;
text-shadow:0rpx 0rpx 2rpx black;
text-align:center;
}
如果要监听的属性过多,可以使用通配符**来监听所有属性的变化,如
observers:{
'rgb.**':function(obj){
this.setData({
fullColor:`${obj.r},${obj.g},${obj.b}`
})
}
}
纯数据字段
指不用于渲染的data数据,如上例中的rgb,如果data中的字段既不展示到界面上,也不会传递给其他组件,仅仅在当前组件内部使用,这种特性的字段适合设置为纯数据字段。 有助于提升页面更新的性能。
使用规则: 在Component的options 节点中,指定pureDataPattern 为一个正则表达式,字段名称符合此正则表达式的字段将成为纯数据字段。如
Component({
options:{
pureDataPattern:/^_/
},
data:{
a:true,
_b:true
}
})
监听纯数据字段:
Component({
options:{
pureDataPattern:/^_/
},
data:{
_rgb:{
r:0,
g:0,
b:0
}
},
observers:{
'_rgb.**':function(obj){
this.setData({
fullColor:`${obj.r},${obj.g},${obj.b}`
})
}
}
})
组件的生命周期
生命周期函数如下:
生命周期函数 | 参数 | 描述说明 |
---|
created | 无 | 在组件实例刚刚被创建时调用 | attached | 无 | 在组件实例进入页面节点树时执行 | ready | 无 | 在组件在视图层布局完成后执行 | moved | 无 | 在组件实例被移动到页面节点树另一个位置时执行 | detached | 无 | 在组件实例被从页面节点树移除时执行 | error | Object Error | 每当组件方法抛出错误时执行 |
顺序如表中的顺序。 重点使用以下几个: created: 在组件被创建好的时候,该函数被触发;此时还不能使用setData,此阶段只能给组件的this添加一些自定义的属性字段。 attached: 该阶段,this.data已经被初始化完毕;绝大多数初始化工作可以在此阶段进行。 detached: 退出页面时,页面中的组件会触发detached;该阶段一般进行清理工作,如取消监听。
生命周期函数的使用:
Component({
lifetimes:{
attached(){},
detached(){}
},
attached(){},
detached(){}
})
新旧方式同时存在时,lifetimes中的生命周期函数会覆盖旧式的。
组件内部也可以监听 组件 所在 页面的生命周期,
生命周期函数 | 参数 | 描述 |
---|
show | 无 | 组件所在页面被展示时执行 | hide | 无 | 组件所在页面被隐藏时执行 | resize | Object Size | 组件所在页面尺寸发生变化时执行 |
使用:
Component({
pageLifetimes:{
show(){},
hide(){},
resize(){}
}
})
插槽
在wxml结构中,提供了一个** <slot> **节点(插槽),用于承载组件使用者提供的wxml结构。 可以理解为组件中预留的占位符,组件使用者提供具体的内容。
单个插槽 小程序中,默认每个自定义组件中只允许使用一个 slot 进行占位,这种个数上的限制,叫做单个插槽。
<view class="wrapper">
<view>这里是组件的内部节点</view>
<slot></slot>
</view>
<component-tag-name>
<view>这里是插入组件slot中的内容</view>
</component-tag-name>
多个插槽 启用多个插槽,在组件的js文件中进行配置:
Component({
options:{
multipleSlot:true
}
})
多个slot标签,使用name来区分
<view class="wrapper">
<slot name="before"></slot>
<view>这里是组件的内部节点</view>
<slot name="after"></slot>
</view>
<component-tag-name>
<view slot="before">这里是插入组件slot中的内容1</view>
<view slot="after">这里是插入组件slot中的内容2</view>
</component-tag-name>
父子组件通信
- 属性绑定
- 用于父组件向子组件的指定属性设置数据,仅能设置json兼容的数据(不能传递方法)
- 事件绑定
- 获取组件实例
- 父组件可以通过 this.selectComponent()获取子组件实例的对象
- 然后就可以直接访问子组件的任意数据和方法
属性绑定 父组件
<view> count值{{count}}</view>
<my-com count="{{count}}"></my-com>
data:{
count:0
}
子组件
properties:{
count:Number
}
<view> 子组件 count值{{count}}</view>
事件绑定
- 父组件的js中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件。
- 父组件的wxml中,通过自定义事件的形式,将步骤1中定义的函数引用,传递给子组件。
- 子组件的js中,通过调用
this.triggerEvent('自定义事件名称‘,{参数对象}) ,将数据发送到父组件。 - 父组件的js中,通过
e.detail 获取到子组件传递过来的数据
父组件
syncCount(e){
console.log("syncCount")
console.log(e.detail.value)
}
<my-com count="{{count}}" bind:sonf="syncCount"></my-com>
<my-com count="{{count}}" bindsonf="syncCount"></my-com>
子组件
methods:{
addcount(){
this.triggerEvent('sonf',{value:this.properties.count})
}
}
获取组件实例 在父组件中调用this.selectComponent("id或者css选择器") ,获取子组件的实例对象,从而直接访问子组件的任意数据和方法。调用时需要传入一个选择器。
<my-com class="comA" id="ca"></my-com>
<button bindtap="getchd">get</button>
getchd(){
const child = this.selectComponent('.comA')
child.setData({
count:child.properties.count + 1
})
child.addCount()
}
behaviors
用于实现组件间代码共享的特性,类似于vue中的mixins。 自定义组件A ←引用←代码共享(behavior)→引用→自定义组件B。 工作方式: 每个behavior 可以包含一组 属性、数据、生命周期函数 和方法。组件引用他时,他的属性、数据和方法会被合并到组件中。 每个组件可以引用多个behavior,behavior也可以引用其他的behavior。 创建behavior 调用Behavior(Object object) 方法即可创建一个共享的behavior实例对象,供所有组件使用: my-b.js
module.exports = Behavior({
properties:{},
data:{username:'zs'},
methods:{},
})
导入并使用behavior 在组件中,使用 require() 方法导入需要的behavior, 挂载后即可访问behavior中的数据或方法,如:
const myBehavior = require("../../behaviors/my-b")
Component({
behaviors:[myBehavior],
})
<view>behavior:{{username}}</view>
behavior中可用的节点 propertie、data、methods、behaviors使用较多。
可用的节点 | 类型 | 是否必填 | 描述 |
---|
properties | Object Map | 否 | 同组件的属性 | data | Object | 否 | 同组件的数据 | methods | Object | 否 | 同自定义组件的方法 | behavior | String Array | 否 | 引入其他的behavior | created | Function | 否 | 生命周期函数 | attached | Function | 否 | 生命周期函数 | ready | Function | 否 | 生命周期函数 | moved | Function | 否 | 生命周期函数 | detached | Function | 否 | 生命周期函数 |
behavior中同名字段的覆盖和组合规则 组价和它引用的 behavior 中 可以包含同名的 字段,处理规则如下:
- 数据(data)重名
- 对象类型数据,会进行对象合并
- 其余情况进行数据覆盖,组件 > 父behavior > 子behavior
- 属性(properties)和方法
- 生命周期函数
使用npm包
小程序支持npm包,但是有3个限制:
- 不支持依赖于 Node.js 内置库的包,如fs、path
- 不支持依赖于浏览器内置对象的包,如jquery
- 不支持依赖于c++插件的包,如一些加密包
所以支持的包很少。
vant Weapp
前端开源 小程序UI组件库,使用MIT开源许可协议。 网址:https://youzan.github.io/vant-weapp/#/home
安装
- 通过npm安装,可以使@1.3.3版本
npm init -y ,生成package.jsonnpm i @vant/weapp -S --production ,安装vant Weapp,也可以指定版本号npm i @vant/weapp@1.3.3 -S --production
- 构建npm包
- 工具→构建npm,勾选使用npm模块
- 右上角 详情,勾选使用npm模块
- 修改app.json
小程序中打开终端的方法:目录树空白处右键,在外部终端窗口打开。
使用
在 app.json 的 usingComponents 节点引入需要的组件。
"usingComponents":{
"van-button":"@vant/weapp/button/index"
}
<van-button type="primary">按钮</van-button>
定制全局主题样式
使用css变量来实现定制主题。 声明自定义属性,属性名前需要加两个减号-- ,属性值可以使任意的css值。
:root{
--my-define-color:brown;
--my-define-width:10px;
}
p{
background-color:var(--my-define-color);
width:var(--my-define-width,5px);
}
在小程序中,app.wxss文件里,写入css变量,即可对全局生效:
page{
--button-danger-background-color:#efef00;
--button-danger-border-color:#efef00;
}
小程序的根节点为page,所以定义到page下。 可在官网的定制主题里查找对应的css变量名称。
API Promise化
基于回调函数的异步API,容易造成回调地狱。 API Promise化,就是通过额外的配置,将官方的API改造成promise的异步api。
实现
依赖第三方npm包,miniprogram-api-promise。 安装:npm install --save miniprogram-api-promise@1.0.4 小程序安装新的npm包后,需要进行构建,防止出错,可以将miniprogram_npm文件夹删除。
在小程序入口文件app.js中,调用一次 promiseifyAll() 方法,即可实现 Promise 化。
import { promisifyAll } from 'miniprogram-api-promise'
const wxp = wx.p = {}
promisifyAll(wx, wxp)
使用Promise化的API
页面js
async getInfo(){
const { data:res } = await wx.p.request({
method:'GET',
url:'https://www.escook.cn/api/get',
data:{name:'zs',age:30}
})
console.log(res)
}
全局数据共享
全局数据共享(又叫状态管理),是为了解决组件之间数据共享的问题。 常用的数据共享方案有:vuex,redux,MobX等。
小程序中的全局共享
使用mobx-miniprogram 配合mobx-miniprogram-bindings 实现全局数据共享。
mobx-miniprogram 用来创建Store实例对象mobx-miniprogram-bindings 用来把Store中的共享数据或方法,绑定到组件或页面中使用
安装MobX
npm install --save mobx-miniprogram@4.13.2 mobx-miniprogram-bindings@1.21 安装完毕后, 需要删除 miniprogram_npm 后,重新工具→构建 npm
创建 MobX
根目录下创建store文件夹,下创建store.js,用来存储数据
import { observable, action } from 'mobx-miniprogram'
export const store = observable({
numA:1,
numB:2,
get sum(){
return this.numA + this.numB
}
updateNum1:action(function(step){
this.numA += step
}),
updateNum2:action(function(step){
this.numB += step
})
})
页面使用
页面js文件
import { createStoreBindings } from 'mobx-miniprogram-bindings'
import { store } from '../../store/store'
Page({
onload:function(){
this.storeBindings = createStoreBindings(this, {
store,
fields:['numA','numB','sum'],
actions:['updateNum1']
})
},
onUnload:function(){
this.storeBindings.destroyStoreBindings()
},
handle1(e){
this.updateNum1(e.target.dataset.step)
}
})
页面wxml
<view>{{numA}} + {{numB}} = {{sum}}</view>
<van-button type="primary" bindtap="handle" data-step="{{1}}">
numA + 1
</van-button>
<van-button type="danger" bindtap="handle" data-step="{{-1}}">
numA - 1
</van-button>
组件中使用
将store中的成员绑定到组件中 组件js文件
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from '../../store/store'
Component({
behaviors:[ storeBindingsBehavior ],
storeBindings:{
store,
fields:{
numA:()=>store.numA,
numB:(store)=>store.numB,
sum:'sum'
},
actions:{
updateNum2:'updateNum2'
}
},
methods:{
handle(e){
this.updateNum2(e.target.dataset.step)
}
}
})
<view>{{numA}} + {{numB}} = {{sum}}</view>
<van-button type="primary" bindtap="handle" data-step="{{1}}">
numB + 1
</van-button>
<van-button type="danger" bindtap="handle" data-step="{{-1}}">
numB - 1
</van-button>
分包
分包指的是把一个完整的小程序项目,按照需求划分为不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。
- 首次启动时缩短下载时间
- 协同开发
分包后,小程序项目有1个主包 + 多个分包组成
- 主包:一般只包含项目的启动页面或tabBar页面、以及所有分包需要的一些公共资源
- 分包:只包含和当前分包相关的页面和私有资源
分包的加载规则:
- 小程序启动时,默认会下载主包并启动主包内页面:
tabBar 页面需要放在主包中 - 当用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示
非tabBar页面可以按照功能的不同,划分为不同的分包之后,进行按需下载。
体积限制:
- 整个小程序的所有分包大小不超过16M
- 单个分包、主包大小不能超过2M
分包的使用需要在app.json 的 subpackages 节点中声明分包的结构:
{
"pages":[
"pages/index",
"pages/logs"
]
"subpackages":[
{
"root":"packageA",
"pages":[
"pages/cat",
"pages/dog"
]
},
{
"root":"packageB",
"name":"pack",
"pages":[
"pages/apple",
"pages/banana"
]
}
]
}
创建分包: 可以在app.json中,直接完善subpackages 节点,保存后,开发者工具会自动生成对应的文件。
查看分包大小: 开发工具右上角,详情,基本信息下方展开 本地代码 查看 打包规则:
- 小程序会按 subpackages 的配置进行分包, subpackages 之外的目录将会被打包到主包中
- 主包也可以有自己的pages,即最外层的pages字段
- tabBar 页面必须在主包内
- 分包之间不能互相嵌套
引用原则:
- 主包无法引用分包内的私有资源
- 分包之间不能相互引用私有资源
- 分包可以引用主包内的公共资源
独立分包
独立分包本质也是分包,但是可以独立于主包和其他分包单独运行。 普通分包必须依赖主包运行。 独立分包可以在不下载主包的情况下,独立运行。独立分包可以不下载主包,提高启动速度。一个小程序中可以有多个独立分包。 通过 independent 节点 声明当前分包为独立分包。
{
"pages":[
"pages/index",
"pages/logs"
]
"subpackages":[
{
"root":"moduleA",
"pages":[
"pages/cat",
"pages/dog"
]
},
{
"root":"moduleB",
"pages":[
"pages/apple",
"pages/banana"
],
"independent":true
}
]
}
独立分包的引用原则: 独立分包和普通分包、主包之间,相互隔绝,不能互相引用彼此的资源。
- 主包无法引用独立分包内的私有资源
- 独立分包之间,不能相互引用私有资源
- 独立分包和普通分包之间,不能相互引用私有资源
- 独立分包中不能引用主包内的公共资源
分包预下载 进入小程序的某个页面时,由框架自动预下载可能需要的分包,从而提升进入后续分包页面的速度。 配置分包预下载配置(preloadRule):
{
"preloadRule":{
"pages/contact/contact":{
"network":"all",
"packages":["pkgA"]
}
}
}
同一个分包中的页面享有共同的预下载大小限额2M。
案例
自定义tabBar
- app.json tabBar节点增加 custom:true;表示使用自定义tabBar。
- 新建名为custom-tab-bar的文件夹,必须为此名称;下面增加名为index的组件,必须叫index:右键,新建component,index。
- 基于vant组件,完善index.wxml。app.json引入组件。
- 使用自定义的tabbar图标和文本,从data中的list使用数据
- 循环渲染tabbar的item项
- 通过css变量修改tabbar图标的margin-bottom值
- 使用list中info的值渲染徽标数字
- 将全局数据中的num监听,同步到data的info中
- 使用全局数据中的active控制页面切换记录
10.修改tabbar文字的颜色
app.json
{
"pages": [
"pages/list/list",
"pages/message/message",
"pages/info/info"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#D8322E",
"navigationBarTitleText": "小微祝福",
"navigationBarTextStyle": "white"
},
"tabBar": {
"custom": true,
"list": [{
"pagePath": "pages/list/list",
"text": "列表",
"iconPath": "image/ln.png",
"selectedIconPath": "image/la.png"
},{
"pagePath": "pages/message/message",
"text": "消息",
"iconPath": "image/mn.png",
"selectedIconPath": "image/ma.png"
},{
"pagePath": "pages/info/info",
"text": "我的",
"iconPath": "image/in.png",
"selectedIconPath": "image/ia.png"
}]
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"usingComponents":{
"van-button":"@vant/weapp/button/index",
"van-tabbar":"@vant/weapp/tabbar/index",
"van-tabbar-item":"@vant/weapp/tabbar-item/index"
}
}
store.js
import { observable, action } from 'mobx-miniprogram'
export const store = observable({
num:7,
active:0,
setActive:action(function(newValue){
this.active = newValue
})
})
index.html
index.wxml
<van-tabbar active="{{ active }}" bind:change="onChange" active-color="#990101">
<van-tabbar-item wx:for="{{list}}" wx:key="index" info="{{item.info ? item.info : ''}}" >
<image slot="icon" src="{{item.iconPath}}" mode="aspectFit" style="width: 30px;height:18px;" />
<image slot="icon-active" src="{{item.selectedIconPath}}" mode="aspectFit" style="width: 30px;height:18px;" />
{{item.text}}
</van-tabbar-item>
</van-tabbar>
index.js
import {storeBindingsBehavior} from 'mobx-miniprogram-bindings'
import {store} from '../store/store'
Component({
options:{
styleIsolation:'shared'
},
observers:{
'num':function(val){
this.setData({
'list[1].info':val
})
}
},
behaviors:[storeBindingsBehavior],
storeBindings:{
store,
fields:{
num:'num',
active:'active'
},
actions:{
setActive:'setActive'
}
},
data: {
"list": [{
"pagePath": "/pages/list/list",
"text": "列表",
"iconPath": "/image/ln.png",
"selectedIconPath": "/image/la.png"
},{
"pagePath": "/pages/message/message",
"text": "消息",
"iconPath": "/image/mn.png",
"selectedIconPath": "/image/ma.png",
info:2
},{
"pagePath": "/pages/info/info",
"text": "我的",
"iconPath": "/image/in.png",
"selectedIconPath": "/image/ia.png"
}]
},
methods: {
onChange(event){
this.setActive(event.detail)
console.log(this.data.list[event.detail].pagePath)
wx.switchTab({
url: this.data.list[event.detail].pagePath,
})
}
}
})
index.css
.van-tabbar-item{
--tabbar-item-margin-bottom:0;
}
商城
黑马教程的教程,地址(https://www.escook.cn/docs-uni-shop/) 基于uniapp开发,官网地址(https://uniapp.dcloud.net.cn/) 下载Hbuilder开发工具, 安装sass编译插件, 新建,项目,基于uni-app模板;
常见错误
渲染层错误ReferenceError: __g is not defined
删除app.json中的 “lazyCodeLoading”: “requiredComponents”
|