在大部分情况下,我们都使用 rpx单位,因为rpx可以进行自适应,在屏幕尺寸和分辨率发生改变的情况下,会自动进行适应。
而px则不会进行适应。
**一般来说:**例如像字体这种,我们有时候不让其自适应。有时候我们设置 边框,border 也可能不应该自适应。
**所有的页面,小程序默认在最外层加上了page标签。**所以有些公共的样式我们可以写在app.wxss文件里面。也要注意,不是所有的公共样式都会被其他页面或者组件继承。比如我们设置的全局页面背景颜色,是不会被组件继承的。但是大部分css样式都是可以被页面继承的。
设置display属性的值为 inline-flex。整体盒外模型是自适应,内部还是flex盒子。也还是蛮不错的选择。
.container{
/* 设置弹性盒,且宽度高度还是自适应 inline-flex */
display: inline-flex;
flex-direction: row;
/* 给一个内边距 */
padding-top: 10rpx;
}
**triggerEvent()**方法
- 参数一:自定义事件名称
- 参数二:js对象,用来传递我们当前的数据,提供给外界的监听函数。默认在外界接收参数的detail属性下。一般也就是在event.detail
- 参数三:触发事件的选项,对象类型。可选属性有三:
- bubbles: 是否冒泡,默认false
- composed: 事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部,默认false
- capturePhase:默认false,事件是否拥有捕获阶段
选项名 | 类型 | 是否必填 | 默认值 | 描述 |
---|---|---|---|---|
bubbles | Boolean | 否 | false | 事件是否冒泡 |
composed | Boolean | 否 | false | 事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部 |
capturePhase | Boolean | 否 | false | 事件是否拥有捕获阶段 |
在定义组件的时候,我们在properties中定义的数据,是提供给外界的出口属性,一般小程序会给其我们设置属性的默认值的,当然也可以通过value属性指定默认值。而在data中定义属性的时候,如果直接将某个属性赋值为Number,String,等等,是不会给其该属性的默认值的。这些属性本来就是构造函数。
**这两个属性,在最后都会合并到一个对象中。也就是properties中和data中定义的属性,都会合并到一起的。**所以我们在定义变量的时候,这两个地方的变量不要设置为同名的变量。
observer属性的类型是一个函数。在这个函数里面,不要修改属性自己的值。不然会引起无限递归。
properties: {
// 期刊号
index: {
type: String,
/**
* 当前属性发生改变的时候,会执行该函数的内容
* 不能在这个里面更新 index 的值 我们应该修改 _index
* 修改index 的值会出现无限递归,因为每次修改值都会调用这个函数
*/
observer(newVal, oldVal, changePath) {
const val = newVal < 10 ? "0" + newVal : newVal;
this.setData({
// 直接无限递归了
// index:val,
_index: val
})
}
}
}
behavior:行为。
给多个组件定义共同的行为。
将多个组件的共性抽取出来,定义在一个单独的文件中。供多个组件一起使用。
进行代码的复用,是通过定义 Behavior来实现的。
可以把该文件认为和组件的js文件基本一致,都可以定义properties,data,methods等属性。
也可以有生命周期。
// Behavior 就是一个构造器
export default Behavior({
properties: {
// 图片
img: String,
// 内容
content: String
},
data: {},
methods: {
}
})
组件想要使用这些定义好的行为,需要显示的继承。一个组件可以继承多个行为,就有了他们共有的属性。
Component({
// 指定继承的behavior,可以继承多个,继承了就有了这些定义好的属性和方法等
behaviors:[classicBeha],
})
继承的生命周期和组件内部定义的生命周期会合并。但是像属性和方法,组件内部定义了的为主,继承的属性和组件内部属性发生冲突时,以组件内部为主。多继承时,继承的属性发生冲突的时候,以最后一个聚餐的behavior为准。
不管是直接传入回调函数,还是以promise来执行回调函数。返回值的data属性才是我们实际拿到的数据。res并不是。
wx.getStorage({
key: 'key',
success (res) {
console.log(res.data)
}
})
wx.getStorage({
key: 'key'
}).then(res=>{
console.log(res.data)
})
@import "需要复用的wxss文件";
<!-- 别忘了后面需要分号结束 -->
小程序将背景音频相关的操作,都放到该对象中。注意:只要给该对象赋值了src属性,就会自动开始播放了。
// 背景音乐管理对象
const mgr = wx.getBackgroundAudioManager();
// 切换暂停和播放的图标
this.setData({
playing: !this.data.playing
});
if (this.data.playing) {
// 音乐播放
mgr.src = this.properties.musicSrc;
mgr.title = "听雨少年!";
console.log(this.properties.musicSrc);
}else{
// 音乐暂停
mgr.pause();
}
在现在的小程序,背景音频对象的 title属性和src属性属于必填项,不然不会生效。
当然,如上配置出现暂停的时候,会报上面的错误。
需要在app.json中配置小程序退到后台时,也可以继续播放音频的属性。
{
"requiredBackgroundModes": ["audio","location"],
}
申明需要后台运行的能力,类型为数组。目前支持以下项目:
audio
: 后台音乐播放location
: 后台定位
以 Promise 风格 调用:支持
保留当前页面,跳转到应用内的某个页面。相当于打开的页面是子页面。
注意:跳转页面传递参数时:只能通过查询字符串进行传递。
需要跳转的应用内非 tabBar 的页面的路径 (代码包路径), 路径后可以带参数。参数与路径之间使用 ?
分隔,参数键与参数值用 =
相连,不同参数用 &
分隔;如 'path?key=value&key2=value2'
和vue了插槽很相似。插槽就是占位使用。如果我们需要插槽的位置插入标签,组件等,就可以直接在外面插入,替换插槽。不需要则不需要传递。
<!-- 组件插槽的使用 -->
<!-- 这是具名插槽,使用的时候也需要指定插槽的名字 -->
<!-- 如果只有一个插槽,是可以不指定插槽的名称的,那样外面使用的时候也可以不指定插槽的名称 -->
<slot name="after"></slot>
在组件的js文件中配置插槽生效。启用插槽。
Component({
// 需要在options属性里面 设置 multipleSlots 的值为true
// 插槽才能起作用
options:{
multipleSlots:true
}
})
插槽的使用
<!-- 插槽的使用 -->
<!-- 直接在标签上 通过slot属性指定要替换的插槽 -->
<text slot="after">{{"+" + comment.nums}}</text>
对于组件插槽,发现了一个特点。
如果说,组件只有一个默认插槽,也就是插槽只有一个,而且没名字,那么,不需要再组件的js文件中设置options对象的multipleSlots属性为true。
设置这个属性为true,主要是为了开启多插槽的使用。
也就是说,只有默认插槽的情况下,不需要配置该属性的。
还有,如果将其他组件用来替换插槽,其他组件是需要在当前所在的页面的json文件中进行配置的。
组件对应 wxss
文件的样式,只对组件wxml内的节点生效。
#a { } /* 在组件中不能使用 */
[a] { } /* 在组件中不能使用 */
button { } /* 在组件中不能使用 */
.a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */
有时,组件希望接受外部传入的样式类。此时可以在 Component
中用 externalClasses
定义段定义若干个外部样式类。
这个特性可以用于实现类似于 view
组件的 hover-class
属性:页面可以提供一个样式类,赋予 view
的 hover-class
,这个样式类本身写在页面中而非 view
组件的实现中。
注意:在同一个节点上使用普通样式类和外部样式类时,两个类的优先级是未定义的,因此最好避免这种情况。
上面这句话就是废话,说白了,就是你传递的外部样式,和我组件的默认样式如果发生冲突的时候,**而你又没有指定两个冲突样式的优先级,**那我就不确定到底使用的是哪一个样式。
因此传递外部样式的时候,建议还是指定某些不生效样式的优先级。
// 外部样式 在组件内定义好样式名,然后实际引用的样式是外部传递的
externalClasses:["tag-class"],
<view class="container tag-class"></view>
感觉小程序在外部样式这一块做的不是很好。正常来说,放到后面的样式,就应该覆盖前面的样式。也就是样式发生冲突的时候,而优先级是一样的情况下,tag-class的样式就应该覆盖container的样式,不然,每次还需要强制优先级多麻烦。
<!-- 传递外部样式 -->
<m-tag tag-class="ex-tag1"/>
我们将在页面的wxss定义的样式名称,也就是class属性值,直接传递给组件内部。
<text>aaaaaaaa
bbbbbb
cccccccc</text>
我们在书写内容的时候,如果因为某些原因,比如不美观等,我们敲下回车进行换行,那么,解析的时候,呈现在页面上的内容,也会因为你的回车换行而换行。
WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML
,可以构建出页面的结构
- WXS 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。
- WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。
- WXS 的运行环境和其他 JavaScript 代码是隔离的,WXS 中不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序提供的API。
- WXS 函数不能作为组件的事件回调。
- 由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。
一个wxs文件,或者写在页面上的wxs标签里面的代码,就是一个单独的wxs模块。
每一个 .wxs
文件和 <wxs>
标签都是一个单独的模块。
每个模块都有自己独立的作用域。即在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见。
一个模块要想对外暴露其内部的私有变量与函数,只能通过 module.exports
实现。
使用wxs的时候,请参照js的es5标准。不要使用es6及其新特性。无法使用的。
所以我们可以用wxs来写过滤器等等。
定义一个过滤器,来处理文本中被转义的换行符。
// 用来过滤掉 \n 解析为换行等
/**
* wxs和js类似。但是,wxs并不是js。wxs有自己独立的运行环境。
* @param {String} text 文本
*/
var format = function (text) { // 只能使用var定义(目前是这样),也不能使用箭头函数
// 获取正则表达式的对象 (参照官方文档)
if(!text) return;
var reg = getRegExp("\\\\n","g");
return text.replace(reg,"\n ");
}
module.exports.format = format;
我们需要导入外部定义的wxs,并指定模块名称。
<!-- 引入wxs模块 ,并使用module属性指定模块名(任意名称)-->
<wxs src="../../utils/filter.wxs" module="util" />
引入后的页面上使用,就可以通过我们定义的模块名使用我们导出的函数或者变量等。
<!-- 数据绑定的时候,这个函数会被执行两次,数据初始化一次,数据更新一次 -->
<!-- decode 解码我们的某些特殊字符 -->
<!-- decode可以解析的有 < > & '     -->
<text decode="{{true}}" class="content">{{util.format(bookDetail.summary)}}</text>
将某些需要复用的函数等卸载wxs文件中,可以进行复用。
wx: wx.previewImage({
// 预览的图片,可以有多个 值是src的值 也就是链接地址
urls: [this.data.movie.image],
// 当前预览的第一张图片
current: this.data.movie.image
})
同时发起多个请求,如果服务器支持多线程,同时可以处理多个请求,那么就是并行。很明显并行是比穿行效率高的。
使用Promise的静态方法,all方法可以将多个promise对象合并为一个promise,当所有的promise处理完毕后,才会返回新的promise。这个返回的promise可以视作为多个我们传递的promise对象和合集。
const getBookDetail = bookModel.getBookDetail(bookId);
const getComments = bookModel.getBookComments(bookId);
const getLikeStatus = bookModel.getBookLikeStatus(bookId);
Promise.all([getBookDetail, getComments, getLikeStatus])
.then((res) => {
// 返回值res就是一个数组,传递到all的参数数组有几个元素
// res这个返回值数组就有几个元素
this.setData({
bookDetail: res[0],
comments: res[1].comments,
likeStatus: res[2].like_status,
likeCount: res[2].fav_nums
})
// 隐藏加载中的提示框
wx.hideLoading();
})
race方法和all方法则相反,只要有任何一个传递的promise对象率先完成了请求,就会执行返回值的回调。不会等待其他的,所以:rece方法返回的promise对象,我们的then方法里面只会有第一个请求完成的那个promise的返回值。
也就是res只有一个返回值。
实现类似瀑布屏一样的效果,我们有两种解决方案
- 使用小程序的scroll-view组件,比较简单
- 使用 Page页面的 onReachBottom事件来实现加载更多的效果。
该处理函数在当前页面上拉触底事件的触发的时候,会进行回调该函数。
onReachBottom() {
// 触发该函数,让搜索组件加载更多的数据
this.setData({
more: randomString(32)
})
}
在页面触发该函数,然后让组件内部就开始发起请求,加载更多数据。randomString(32)是生成指定位数随机字符串的自定义函数。
开发search组件的时候,在发起请求的时候,我们一般都会在请求之前进行加锁,避免在请求的数据没有返回之前,发起二次请求,这时候,我们是讲锁的释放防到了请求成功的回调函数中,这种情况下,一旦发起的请求失败,比如突然断网,这时候就会处于锁无法释放。
在请求失败的请求下,我们也需要释放锁。这里要注意。
使用该标签可以展示微信提供的接口。也就是展示微信开放的数据。比如使用这个标签的type属性看样子展示用户头像,用户昵称等。
<!-- 获取用户信息 调用小程序的接口 不需要授权就能显示用户的头像和昵称 -->
<!-- 这种方式只能展示,无法在js中获取用户的信息 -->
<open-data class="avatar avatar-position" type="userAvatarUrl" />
如果只是展示用户的某些数据,可以使用这个标签。但是如果需要获取用户信息提交到服务器,那么我们必然是需要在js中获取到用户信息的。那么这种方式是不可行的。
截止21年9月,目前新版的授权方式发生了比较大的变化。
获取用户信息。页面产生点击事件(例如 button
上 bindtap
的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回 userInfo
。该接口用于替换 wx.getUserInfo
也就是说: 只有在页面发生了点击行为的回调函数中,才能通过这个getUserProfile方法进行弹出用户授权的提示框。而且即使授权过,也可以再次点击授权,用户同意授权后可以在该方法的回调函数中拿到用户授权后的信息。
<button bindtap="getUserInfo">授权</button>
getUserInfo(event){
wx.getUserProfile({
lang:"zh_CN",
desc:"是否授权",
}).then((res)=>{
console.log(res);
})
}
这种方式每次只有点击以后才能获取到授权后的用户数据。