条件编译
由于各个平台有起独特性,可以使用类似于C语言的条件编译语句,指定一段代码只在特定的环境中被编译。
格式:
//#ifdef %PLATFORM%
平台特有的API实现
//#endif
判断某个平台存在:
//#ifdef %PLATFORM%
需条件编译的代码
//#endif
判断某个平台不存在:
//#ifndef %PLATFORM%
需条件编译的代码
//#endif
%PLATFORM% 可取值如下:
值 | 平台 | 相关规范 |
---|---|---|
APP-PLUS | App | HTML5+ App 规范 |
APP-PLUS-NVUE或APP-NVUE | App nvue | |
H5 | H5 | |
MP-WEIXIN | 微信小程序 | 微信小程序 |
MP-ALIPAY | 支付宝小程序 | |
MP-BAIDU | 百度小程序 | |
MP-TOUTIAO | 字节跳动小程序 | |
MP-QQ | QQ小程序 | |
MP-360 | 360小程序 | |
MP | 微信小程序/支付宝小程序/百度小程序/字节跳动小程序/QQ小程序/360小程序 | |
QUICKAPP-WEBVIEW | 快应用通用(包含联盟、华为) | |
QUICKAPP-WEBVIEW-UNION | 快应用联盟 | |
QUICKAPP-WEBVIEW-HUAWEI | 快应用华为 |
支持的文件
- .vue
- .js
- .css
- pages.json
- 各预编译语言文件,如:.scss、.less、.stylus、.ts、.pug
例如:
//#ifdef APP-PLUS
plus.push.addEventListener('click', function (msg) {
var payload = null;
var action = '';
if (msg.payload) {
if (typeof msg.payload === 'string') {
payload = JSON.parse(msg.payload);
}
action = payload.action;
if (action === 'open') {
plus.webview.open(payload.url);
}
}
});
//#endif
在HTML或CSS中也可以使用条件编译:
<!-- #ifdef MP-WEIXIN -->
需条件编译的代码
<!-- #endif -->
/* #ifdef %PLATFORM% */
需条件编译的代码
/* #endif */
可以使用 ||
连接多个平台,例如:
// #ifdef H5 || MP-WEIXIN
需条件编译的代码
// #endif
使用Vue进行开发
兼容支持
由于需要兼容多端,许多浏览器特性、Vue特性是不能使用的,支持情况如下:
支持:
- 条件渲染(v-if、v-show)
- 列表渲染(v-for)
- 计算属性(computed)
- 事件处理(v-on,@)、事件修饰符
不支持:
- 所有的 BOM/DOM 都不能用
- v-html 指令不能用,可以使用 rich-text组件 代替
- Vue 过滤器(filter)
- data 必须声明为返回一个初始数据对象的函数
- 按键修饰符
- 模板中不支持复杂的JavaScript表达式
事件处理
uni-app 支持大部分 Vue 中的事件,对部分事件进行了改写:
// 事件映射表,左侧为 WEB 事件,右侧为 ``uni-app`` 对应事件
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
longtap: 'longtap',
input: 'input',
change: 'change',
submit: 'submit',
blur: 'blur',
focus: 'focus',
reset: 'reset',
confirm: 'confirm',
columnchange: 'columnchange',
linechange: 'linechange',
error: 'error',
scrolltoupper: 'scrolltoupper',
scrolltolower: 'scrolltolower',
scroll: 'scroll'
}
在 input 和 textarea 中 change 事件会被转为 blur 事件。
事件修饰符
- stop 的使用会阻止冒泡,但是同时绑定了一个非冒泡事件,会导致该元素上的 catchEventName 失效!
- prevent 可以直接干掉,因为uni-app里没有什么默认事件,比如 submit 并不会跳转页面
- self 没有可以判断的标识
- once 也不能做,因为uni-app没有 removeEventListener,虽然可以直接在 handleProxy 中处理,但非常的不优雅,违背了原意,暂不考虑
其他不支持的部分
模板中不支持复杂的JavaScript表达式
目前可以使用的有 + - * % ?: ! == === > < [] .
。
比如以下模板语法就不支持:
<view>{{ message.split('').reverse().join('') }}</view>
通常这种情况使用计算属性即可。
生命周期
应用程序生命周期
这是整个程序的生命周期函数,仅可在App.vue中监听,在其它页面监听无效。
- onLaunch 当uni-app 初始化完成时触发(全局只触发一次)
- onShow 当 uni-app 启动,或从后台进入前台显示
- onHide 当 uni-app 从前台进入后台
页面生命周期
uni-app 支持如下页面生命周期函数:
- onLoad 监听页面加载,其参数为上个页面传递的数据,参数类型为Object(用于页面传参)
- onShow 监听页面显示
- onReady 监听页面初次渲染完成
- onHide 监听页面隐藏
- onUnload 监听页面卸载
- onPullDownRefresh 监听用户下拉动作,一般用于下拉刷新,参考示例
- onReachBottom 页面上拉触底事件的处理函数
- onShareAppMessage 用户点击右上角分享,仅微信小程序支持
- onNavigationBarButtonTap 监听原生标题栏按钮点击事件,参数为Object,仅5+ App支持
- onPageScroll 监听页面滚动,参数为Object
Vue 实例生命周期
跟正常开发 Vue 一样,由以下 Vue 的生命周期构成:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
页面事件监听
pageScrollTo
pageScrollTo 可以滚动到指定页面指定位置,通常用于制作返回顶部,以及一些滑动特效。
uni.pageScrollTo({
scrollTop: 0,
duration: 300
});
- scrollTop Number 必填,滚动到页面的目标位置(单位px)
- duration Number 可选,滚动动画的时长,默认300ms,单位 ms
下拉刷新
首先,得在 page.json 中开启当前页的下拉刷新配置:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"enablePullDownRefresh": true
}
}
]
}
这样就可以在页面中监听其下拉事件了:
export default {
onPullDownRefresh () {
console.log('refresh');
setTimeout(function () {
uni.stopPullDownRefresh();
}, 1000);
}
}
:::info onPullDownRefresh 是跟 onLoad 同级的页面事件。 :::
下拉事件可以通过下拉页面触发,也可通过事件绑定触发,比如绑定一个按钮事件:
<template lang='pug'>
.test
button(@tap='refresh') refresh
</template>
<script>
export default {
methods: {
refresh () {
uni.startPullDownRefresh();
}
}
}
</script>
uni.startPullDownRefresh()
开始刷新uni.stopPullDownRefresh()
停止刷新
上拉加载更多
首先,得在 page.json 中开启当前页的触底距离,也可以不配置,默认为50:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"onReachBottomDistance": 50
}
}
]
}
在页面中监听其触底事件:
export default {
onReachBottom () {
console.log('get more data');
}
}
封装了一个上拉加载更多的组件:
<template lang="pug">
scroll-view.wrap(scroll-y
upper-threshold="0.01"
@scrolltoupper='scrolltoupper'
@scrolltolower='scrolltolower'
)
.content
.list
.item(v-for='(item, key) in list' :key='key') {{item}}
load-more(:loadingType='loadingType')
</template>
<script>
import throttle from 'lodash.throttle'
export default {
data () {
return {
loadingType: 0,
last: 20,
list: Array.from({length: this.last}, (v, i) => `item${i+1}`)
}
},
methods: {
scrolltoupper: throttle(function () {
if (this.loadingType) return
this.loadingType = 1
console.log('scrolltoupper');
}, 2000),
scrolltolower: throttle(function () {
if (this.loadingType) return
this.loadingType = 1
setTimeout(() => {
this.loadingType = 2
}, 1000)
}, 2000)
}
}
</script>
<style scoped lang="stylus">
.wrap
height: 100vh;
.content
.item
font-size: 1.2em;
padding: 1em;
border-bottom: 1px dashed #ff0;
</style>
这是 load-more 组件的写法,参考了官方 Demo 的写法:
components/load-more.vue
<template>
<view class="load-more">
<view class="loading-img" v-show="loadingType === 1 && showImage">
<view class="load1">
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
</view>
<view class="load2">
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
</view>
<view class="load3">
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
</view>
</view>
<text class="loading-text" :style="{color:color}">{{loadingType === 0 ? contentText.contentdown : (loadingType === 1 ? contentText.contentrefresh : contentText.contentnomore)}}</text>
</view>
</template>
<script>
export default {
name: "load-more",
props: {
loadingType: {
//上拉的状态:0-loading前;1-loading中;2-没有更多了
type: Number,
default: 0
},
showImage: {
type: Boolean,
default: true
},
color: {
type: String,
default: "#777777"
},
contentText: {
type: Object,
default () {
return {
contentdown: "上拉显示更多",
contentrefresh: "正在加载...",
contentnomore: "没有更多数据了"
};
}
}
},
data() {
return {}
}
}
</script>
<style>
.load-more {
display: flex;
flex-direction: row;
height: 80upx;
align-items: center;
justify-content: center;
}
.loading-img {
height: 24px;
width: 24px;
margin-right: 10px;
}
.loading-text {
font-size: 15px;
color: #777777;
}
.loading-img>view {
position: absolute;
}
.load1,
.load2,
.load3 {
height: 24px;
width: 24px;
}
.load2 {
transform: rotate(30deg);
}
.load3 {
transform: rotate(60deg);
}
.loading-img>view view {
width: 6px;
height: 2px;
border-top-left-radius: 1px;
border-bottom-left-radius: 1px;
background: #777;
position: absolute;
opacity: 0.2;
transform-origin: 50%;
-webkit-animation: load 1.56s ease infinite;
}
.loading-img>view view:nth-child(1) {
transform: rotate(90deg);
top: 2px;
left: 9px;
}
.loading-img>view view:nth-child(2) {
-webkit-transform: rotate(180deg);
top: 11px;
right: 0px;
}
.loading-img>view view:nth-child(3) {
transform: rotate(270deg);
bottom: 2px;
left: 9px;
}
.loading-img>view view:nth-child(4) {
top: 11px;
left: 0px;
}
.load1 view:nth-child(1) {
animation-delay: 0s;
}
.load2 view:nth-child(1) {
animation-delay: 0.13s;
}
.load3 view:nth-child(1) {
animation-delay: 0.26s;
}
.load1 view:nth-child(2) {
animation-delay: 0.39s;
}
.load2 view:nth-child(2) {
animation-delay: 0.52s;
}
.load3 view:nth-child(2) {
animation-delay: 0.65s;
}
.load1 view:nth-child(3) {
animation-delay: 0.78s;
}
.load2 view:nth-child(3) {
animation-delay: 0.91s;
}
.load3 view:nth-child(3) {
animation-delay: 1.04s;
}
.load1 view:nth-child(4) {
animation-delay: 1.17s;
}
.load2 view:nth-child(4) {
animation-delay: 1.30s;
}
.load3 view:nth-child(4) {
animation-delay: 1.43s;
}
@-webkit-keyframes load {
0% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
</style>
注意到,这里没用使用图片,也没有使用字体图标,而是纯代码生成的一个加载更多动画。
全局引入:main.js
import loadMore from './components/load-more.vue'
Vue.component('loadMore', loadMore)
页面滚动监听
通过 onPageScroll 监听页面滚动:
export default {
onPageScroll (e) {
console.log(e.scrollTop); // 页面在垂直方向已滚动的距离(单位px)
}
}
可以使用防抖函数防止事件频繁调用:
import throttle from 'lodash.throttle'
export default {
onPageScroll: throttle(function (e) {
console.log(e.scrollTop);
}, 2000)
}
页面分享
使用 onShareAppMessage 可设置页面分享,接收一个参数 obj,包括如下内容:
- from String 转发事件来源。button:页面内转发按钮;menu:右上角转发菜单
- target 如果 from 值是 button,则 target 是触发这次转发事件的 button,否则为 undefined
onShareAppMessage 需要设置一个返回值,用于自定义分享的标题、路径、图片,都是可选的。
- title 默认为当前小程序名称
- path 默认为当前页面 path ,若自定义必须是以 / 开头的完整路径
- imageUrl 默认使用当前页面截图
export default {
onShareAppMessage (e) {
console.log(e);
return {
title: 'Hello world',
path: '/',
imageUrl: '/static/imgs/test.png'
}
}
}
scroll-view 滚动监听
在 scroll-view 组件中,提供了三个事件监听:
- scrolltoupper 滑动到容器顶部触发的事件,距离由 upper-threshold 决定,默认 50
- scrolltolower 滑动到容器底部触发的事件,距离由 lower-threshold 决定,默认 50
- scroll 容器滚动时触发,接收一个 event 参数,可获取当前位置 event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY} ```vue scroll-view.wrap(scroll-y upper-threshold=”0.01” lower-threshold=”0.01” @scrolltoupper=’scrolltoupper’ @scrolltolower=’scrolltolower’ ) .content .list .item(v-for=’(item, key) in list’ :key=’key’) {{item}}
```
以上代码,使用了 lodash.throttle
防止抖动。