Appearance
UniApp (Obsolete)
1. 工程
1.1. 目录结构
Text
┌ uniCloud -------- 云空间目录,阿里云为 uniCloud-aliyun,腾讯云为 uniCloud-tcb
├ components ------ 符合 vue 组件规范的 uni-app 组件目录
│ └ comp-a.vue ---- 可复用的 a 组件
├ hybrid ---------- App 端存放本地 html 文件的目录
├ platforms ------- 存放各平台专用页面的目录
├ pages ----------- 业务页面文件存放的目录
│ ├ index
│ │ └ index.vue --- index 页面
│ └ list
│ └ list.vue ---- list 页面
├ static ---------- 存放应用引用的本地静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此
├ uni_modules ----- 存放 uni_module 规范的插件
├ wxcomponents ---- 存放小程序组件的目录
├ main.js --------- Vue 初始化入口文件
├ App.vue --------- 应用配置,用来配置 App 全局样式以及监听应用生命周期
├ manifest.json --- 配置应用名称、appid、logo、版本等打包信息
├ pages.json ------ 配置页面路由、导航条、选项卡等页面类信息
└ uni.scss -------- 这里是 uni-app 内置的常用样式变量关于 static 目录:
- 编译到任意平台时,
static目录下的文件均会被完整打包进去,且不会编译。非static目录下的文件(vue、js、css 等)只有被引用到才会被打包编译进去。 static目录下的 js 文件不会被编译,如果里面有 ES6 的代码,不经过转换直接运行,在手机设备上会报错。- css、less/scss 等资源不要放在
static目录下,建议这些公用的资源放在自建的common目录下。
2. 页面
2.1. .vue 与 .nvue 文件
一个页面就是一个符合 Vue SFC 规范的 .vue 文件或 .nvue 文件。
.vue 页面和 .nvue 页面,均全平台支持,差异在于当 uni-app 发行到 App 平台时,.vue 文件会使用 webview 进行渲染,.nvue 会使用原生进行渲染,详见:nvue 原生渲染。
2.2. 页面生命周期
| 函数名 | 说明 | 平台差异说明 | 最低版本 |
|---|---|---|---|
| onInit | 监听页面初始化,其参数同 onLoad 参数,为上个页面传递的数据,参数类型为 Object(用于页面传参),触发时机早于 onLoad | 百度小程序 | 3.1.0+ |
| onLoad | 监听页面加载,其参数为上个页面传递的数据,参数类型为 Object(用于页面传参),参考示例 | ||
| onShow | 监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面 | ||
| onReady | 监听页面初次渲染完成。注意如果渲染速度快,会在页面进入动画完成前触发 | ||
| onHide | 监听页面隐藏 | ||
| onUnload | 监听页面卸载 | ||
| onResize | 监听窗口尺寸变化 | App、微信小程序、快手小程序 | |
| onPageScroll | 监听页面滚动,参数为 Object,具体见下方注意事项 | nvue 暂不支持 | |
| onPullDownRefresh | 监听用户下拉动作,一般用于下拉刷新,参考示例 | ||
| onReachBottom | 页面滚动到底部的事件(不是 scroll-view 滚到底),常用于下拉下一页数据。具体见下方注意事项 | ||
| onTabItemTap | 点击 tab 时触发,参数为 Object,具体见下方注意事项 | App、H5、微信小程序、QQ 小程序、支付宝小程序、快手小程序、百度小程序、京东小程序 | |
| onBackPress | 监听页面返回,参数为 Object,具体见下方注意事项。 详细说明及使用:onBackPress 详解 | App、H5、支付宝小程序 | |
| onNavigationBarButtonTap | 监听原生标题栏按钮点击事件,参数为 Object,具体见下方注意事项 | App、H5 | |
| onNavigationBarSearchInputChanged | 监听原生标题栏搜索输入框输入内容变化事件 | App、H5 | 1.6.0 |
| onNavigationBarSearchInputConfirmed | 监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的 “搜索” 按钮时触发 | App、H5 | 1.6.0 |
| onNavigationBarSearchInputClicked | 监听原生标题栏搜索输入框点击事件(pages.json 中的 searchInput 配置 disabled 为 true 时才会触发) | App、H5 | 1.6.0 |
| onShareAppMessage | 用户点击右上角分享 | 微信小程序、QQ 小程序、支付宝小程序、字节小程序、飞书小程序、快手小程序、京东小程序 | |
| onShareTimeline | 监听用户点击右上角转发到朋友圈 | 微信小程序 | 2.8.1+ |
| onAddToFavorites | 监听用户点击右上角收藏 | 微信小程序 | 2.8.1+ |
2.2.1. 关于 onInit
- 仅百度小程序基础库 3.260 以上支持
onInit生命周期 - 其他版本或平台可以同时使用
onLoad生命周期进行兼容,注意避免重复执行相同逻辑 - 不依赖页面传参的逻辑可以直接使用
created生命周期替代
2.2.2. 关于 onPageScroll
参数对象说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| scrollTop | Number | 页面在垂直方向已滚动的距离(单位 px) |
JavaScript
onPageScroll: function(e) { // nvue 暂不支持滚动监听,可用 bindingx 代替
console.log("滚动距离为:" + e.scrollTop);
},1
2
3
2
3
onPageScroll里不要写交互复杂的 js,比如频繁修改页面。因为这个生命周期是在渲染层触发的,在非 H5 端,js 是在逻辑层执行的,两层之间通信是有损耗的。如果在滚动过程中,频发触发两层之间的数据交换,可能会造成卡顿。- 在 App、微信小程序、H5 中,也可以使用
wxs监听滚动(参考);在 app-nvue 中,可以使用bindingx监听滚动(参考)。 - 如果想实现滚动时标题栏透明渐变,在 App 和 H5 下,可在
pages.json中配置titleNView下的type为transparent(参考)。 - 如果需要滚动吸顶固定某些元素,推荐使用 css 的粘性布局,参考 插件市场。插件市场也有其他 js 实现的吸顶插件,但性能不佳,需要时可自行搜索。
2.2.3. 关于 onReachBottom
- 可在
pages.json里定义具体页面底部的触发距离 onReachBottomDistance,比如设为50,那么滚动页面到距离底部50px时,就会触发onReachBottom事件。 - 如使用
scroll-view导致页面没有滚动,则触底事件不会被触发。scroll-view滚动到底部的事件请参考scroll-view的文档
2.2.4. 关于 onTabItemTap
参数对象说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| index | String | 被点击 tabItem 的序号,从 0 开始 |
| pagePath | String | 被点击 tabItem 的页面路径 |
| text | String | 被点击 tabItem 的按钮文字 |
JavaScript
onTabItemTap: function(e) {
console.log(e); // e 的返回格式为 json 对象:{"index":0,"text":"首页","pagePath":"pages/index/index"}
},1
2
3
2
3
onTabItemTap常用于点击当前tabItem,滚动或刷新当前页面。- 如果是点击不同的
tabItem,一定会触发页面切换。要是想在 App 端实现点击某个tabItem不跳转页面,不能使用onTabItemTap,可以使用plus.nativeObj.view放一个区块盖住原先的tabItem,并拦截点击事件。 - 支付宝小程序平台
onTabItemTap表现为点击非当前tabItem后触发,因此不能用于实现点击返回顶部这种操作。
2.2.5. 关于 onBackPress
onBackPress上不可使用async,会导致无法阻止默认返回
| 属性 | 类型 | 说明 |
|---|---|---|
| from | String | 触发返回行为的来源; “backbutton”:—— 左上角导航栏按钮及安卓返回键; “navigateBack”:navigateBack 方法引发的; 支付宝小程序只有真机能触发,且只能监听非 navigateBack 引起的返回,不可阻止默认行为; |
2.2.6. 关于 onNavigationBarButtonTap
参数对象说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| index | Number | 原生标题栏按钮数组的下标 |
JavaScript
onNavigationBarButtonTap: function (e) {
console.log(e); // e 的返回格式为 json 对象:{"text":"测试","index":0}
}1
2
3
2
3
2.3. 页面调用接口
2.3.1. getApp()
getApp() 函数用于获取当前应用实例,一般用于获取 globalData。
JavaScript
const app = getApp()
console.log(app.globalData)1
2
2
- 不要在
App()函数中,或调用App()前调用getApp(),可以通过this.$scope获取对应的 App 实例。 - 通过
getApp()获取实例之后,不要私自调用生命周期函数。 - 当在首页 nvue 中使用
getApp()不一定可以获取真正的 App 对象。对此提供了const app = getApp({allowDefault: true})用来获取原始的 App 对象,可以用来在首页对globalData等初始化。
2.3.2. getCurrentPages()
getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
提示
getCurrentPages() 仅用于展示页面栈的情况,请勿修改页面栈,以免造成页面状态错误。
每个 page 实例的方法属性列表:
| 方法 | 描述 | 平台说明 |
|---|---|---|
| page.$getAppWebview() | 获取当前页面的 webview 对象实例 | App |
| page.route | 获取当前页面的路由 |
navigateTo、redirectTo只能打开非tabBar页面。switchTab只能打开tabBar页面。reLaunch可以打开任意页面。- 页面底部的
tabBar由页面决定,即只要是定义为tabBar的页面,底部都有tabBar。 - 不能在
App.vue里面进行页面跳转。
2.3.3. $getAppWebview()
uni-app 在 getCurrentPages() 获得的 page 对象里内置了一个方法 $getAppWebview() 可以得到当前 webview 的对象实例,从而实现对 webview 更强大的控制。在 html5Plus 中,plus.webview 具有强大的控制能力,可参考:WebviewObject。
但 uni-app 框架有自己的窗口管理机制,请不要自己创建和销毁 webview,如有需求覆盖到子窗体上,请使用原生子窗体 subNvue。
提示
注意该方法仅在 App 中支持。
获取当前页面 webview 的对象实例:
JavaScript
export default {
...,
onLoad() {
// #ifdef APP-PLUS
const currentWebview = this.$scope.$getAppWebview(); // 此对象相当于 html5plus 里的 plus.webview.currentWebview(),在 uni-app 里 vue 页面直接使用 plus.webview.currentWebview() 无效
currentWebview.setBounce({position:{top:'100px'},changeoffset:{top:'0px'}}); // 动态重设 bounce 效果
// #endif
}
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
获取指定页面 webview 的对象实例:
JavaScript
export default {
...,
onLoad() {
var pages = getCurrentPages();
var page = pages[pages.length - 1];
// #ifdef APP-PLUS
var currentWebview = page.$getAppWebview();
console.log(currentWebview.id); // 获得当前 webview 的 id
console.log(currentWebview.isVisible()); // 查询当前 webview 是否可见
// #endif
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
uni-app 自带的 web-view 组件,是页面中新插入的一个子 webview。获取该对象的方法见:uni-app 中如何使用 5+ 的原生界面控件
2.4. 页面通讯
自 HBuilderX 2.0.0 起支持,使用指南:参见
2.4.1. uni.$emit(eventName, data)
触发全局的自定义事件,附加参数都会传给监听器回调。
| 属性 | 类型 | 描述 |
|---|---|---|
eventName | String | 事件名 |
data | Object | 触发事件携带的附加参数 |
JavaScript
uni.$emit('update', {msg:'页面更新'})2.4.2. uni.$on(eventName, callback)
监听全局的自定义事件。事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数。
| 属性 | 类型 | 描述 |
|---|---|---|
eventName | String | 事件名 |
callback | Function | 事件的回调函数 |
JavaScript
uni.$on('update', function(data) {
console.log('监听到事件来自 update,携带参数 msg 为:' + data.msg);
})1
2
3
2
3
2.4.3. uni.$off([eventName, callback])
移除全局自定义事件监听器。
| 属性 | 类型 | 描述 |
|---|---|---|
eventName | Array<String> | 事件名 |
callback | Function | 事件的回调函数 |
- 如果没有提供参数,则移除所有的事件监听器;
- 如果只提供了事件,则移除该事件所有的监听器;
- 如果同时提供了事件与回调,则只移除这个回调的监听器;
- 提供的回调必须跟
$on的回调为同一个才能移除这个回调的监听器;
2.4.4. 综合示例
$emit、$on、$off 常用于跨页面、跨组件通讯,这里为了方便演示放在同一个页面:
HTML
<template>
<view class="content">
<view class="data">
<text>{{val}}</text>
</view>
<button type="primary" @click="comunicationOff">结束监听</button>
</view>
</template>
<script>
export default {
data() {
return {
val: 0
}
},
onLoad() {
setInterval(() => {
uni.$emit('add', {
data: 2
})
}, 1000)
uni.$on('add', this.add)
},
methods: {
comunicationOff() {
uni.$off('add', this.add)
},
add(e) {
this.val += e.data
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.data {
text-align: center;
line-height: 40px;
margin-top: 40px;
}
button {
width: 200px;
margin: 20px 0;
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
uni.$emit、uni.$on、uni.$once、uni.$off触发的事件都是 App 全局级别的,跨任意组件、页面、nvue、vue 等- 使用时,注意及时销毁事件监听,比如,在
onLoad里边uni.$on注册监听,在onUnload里边uni.$off移除,针对于一次性的事件,直接使用uni.$once就可以了
2.5. 路由
uni-app 页面路由为框架统一管理,开发者需要在 pages.json 里配置每个路由页面的路径及页面样式。类似小程序在 app.json 中配置页面路由一样。所以 uni-app 的路由用法与 Vue Router 不同,如仍希望采用 Vue Router 方式管理路由,可在插件市场搜索 Vue-Router。
2.5.1. 路由跳转
uni-app 有两种页面路由跳转方式:使用 navigator 组件跳转、调用 API 跳转。
2.6. 页面栈
框架以栈的形式管理当前所有页面,当发生路由切换的时候,页面栈的表现如下:
| 路由方式 | 页面栈表现 | 触发时机 |
|---|---|---|
| 初始化 | 新页面入栈 | uni-app 打开的第一个页面; |
| 打开新页面 | 新页面入栈 | 调用 API uni.navigateTo; 使用组件 <navigator open-type="navigate"/>; |
| 页面重定向 | 当前页面出栈,新页面入栈 | 调用 API uni.redirectTo; 使用组件 <navigator open-type="redirectTo"/>; |
| 页面返回 | 页面不断出栈,直到目标返回页 | 调用 API uni.navigateBack; 使用组件 <navigator open-type="navigateBack"/>;用户按左上角返回按钮; 安卓用户点击物理 back 按键; |
| Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 | 调用 API uni.switchTab; 使用组件 <navigator open-type="switchTab"/>;用户切换 Tab; |
| 重加载 | 页面全部出栈,只留下新的页面 | 调用 API uni.reLaunch; 使用组件 <navigator open-type="reLaunch"/>; |
2.7. 页面代码规范
uni-app 支持在 template 模板中嵌套 <template/>,用来进行 列表渲染 和 条件渲染。<template/> 仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
HTML
<template>
<view>
<template v-if="test">
<view>test 为 true 时显示</view>
</template>
<template v-else>
<view>test 为 false 时显示</view>
</template>
</view>
</template>1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
2.8. 组件生命周期
uni-app 组件支持的生命周期,与 vue 标准组件的生命周期相同。不过这里没有页面级的 onLoad 等生命周期:
| 函数名 | 说明 | 平台差异说明 | 最低版本 |
|---|---|---|---|
| beforeCreate | 在实例初始化之前被调用(详见) | ||
| created | 在实例创建完成后被立即调用(详见) | ||
| beforeMount | 在挂载开始之前被调用(详见) | ||
| mounted | 挂载到实例上去之后调用(详见)。注意:此处并不能确定子组件被全部挂载,如果需要子组件完全挂载之后在执行操作可以使用 $nextTick Vue 官方文档 | ||
| beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前(详见) | 仅 H5 平台支持 | |
| updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子(详见) | 仅 H5 平台支持 | |
| beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用(详见) | ||
| destroyed | Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁(详见) |
3. 引用
3.1. 引用组件
传统 vue 项目开发,引用组件需要 “导入-注册-使用” 三个步骤:
HTML
<template>
<view>
<!-- 3. 使用组件 -->
<uni-rate text=""></uni-rate>
</view>
</template>
<script>
// 1. 导入组件
import uniRate from `@/components/uni-rate/uni-rate.vue`
export default {
// 2. 注册组件
components: {
uniRate
}
}
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在 Vue 3 中增加了 script setup 特性,将三步优化为两步,无需注册步骤,更为简洁:
HTML
<template>
<view>
<!-- 2. 使用组件 -->
<uni-rate text=""></uni-rate>
</view>
</template>
<script>
// 1. 导入组件
import uniRate from `@/components/uni-rate/uni-rate.vue`
</script>1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
基于 uni-app 的 easycom 机制,将组件引用进一步优化,开发者只管使用,无需考虑导入和注册,更为高效:
HTML
<template>
<view>
<!-- 1. 使用组件 -->
<uni-rate text=""></uni-rate>
</view>
</template>
<script>
</script>1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
easycom 规范详细介绍,参考:easycom
3.2. 引用 js
js 文件或 script 标签内(包括 renderjs 等)引入 js 文件时,均可使用相对路径和绝对路径:
JavaScript
// 绝对路径,@ 指向项目根目录,在 cli 项目中 @ 指向 src 目录
import add from '@/common/add.js';
// 相对路径
import add from '../../common/add.js';1
2
3
4
5
2
3
4
5
警告
js 文件不支持使用 / 开头的方式引入。
3.3. 引用 css
使用 @import 语句可以导入外联样式表,@import 后跟需要导入的外联样式表的相对路径,用 ; 表示语句结束。
HTML
<style>
@import "../../common/uni.css";
.uni-card {
box-shadow: none;
}
</style>1
2
3
4
5
6
7
2
3
4
5
6
7
3.4. 引用静态资源
3.4.1. template 内引入静态资源
HTML
<template>
<view>
<!-- 绝对路径,/static 指根目录下的 static 目录,在 cli 项目中 /static 指 src 目录下的 static 目录 -->
<image class="logo" src="/static/logo.png"></image>
<image class="logo" src="@/static/logo.png"></image>
<!-- 相对路径 -->
<image class="logo" src="../../static/logo.png"></image>
</view>
</template>1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
@开头的绝对路径以及相对路径会经过 base64 转换规则校验。- 在 H5 平台,小于 4kb 的资源会被转换成 base64。在非 H5 平台,均不转为 base64。
- 支付宝小程序组件内
image标签不可使用相对路径。
3.4.2. css 内引入静态资源
HTML
<style>
/* 绝对路径 */
@import url('/common/uni.css');
@import url('@/common/uni.css');
/* 相对路径 */
@import url('../../common/uni.css');
</style>1
2
3
4
5
6
7
2
3
4
5
6
7
需要注意的是,有些小程序端 css 文件不允许引用本地文件(请看注意事项):
CSS
/* 绝对路径 */
background-image: url(/static/logo.png);
background-image: url(@/static/logo.png);
/* 相对路径 */
background-image: url(../../static/logo.png);1
2
3
4
5
2
3
4
5
@开头的绝对路径以及相对路径会经过 base64 转换规则校验。- 在 H5 平台,小于 4kb 的资源会被转换成 base64。在非 H5 平台,均不转为 base64。
- 不支持本地图片的平台,小于 40kb,一定会转 base64。(共四个平台:mp-weixin、mp-qq、mp-toutiao、app v2)
- 引入字体图标请参考:字体图标
3.5. 引用原生插件
3.5.1. uni.requireNativePlugin(pluginName)
引入 App 原生插件。
JavaScript
const pluginName = uni.requireNativePlugin("pluginName");3.5.2. 内置原生插件
- 仅在 nvue 页面,支持引入
BindingX、animation、DOM.addRule等 - 在 vue 页面,支持引入
clipboard、storage、stream、deviceInfo等
HTML
<template>
<view>
<text class="my-iconfont"></text>
</view>
</template>
<script>
export default {
beforeCreate() {
const domModule = uni.requireNativePlugin('dom')
domModule.addRule('fontFace', {
'fontFamily': "myIconfont",
'src': "url('http://at.alicdn.com/t/font_2234252_v3hj1klw6k9.ttf')"
})
}
}
</script>
<style>
.my-iconfont {
font-family: myIconfont;
font-size: 60rpx;
color: #00AAFF;
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
3.5.3. 非内置原生插件
非内置原生插件,分为本地插件和云端插件。集成原生插件后,需要提交云端打包或制作自定义基座运行才会生效。
3.5.3.1. 本地插件
本地插件,是 uni-app 项目 nativeplugins 目录(目录不存在则创建)下的原生插件。
获取本地原生插件
方式一:插件市场下载免费 uni-app 原生插件
可以登录 uni-app 原生插件市场,在免费的插件详情页中点击 “下载 for 离线打包” 下载原生插件(zip 格式),解压到 HBuilderX 中 uni-app 项目下的
nativeplugins目录(如不存在则创建),以下是DCloud-RichAlert插件举例(下载地址),下载解压后目录结构如下:
方式二:开发者自己开发 uni-app 原生插件
原生插件开发完成后按指定格式压缩为 zip 包,参考 uni-app 原生插件格式说明文档。按上图的格式配置到 uni-app 项目下的
nativeplugins目录。
配置本地原生插件
在
manifest.json-> App 原生插件配置 -> 选择本地插件 -> 选择需要打包的插件 -> 保存后提交云端打包生效。
开发调试本地原生插件
在 vue 页面或 nvue 页面引入这个原生插件。使用
uni.requireNativePlugin的 api,参数为插件的 id。JavaScriptconst dcRichAlert = uni.requireNativePlugin('DCloud-RichAlert')打包发布
使用自定义基座开发调试本地原生插件后,不可直接将自定义基座 apk 作为正式版发布。应该重新提交云端打包(不能勾选 “自定义基座”)生成正式版本。
3.5.3.2. 云端插件
已经在插件市场绑定或购买的插件,无需下载插件到工程中,云打包时会直接合并打包原生插件到 APP 中。(试用插件只能在自定义基座中使用)
购买或下载云端原生插件
使用前需先登录 uni-app 原生插件市场,在插件详情页中购买,免费插件也可以在插件市场 0 元购。购买后才能够云端打包使用插件。
使用自定义基座打包 uni-app 原生插件(注:请使用真机运行自定义基座)
在
manifest.json-> App 原生插件配置 -> 选择云端插件 -> 选择需要打包的插件 -> 保存后提交云端打包生效。
开发调试云端原生插件
在 vue 页面或 nvue 页面引入这个原生插件。使用
uni.requireNativePlugin的 api,参数为插件的 id。JavaScriptconst dcRichAlert = uni.requireNativePlugin('DCloud-RichAlert')打包发布
使用自定义基座开发调试云端原生插件后,不可直接将自定义基座 apk 作为正式版发布。应该重新提交云端打包(不能勾选 “自定义基座”)生成正式版本。
3.5.4. 开发原生插件
- 如需开发 uni-app 原生插件请参考 uni-app 原生插件开发文档。
- 如果插件需要传递文件路径,则需要传手机文件的绝对路径,可使用 5+ IO 模块 的相关 API 得到文件的绝对路径。
4. js 语法
uni-app 的 js API 由标准 ECMAScript 的 js API 和 uni 扩展 API 这两部分组成。
标准 ECMAScript 的 js 仅是最基础的 js。浏览器基于它扩展了 window、document、navigator 等对象。小程序也基于标准 js 扩展了各种 wx.xx、my.xx、swan.xx 的 API。node 也扩展了 fs 等模块。
uni-app 基于 ECMAScript 扩展了 uni 对象,并且 API 命名与小程序保持兼容。
4.1. 标准 js 和浏览器 js 的区别
uni-app 的 js 代码,H5 端运行于浏览器中。非 H5 端(包含小程序和 App),Android 平台运行在 v8 引擎中,iOS 平台运行在 iOS 自带的 jscore 引擎中,都没有运行在浏览器或 webview 里。
非 H5 端,虽然不支持 window、document、navigator 等浏览器的 js API,但也支持标准 ECMAScript。
请注意不要把浏览器里的 js 扩展对象等价于标准 js。
所以 uni-app 的非 H5 端,一样支持标准 js,支持 if、for 等语法,支持字符串、数字、时间、布尔值、数组、自定义对象等变量类型及各种处理方法。仅仅是不支持 window、document、navigator 等浏览器专用对象。
4.2. ES6 支持
uni-app 在支持绝大部分 ES6 API 的同时,也支持了 ES7 的 await、async。
ES6 API 的支持,详见如下表格部分(“×” 表示不支持,无特殊说明则表示支持):
因为 iOS 上不允许三方 js 引擎,所以 iOS 上不区分 App、小程序、H5,各端均仅依赖 iOS 版本。
各端 Android 版本有差异:
| 类型 | 方法 | iOS | Android |
|---|---|---|---|
| Map | |||
| Promise | |||
| Proxy | × ≥ 9 | × > 5 | |
| Reflect | |||
| Set | |||
| Symbol | |||
| Array | Array.from | ||
| Array | Array.of | ||
| Array | copyWithin | ||
| Array | entries | ||
| Array | fill | ||
| Array | find | ||
| Array | findIndex | ||
| Array | includes | × ≥ 8 | |
| Array | keys | ||
| Array | values | × ≥ 8 | × |
| Math | acosh | ||
| Math | asinh | ||
| Math | atanh | ||
| Math | cbrt | ||
| Math | clz32 | ||
| Math | cosh | ||
| Math | expm1 | ||
| Math | fround | ||
| Math | hypot | ||
| Math | imul | ||
| Math | log10 | ||
| Math | log1p | ||
| Math | log2 | ||
| Math | sign | ||
| Math | sinh | ||
| Math | tanh | ||
| Math | trunc | ||
| Number | EPSILON | ||
| Number | isFinite | ||
| Number | isInteger | ||
| Number | isNaN | ||
| Number | isSafeInteger | ||
| Number | parseFloat | ||
| Number | parseInt | ||
| Object | assign | ||
| Object | getOwnPropertyDescriptor | ||
| Object | getOwnPropertyNames | ||
| Object | getOwnPropertySymbols | ||
| Object | is | ||
| Object | keys | ||
| String | codePointAt | ||
| String | endsWith | ||
| String | includes | ||
| String | normalize | × ≥ 9 | 仅支持 NFD/NFC |
| String | repeat | ||
| String | startsWith | ||
| String | String.fromCodePoint |
App 端 Android 支持不依赖 Android 版本号,即便是 Android4.4 也是上表数据。因为 uni-app 的 js 代码运行在自带的独立 jscore 中,没有 js 的浏览器兼容性问题。uni-app 的 vue 页面在 Android 低端机上只有 css 浏览器兼容性问题,因为 vue 页面仍然渲染在 webview 中,受 Android 版本影响,太新的 css 语法在低版本不支持。
默认不需要在微信工具里继续开启 ES6 转换。但如果用了微信的 wxml 自定义组件(wxcomponents 目录下),uni-app 编译器并不会处理这些文件中的 ES6 代码,需要去微信工具里开启转换。从 HBuilderX 调起微信工具时,如果发现工程下有 wxcomponents 目录会自动配置微信工程打开 ES6 转换。
5. css 语法
5.1. css 支持
uni-app 有 vue 页面和 nvue 页面。vue 页面是 webview 渲染的、app 端的 nvue 页面是原生渲染的。在 nvue 页面里样式比 web 会限制更多,另见:nvue 样式专项文档
5.2. 尺寸单位
vue 页面支持下面这些普通 H5 单位,但在 nvue 里不支持:
- rem 根字体大小可以通过 page-meta 配置
- vh viewpoint height,视窗高度,
1vh等于视窗高度的 1% - vw viewpoint width,视窗宽度,
1vw等于视窗宽度的 1%
uni-app 支持的通用 css 单位包括 px、rpx:
px即屏幕像素rpx即响应式px,一种根据屏幕宽度自适应的动态单位。以 750 宽的屏幕为基准,750rpx恰好为屏幕宽度。屏幕变宽,rpx 实际显示效果会等比放大,但在 App(vue2 不含 nvue)端和 H5(vue2)端屏幕宽度达到960px时,默认将按照375px的屏幕宽度进行计算,具体配置参考:rpx 计算配置。
weex 模式(nvue 不同编译模式介绍)目前遵循 weex 的单位,它的单位比较特殊:
px以 750 宽的屏幕为基准动态计算的长度单位,与 vue 页面中的rpx理念相同。(一定要注意 weex 模式的px,和 vue 里的px逻辑不一样。)wx与设备屏幕宽度无关的长度单位,与 vue 页面中的px理念相同。
- App 端,在
pages.json里的titleNView或页面里写的 plus api 中涉及的单位,只支持px不支持rpx。rpx不支持动态横竖屏切换计算,使用rpx建议锁定屏幕方向。
5.3. 选择器
目前支持的选择器有:
| 选择器 | 样例 | 样例描述 |
|---|---|---|
| .class | .intro | 选择所有拥有 class="intro" 的组件 |
| #id | #firstname | 选择拥有 id="firstname" 的组件 |
| element | view | 选择所有 view 组件 |
| element, element | view, checkbox | 选择所有文档所有的 view 和 checkbox 组件 |
| ::after | view::after | 在 view 组件后边插入内容,仅 vue 页面生效 |
| ::before | view::before | 在 view 组件前边插入内容,仅 vue 页面生效 |
在 uni-app 中不能使用
*选择器。微信小程序自定义组件中仅支持 class 选择器。
page相当于body节点例如:CSS<!-- 设置页面背景颜色,使用 scoped 会导致失效 --> page { background-color: #ccc; }1
2
3
4
5.4. 全局样式与局部样式
定义在 App.vue 中的样式为全局样式,作用于每一个页面。在 pages 目录下的 vue 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 App.vue 中相同的选择器。
App.vue中通过@import语句可以导入外联样式,一样作用于每一个页面。- nvue 页面暂不支持全局样式。
5.5. CSS 变量
uni-app 内置支持 CSS 变量
| CSS 变量 | 描述 | App | 小程序 | H5 |
|---|---|---|---|---|
| --status-bar-height | 系统状态栏高度 | 系统状态栏高度、nvue 注意见下 | 25px | 0 |
| --window-top | 内容区域距离顶部的距离 | 0 | 0 | NavigationBar 的高度 |
| --window-bottom | 内容区域距离底部的距离 | 0 | 0 | TabBar 的高度 |
--status-bar-height此变量在微信小程序环境为固定25px,在 App 里为手机实际状态栏高度。- 当设置
"navigationStyle":"custom"取消原生导航栏后,由于窗体为沉浸式,占据了状态栏位置。此时可以使用一个高度为--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
.status_bar {
height: var(--status-bar-height);
width: 100%;
}1
2
3
4
2
3
4
nvue 页面获取状态栏高度:
HTML
<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>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
5.6. 背景图片
uni-app 支持使用在 css 里设置背景图片,使用方式与普通 web 项目大体相同,但需要注意以下几点:
- 支持 base64 格式图片和网络路径图片。
- 小程序不支持在 css 中使用本地文件,包括本地的背景图和字体文件(可以转为 base64 格式图片使用)。
- 使用本地路径背景图片需注意:
- 在背景图片小于 40kb 时,uni-app 编译到不支持本地背景图的平台时,会自动将其转化为 base64 格式。
- 图片大于等于 40kb 时,base64 会有性能问题(建议将其挪到服务器上,从网络地址引用)。
- 本地背景图片的引用路径推荐使用以
~@开头的绝对路径。
CSS
.test2 {
background-image: url('~@/static/logo.png');
}1
2
3
2
3
5.7. 字体图标
uni-app 支持使用字体图标,使用方式与普通 web 项目大体相同,但需要注意以下几点:
- 支持 base64 格式字体图标和网络路径字体图标。
- 小程序不支持在 css 中使用本地文件,包括本地的背景图和字体文件(可以转为 base64 格式图片使用)。
- 网络路径必须为 https 协议头。
- 从 www.iconfont.cn 上拷贝的代码,默认是没加协议头的。
- 从 www.iconfont.cn 上下载的字体文件,都是同名字体(字体名都叫 iconfont,安装字体文件时可以看到),在 nvue 内使用时需要注意,此字体名重复可能会显示不正常,可以使用工具修改。
- 使用本地路径图标字体需注意:
- 在字体文件小于 40kb 时,uni-app 会自动将其转化为 base64 格式。
- 字体文件大于等于 40kb 时,base64 会有性能问题(建议将其挪到服务器上,从网络地址引用)。
- 字体文件的引用路径推荐使用以
~@开头的绝对路径。
HTML
<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>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在 nvue 中使用字体文件时需要注意:
- nvue 中不支持直接使用 css 的方式引入字体文件,需要在 js 内引入(参见下方示例)。
- nvue 内不支持本地路径引入字体,请使用网络链接或者 base64 形式。
- src 字段的 url 的括号内一定要使用单引号。
JavaScript
var domModule = weex.requireModule('dom');
domModule.addRule('fontFace', {
fontFamily: 'fontFamilyName',
src: "url('https://...')",
});1
2
3
4
5
2
3
4
5
6. vue 语法
6.1. vue3
6.1.1. 基础部分差异
相比 Web 平台,Vue.js 在 uni-app 中使用差异主要集中在两个方面:
一些详细的注意事项:
- vue3 响应式基于 Proxy 实现,不支持 iOS9 和 ie11。
- 暂不支持新增的
Teleport、Suspense组件。 - 暂不支持 template 下存在多个根节点。
- 在 H5 平台使用
v-for循环整数时和其他平台存在差异,如v-for="(item, index) in 10"中,在 H5 平台item从1开始,其他平台item从0开始,可使用第二个参数index来保持一致。 - 在非 H5 平台循环对象时不支持第三个参数,如
v-for="(value, name, index) in object"中,index参数是不支持的。 - 小程序端数据为差量更新方式,由于小程序不支持删除对象属性,加上目前 uni-app 对该情况的处理方式是简单地将该属性值赋值为
null,所以导致遍历对象时可能出现不符合预期的情况,需要自行过滤一下值为null的数据(相关反馈)。 - 为兼容各端,事件需使用
@的方式绑定,请勿使用小程序端的bind和catch进行事件绑定;也不能在 JS 中使用event.preventDefault()和event.stopPropagation()方法。.stop: 各平台均支持,使用时会阻止事件冒泡,在非 H5 端同时也会阻止事件的默认行为.prevent: 仅在 H5 平台支持.capture: 仅在 H5 平台支持.self: 仅在 H5 平台支持.once: 仅在 H5 平台支持.passive: 仅在 H5 平台支持
- 按键修饰符:uni-app 运行在手机端,没有键盘事件,所以不支持按键修饰符。
- 若需要禁止蒙版下的页面滚动,可使用
@touchmove.stop.prevent="moveHandle",moveHandle可以用来处理touchmove的事件,也可以是一个空函数。 - nvue 页面不支持
v-show
7. App 专题
7.1. nvue
7.1.1. 使用场景
建议使用 vue 页面为主,在 App 端某些 vue 页面表现不佳的场景下使用 nvue 作为强化补充:
- 需要高性能的区域长列表或瀑布流滚动。webview 的页面级长列表滚动是没有性能问题的(就是滚动条覆盖 webview 整体高度),但页面中某个区域做长列表滚动,则需要使用 nvue 的
list、recycle-list、waterfall等组件(详见)。这些组件的性能要高于 vue 页面里的区域滚动组件scroll-view。 - 复杂高性能的自定义下拉刷新。uni-app 的
pages.json里可以配置原生下拉刷新,但引擎内置的下拉刷新样式只有雪花和圆圈 2 种样式。如果你需要自己做复杂的下拉刷新,推荐使用 nvue 的refresh组件。当然 插件市场 里也有很多 vue 下的自定义下拉刷新插件,只要是基于 renderjs 或 wxs 的,性能也可以商用,只是没有 nvue 的refresh组件更极致。 - 左右拖动的长列表。在 webview 里,通过
swiper+scroll-view实现左右拖动的长列表,前端模拟下拉刷新,这套方案的性能不好。此时推荐使用 nvue,比如新建 uni-app 项目时的 新闻示例模板,就采用了 nvue,切换很流畅。 - 实现区域滚动长列表 + 左右拖动列表 + 吸顶的复杂排版效果,效果可参考 hello uni-app 模板里的
swiper-list(详见)。 - 如需要将软键盘右下角按钮文字改为 “发送”,则需要使用 nvue。比如聊天场景,除了软键盘右下角的按钮文字处理外,还涉及聊天记录区域长列表滚动,适合 nvue 来做。
- 解决前端控件无法覆盖原生控件的层级问题。当你使用
map、video、live-pusher等原生组件时,会发现前端写的 view 等组件无法覆盖原生组件,层级问题处理比较麻烦,此时使用 nvue 更好。或者在 vue 页面上也可以覆盖一个subnvue(一种非全屏的 nvue 页面覆盖在 webview 上),以解决 App 上的原生控件层级问题(详见)。 - 如深度使用
map组件,建议使用 nvue。除了层级问题,App 端 nvue 文件的map功能更完善,和小程序拉齐度更高,多端一致性更好。 - 如深度使用
video,建议使用 nvue。比如如下 2 个场景:video内嵌到swiper中,以实现抖音式视频滑动切换,例子见 插件市场;nvue 的视频全屏后,通过cover-view实现内容覆盖,比如增加文字标题、分享按钮。 - 直播推流:nvue 下有
live-pusher组件,和小程序对齐,功能更完善,也没有层级问题。 - 对 App 启动速度要求极致化。App 端如果首页使用 nvue 且在 manifest 里配置 fast 模式,那么 App 的启动速度可以控制在 1 秒左右。而使用 vue 页面的话,App 的启动速度一般是 3 秒起,取决于你的代码性能和体积。
但在某些场景下,nvue 不如 vue:
- nvue 的 canvas 性能不高,尤其是 Android App 平台,所以这个组件干脆没有内置,而是需要单独引入。操作 canvas 动画,最高性能的方式是使用 vue 页面的 renderjs 技术,在 hello uni-app 里的 canvas 示例就是如此。
- 动态横竖屏。nvue 页面的 css 不支持媒体查询,所以横竖屏动态切换、动态适配屏幕是很困难的。
7.1.2. vue 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 包裹在 条件编译 里。- 不能在 style 中引入字体文件,nvue 中字体图标的使用参考:加载自定义字体。如果是本地字体,可以用
plus.io的 API 转换路径。 - 目前不支持在 nvue 页面使用
typescript/ts。 - nvue 页面关闭原生导航栏时,想要模拟状态栏,可以参考 文章。但是,仍然强烈建议在 nvue 页面使用原生导航栏。nvue 的渲染速度再快,也没有原生导航栏快。原生排版引擎解析 json 绘制原生导航栏耗时很少,而解析 nvue 的 js 绘制整个页面的耗时要大的多,尤其在新页面进入动画期间,对于复杂页面,没有原生导航栏会在动画期间产生整个屏幕的白屏或闪屏。
7.1.3. vue nvue 同名文件
- 如果一个页面路由下出现同名的 vue 和 nvue 文件。那么在 App 端,会仅使用 nvue 页面,同名的 vue 文件将不会被编译到 App 端。而在非 App 端,会优先使用 vue 页面。
- 如果不同名,只有 nvue 页面,则在非 App 端,只有 uni-app 编译模式的 nvue 文件才会编译。
7.1.4. 详细介绍
nvue 的组件和 API 写法与 vue 页面一致。nvue 页面结构同 vue, 由 template、style、script 构成。更多内容:详见。