单页面程序
uni-app
约定页面文件遵循 Vue 单文件组件 (SFC) 规范,采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统
<template>
<view class="container">
{{message}}
</view>
</template>
<script>
export default {
data() {
return {
message: 'Hello World',
}
}
}
</script>
<style>
.container{
background-color: #FFF;
}
</style>
模版语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
模板语法,包含插值、指令
插值
文本,使用双大括号:<span>Message: {{ msg }}</span>
原始HTML,使用v-html指令:<p>Using v-html directive: <span v-html="rawHtml"></span></p>
使用 JavaScript 表达式,支持JavaScript 表达式:{{ number + 1 }}
指令
v-on与v-bind是常用的指令,用于绑定属性设置事件
Attribute,使用v-bind 指令(缩写可以直接省略v-bind):<button v-bind:disabled="isButtonDisabled">Button</button>
事件,使用 v-on 指令(缩写@):<a @click="doSomething">...</a>
条件与循环,v-if
v-else-if
v-else
v-for
计算属性与侦听器
计算属性
计算属性是基于它们的响应式依赖进行缓存的
computed: {
msg: function () {
return this.message
}
}
侦听属性
当你有一些数据需要随着其它数据变动而变动时,用 watch
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
}
class与style绑定
通过动态绑定 style、class 属性来控制组件的样式。
-
style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。
<view :style="{color:color, fontSize: fontSize + 'px'}" />
-
动态绑定 class
<view class="normal_view" />
对象语法:
<view :class="{ active: isActive }"></view>
上面的语法表示
active
这个 class 存在与否将取决于数据isActive
数组语法:
<view v-bind:class="[activeClass, errorClass]"></view> data: { activeClass: 'active', errorClass: 'text-danger' }
关于选择器注意:
在
uni-app
中不能使用*
选择器。目前vue支持的选择器有:
.class
#id
element
::after
`::beforenvue页面、微信小程序自定义组件中仅支持 class 选择器
生命周期
应用生命周期
uni-app
支持如下应用生命周期函数:
函数名 | 说明 |
---|---|
onLaunch | 当uni-app 初始化完成时触发(全局只触发一次) |
onShow | 当 uni-app 启动,或从后台进入前台显示 |
onHide | 当 uni-app 从前台进入后台 |
onError | 当 uni-app 报错时触发 |
onUniNViewMessage | 对 nvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯
|
onUnhandledRejection | 对未处理的 Promise 拒绝事件监听函数(2.8.1+) |
onPageNotFound | 页面不存在监听函数 |
onThemeChange | 监听系统主题变化 |
注意
-
应用生命周期仅可在
App.vue
中监听,在其它页面监听无效。示例代码
<script> // 只能在App.vue里监听应用的生命周期 export default { onLaunch: function() { console.log('App Launch') }, onShow: function() { console.log('App Show') }, onHide: function() { console.log('App Hide') } } </script>
页面生命周期
uni-app
支持如下页面生命周期函数:
函数名 | 说明 | 平台差异说明 | 最低版本 |
---|---|---|---|
onInit | 监听页面初始化,其参数同 onLoad 参数,为上个页面传递的数据,参数类型为 Object(用于页面传参),触发时机早于 onLoad | 百度小程序 | 3.1.0+ |
onLoad | 监听页面加载,其参数为上个页面传递的数据,参数类型为 Object(用于页面传参),参考示例 | ||
onShow | 监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面 | ||
onReady | 监听页面初次渲染完成。注意如果渲染速度快,会在页面进入动画完成前触发 | ||
onHide | 监听页面隐藏 | ||
onUnload | 监听页面卸载 | ||
onResize | 监听窗口尺寸变化 | App、微信小程序 | |
onPullDownRefresh | 监听用户下拉动作,一般用于下拉刷新,参考示例 | ||
onReachBottom | 页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据。具体见下方注意事项 | ||
onTabItemTap | 点击 tab 时触发,参数为Object,具体见下方注意事项 | 微信小程序、支付宝小程序、百度小程序、H5、App(自定义组件模式) | |
onShareAppMessage | 用户点击右上角分享 | 微信小程序、百度小程序、字节跳动小程序、支付宝小程序 | |
onPageScroll | 监听页面滚动,参数为Object | nvue暂不支持 | |
onNavigationBarButtonTap | 监听原生标题栏按钮点击事件,参数为Object | App、H5 | |
onBackPress | 监听页面返回,返回 event = {from:backbutton、 navigateBack} ,backbutton 表示来源是左上角返回按钮或 android 返回键;navigateBack表示来源是 uni.navigateBack ;详细说明及使用:onBackPress 详解。支付宝小程序只有真机能触发,只能监听非navigateBack引起的返回,不可阻止默认行为。 | app、H5、支付宝小程序 | |
onNavigationBarSearchInputChanged | 监听原生标题栏搜索输入框输入内容变化事件 | App、H5 | 1.6.0 |
onNavigationBarSearchInputConfirmed | 监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的“搜索”按钮时触发。 | App、H5 | 1.6.0 |
onNavigationBarSearchInputClicked | 监听原生标题栏搜索输入框点击事件 | App、H5 | 1.6.0 |
onShareTimeline | 监听用户点击右上角转发到朋友圈 | 微信小程序 | 2.8.1+ |
onAddToFavorites | 监听用户点击右上角收藏 | 微信小程序 | 2.8.1+ |
页面样式与布局
uni-app的css与web的css基本一致。
uni-app有vue页面和nvue页面。vue页面是webview渲染的、app端的nvue页面是原生渲染的
尺寸单位
uni-app
支持的通用 css 单位包括 px、rpx
- px 即屏幕像素
- rpx 即响应式px,一种根据屏幕宽度自适应的动态单位。以750宽的屏幕为基准,750rpx恰好为屏幕宽度。屏幕变宽,rpx 实际显示效果会等比放大,但在 App 端和 H5 端屏幕宽度达到 960px 时,默认将按照 375px 的屏幕宽度进行计算
- uni-app 2.9+ 新增 leftWindow, topWindow, rightWindow 配置。用于解决宽屏适配问题。
nvue还不支持百分比单位
vue页面支持下面这些普通H5单位,但在nvue里不支持:
- rem 根字体大小可以通过 page-meta 配置
- vh viewpoint height,视窗高度,1vh等于视窗高度的1%
- vw viewpoint width,视窗宽度,1vw等于视窗宽度的1%
App端,在 pages.json 里的 titleNView 或页面里写的 plus api 中涉及的单位,只支持 px。注意此时不支持 rpx
nvue中,uni-app 模式(nvue 不同编译模式介绍)可以使用 px 、rpx,表现与 vue 中一致。weex 模式目前遵循weex的单位,它的单位比较特殊:
- px:,以750宽的屏幕为基准动态计算的长度单位,与 vue 页面中的 rpx 理念相同。(一定要注意 weex 模式的 px,和 vue 里的 px 逻辑不一样。)
- wx:与设备屏幕宽度无关的长度单位,与 vue 页面中的 px 理念相同
下面对 rpx
详细说明:
设计师在只提供一个分辨率的图,严格按设计图标注的 px 做开发,在不同宽度的手机上界面的宽度很容易变形。
rpx
动态宽度单位,就是为了解决这个问题。uni-app
在 App 端、H5 端都支持了 rpx
在Pages.json的globalStyle中可以配置不同屏幕宽度的计算方式,具体参考:
属性 | 默认值 | 描述 | 平台兼容 |
---|---|---|---|
rpxCalcMaxDeviceWidth | 960 | rpx 计算所支持的最大设备宽度,单位 px | App、H5 |
rpxCalcBaseDeviceWidth | 375 | rpx 计算使用的基准设备宽度,设备实际宽度超出 rpx 计算所支持的最大设备宽度时将按基准宽度计算,单位 px | App、H5 |
rpxCalcIncludeWidth | 750 | rpx 计算特殊处理的值,始终按实际的设备宽度计算,单位 rpx | App、H5 |
maxWidth | 1190 | 单位px,当浏览器可见区域宽度大于maxWidth时,两侧留白,当小于等于maxWidth时,页面铺满;不同页面支持配置不同的maxWidth;maxWidth = leftWindow(可选)+page(页面主体)+rightWindow(可选) | H5 |
rpx 是相对于基准宽度的单位,可以根据屏幕宽度进行自适应。uni-app
规定屏幕基准宽度 750rpx,页面元素宽度在 uni-app
中的宽度计算公式:
宽度rpx = 750 * 元素在设计稿中的宽度 / 设计稿基准宽度
比如: 若设计稿宽度为 750px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app
里面的宽度应该设为:750 * 100 / 750
,结果为:100rpx。
Tips
- 注意 rpx 是和宽度相关的单位,屏幕越宽,该值实际像素越大。如不想根据屏幕宽度缩放,则应该使用 px 单位。
- 如果开发者在字体或高度中也使用了 rpx ,那么需注意这样的写法意味着随着屏幕变宽,字体会变大、高度会变大。如果你需要固定高度,则应该使用 px 。
- rpx不支持动态横竖屏切换计算,使用rpx建议锁定屏幕方向
- 设计师可以用 iPhone6 作为视觉稿的标准。
- 如果设计稿不是750px,HBuilderX提供了自动换算的工具,详见:https://ask.dcloud.net.cn/article/35445。
- App端,在 pages.json 里的 titleNView 或页面里写的 plus api 中涉及的单位,只支持 px,不支持 rpx。
- 早期 uni-app 提供了 upx ,目前已经推荐统一改为 rpx 了,详见
样式导入
使用@import
语句可以导入外联样式表,@import
后跟需要导入的外联样式表的相对路径,用;
表示语句结束。
示例代码:
<style>
@import "../../common/uni.css";
.uni-card {
box-shadow: none;
}
</style>
全局样式与局部样式
定义在 App.vue 中的样式为全局样式,作用于每一个页面。在 pages 目录下 的 vue 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 App.vue 中相同的选择器。
注意:
- App.vue 中通过
@import
语句可以导入外联样式,一样作用于每一个页面。 - nvue页面暂不支持全局样式
CSS变量
uni-app 提供内置 CSS 变量
CSS变量 | 描述 | App | 小程序 | H5 |
---|---|---|---|---|
--status-bar-height | 系统状态栏高度 | 系统状态栏高度、nvue页面不支持 | 25px | 0 |
--window-top | 内容区域距离顶部的距离 | 0 | 0 | NavigationBar 的高度 |
--window-bottom | 内容区域距离底部的距离 | 0 | 0 | TabBar 的高度 |
注意:
-
var(--status-bar-height)
此变量在微信小程序环境为固定25px
,在 App 里为手机实际状态栏高度。 - 当设置
"navigationStyle":"custom"
取消原生导航栏后,由于窗体为沉浸式,占据了状态栏位置。此时可以使用一个高度为var(--status-bar-height)
的 view 放在页面顶部,避免页面内容出现在状态栏。 - 由于在H5端,不存在原生导航栏和tabbar,也是前端div模拟。如果设置了一个固定位置的居底view,在小程序和App端是在tabbar上方,但在H5端会与tabbar重叠。此时可使用
--window-bottom
,不管在哪个端,都是固定在tabbar上方。 - 目前 nvue 在App端,还不支持
--status-bar-height
变量,替代方案是在页面onLoad时通过uni.getSystemInfoSync().statusBarHeight获取状态栏高度,然后通过style绑定方式给占位view设定高度。下方提供了示例代码
代码块
快速书写css变量的方法是:在css中敲hei,在候选助手中即可看到3个css变量。(HBuilderX 1.9.6以上支持)
示例1 - 普通页面使用css变量:
<template>
<!-- HBuilderX 2.6.3+ 新增 page-meta, 详情:https://uniapp.dcloud.io/component/page-meta -->
<page-meta>
<navigation-bar />
</page-meta>
<view>
<view class="status_bar">
<!-- 这里是状态栏 -->
</view>
<view> 状态栏下的文字 </view>
</view>
</template>
<style>
.status_bar {
height: var(--status-bar-height);
width: 100%;
}
</style>
<template>
<view>
<view class="toTop">
<!-- 这里可以放一个向上箭头,它距离底部tabbar上浮10px-->
</view>
</view>
</template>
<style>
.toTop {
bottom: calc(var(--window-bottom) + 10px)
}
</style>
示例2 - nvue页面获取状态栏高度
<template>
<view class="content">
<view :style="{ height: iStatusBarHeight + 'px'}"></view>
</view>
</template>
<script>
export default {
data() {
return {
iStatusBarHeight:0
}
},
onLoad() {
this.iStatusBarHeight = uni.getSystemInfoSync().statusBarHeight
}
}
</script>
固定值
uni-app
中以下组件的高度是固定的,不可修改:
组件 | 描述 | App | H5 |
---|---|---|---|
NavigationBar | 导航栏 | 44px | 44px |
TabBar | 底部选项卡 | 50px(可以自主更改高度) | 50px |
各小程序平台,包括同小程序平台的iOS和Android的高度也不一样。
Flex布局
为支持跨平台,框架建议使用Flex布局,关于Flex布局可以参考外部文档阮一峰的flex教程等。
背景图片
uni-app
支持使用在 css 里设置背景图片,使用方式与普通 web
项目大体相同,但需要注意以下几点:
支持 base64 格式图片。
支持网络路径图片。
小程序不支持在css中使用本地文件,包括本地的背景图和字体文件。需以base64方式方可使用。App端在v3模式以前,也有相同限制。v3编译模式起支持直接使用本地背景图和字体。
-
使用本地路径背景图片需注意:
为方便开发者,在背景图片小于 40kb 时,
uni-app
编译到不支持本地背景图的平台时,会自动将其转化为 base64 格式;图片大于等于 40kb,会有性能问题,不建议使用太大的背景图,如开发者必须使用,则需自己将其转换为 base64 格式使用,或将其挪到服务器上,从网络地址引用。
-
本地背景图片的引用路径推荐使用以 ~@ 开头的绝对路径。
.test2 { background-image: url('~@/static/logo.png'); }
注意
- 微信小程序不支持相对路径(真机不支持,开发工具支持)
字体图标
uni-app
支持使用字体图标,使用方式与普通 web
项目相同,需要注意以下几点:
支持 base64 格式字体图标。
支持网络路径字体图标。
小程序不支持在css中使用本地文件,包括本地的背景图和字体文件。需以base64方式方可使用。App端在v3模式以前,也有相同限制。v3编译模式起支持直接使用本地背景图和字体。
网络路径必须加协议头
https
。从 http://www.iconfont.cn 上拷贝的代码,默认是没加协议头的。
从 http://www.iconfont.cn 上下载的字体文件,都是同名字体(字体名都叫iconfont,安装字体文件时可以看到),在nvue内使用时需要注意,此字体名重复可能会显示不正常,可以使用工具修改。
-
使用本地路径图标字体需注意:
为方便开发者,在字体文件小于 40kb 时,
uni-app
会自动将其转化为 base64 格式;字体文件大于等于 40kb,仍转换为 base64 方式使用的话可能有性能问题,如开发者必须使用,则需自己将其转换为 base64 格式使用,或将其挪到服务器上,从网络地址引用;
-
字体文件的引用路径推荐使用以 ~@ 开头的绝对路径。
@font-face { font-family: test1-icon; src: url('~@/static/iconfont.ttf'); }
nvue
中不可直接使用css的方式引入字体文件,需要使用以下方式在js内引入。nvue内不支持本地路径引入字体,请使用网络链接或者base64
形式。src
字段的url
的括号内一定要使用单引号。
var domModule = weex.requireModule('dom');
domModule.addRule('fontFace', {
'fontFamily': "fontFamilyName",
'src': "url('https://...')"
})
示例:
<template>
<view>
<view>
<text class="test"></text>
<text class="test"></text>
<text class="test"></text>
</view>
</view>
</template>
<style>
@font-face {
font-family: 'iconfont';
src: url('https://at.alicdn.com/t/font_865816_17gjspmmrkti.ttf') format('truetype');
}
.test {
font-family: iconfont;
margin-left: 20rpx;
}
</style>
nvue页面的样式注意事项
- nvue 页面控制显隐只可以使用
v-if
不可以使用v-show
- nvue 页面只能使用
flex
布局,不支持其他布局方式。页面开发前,首先想清楚这个页面的纵向内容有什么,哪些是要滚动的,然后每个纵向内容的横轴排布有什么,按 flex 布局设计好界面。 - nvue 页面的布局排列方向默认为竖排(
column
),如需改变布局方向,可以在manifest.json
->app-plus
->nvue
->flex-direction
节点下修改,仅在 uni-app 模式下生效。详情。 - nvue页面编译为H5、小程序时,会做一件css默认值对齐的工作。因为weex渲染引擎只支持flex,并且默认flex方向是垂直。而H5和小程序端,使用web渲染,默认不是flex,并且设置
display:flex
后,它的flex方向默认是水平而不是垂直的。所以nvue编译为H5、小程序时,会自动把页面默认布局设为flex、方向为垂直。当然开发者手动设置后会覆盖默认设置。 - 文字内容,必须、只能在
<text>
组件下。不能在<div>
、<view>
的text
区域里直接写文字。否则即使渲染了,也无法绑定js里的变量。 - 只有
text
标签可以设置字体大小,字体颜色。 - 布局不能使用百分比、没有媒体查询。
- nvue 切换横竖屏时可能导致样式出现问题,建议有 nvue 的页面锁定手机方向。
- 支持的css有限,不过并不影响布局出你需要的界面,
flex
还是非常强大的。详见 - 不支持背景图。但可以使用
image
组件和层级来实现类似web中的背景效果。因为原生开发本身也没有web这种背景图概念 - css选择器支持的比较少,只能使用 class 选择器。详见
- nvue 的各组件在安卓端默认是透明的,如果不设置
background-color
,可能会导致出现重影的问题。 -
class
进行绑定时只支持数组语法。 - Android端在一个页面内使用大量圆角边框会造成性能问题,尤其是多个角的样式还不一样的话更耗费性能。应避免这类使用。
- nvue页面没有
bounce
回弹效果,只有几个列表组件有bounce
效果,包括list
、recycle-list
、waterfall
。 - 原生开发没有页面滚动的概念,页面内容高过屏幕高度并不会自动滚动,只有部分组件可滚动(
list
、waterfall
、scroll-view/scroller
),要滚得内容需要套在可滚动组件下。这不符合前端开发的习惯,所以在 nvue 编译为 uni-app模式时,给页面外层自动套了一个scroller
,页面内容过高会自动滚动。(组件不会套,页面有recycle-list
时也不会套)。后续会提供配置,可以设置不自动套。 - 在 App.vue 中定义的全局js变量不会在 nvue 页面生效。
globalData
和vuex
是生效的。 - App.vue 中定义的全局css,对nvue和vue页面同时生效。如果全局css中有些css在nvue下不支持,编译时控制台会报警,建议把这些不支持的css包裹在条件编译里,
APP-PLUS-NVUE
- 不能在
style
中引入字体文件,nvue 中字体图标的使用参考:加载自定义字体。如果是本地字体,可以用plus.io
的API转换路径。 - 目前不支持在 nvue 页面使用
typescript/ts
。 - nvue 页面关闭原生导航栏时,想要模拟状态栏,可以参考文章。但是,仍然强烈建议在nvue页面使用原生导航栏。nvue的渲染速度再快,也没有原生导航栏快。原生排版引擎解析
json
绘制原生导航栏耗时很少,而解析nvue的js绘制整个页面的耗时要大的多,尤其在新页面进入动画期间,对于复杂页面,没有原生导航栏会在动画期间产生整个屏幕的白屏或闪屏。