14 App相关
nvue原生渲染
概述
uni-app App 端内置了一个基于 weex 改进的原生渲染引擎,提供了原生渲染能力。
在 App 端,如果使用 vue 页面,则使用 webview 渲染;如果使用 nvue 页面(native vue 的缩写),则使用原生渲染。一个 App 中可以同时使用两种页面,比如首页使用 nvue,二级页使用 vue 页面,hello uni-app 示例就是如此。
虽然 nvue 也可以多端编译,输出 H5 和小程序,但 nvue 的 css 写法受限,所以如果你不开发 App,那么不需要使用 nvue。
以往的 weex ,有个很大的问题是它只是一个高性能的渲染器,没有足够的 API 能力(比如各种 push sdk 集成、蓝牙等能力调用),使得开发时非常依赖原生工程师协作,开发者本来想节约成本,结果需要前端、iOS、Android 3 拨人开发,适得其反。 nvue 解决了这个问题,让前端工程师可以直接开发完整 App,并提供丰富的插件生态和云打包。这些组合方案,帮助开发者切实的提高效率、降低成本。
同时uni-app扩展了 weex 原生渲染引擎的很多排版能力,修复了很多 bug。比如
- Android 端良好支持边框阴影,详情 (opens new window)
- iOS 端支持高斯模糊,详情 (opens new window)
- 可实现区域滚动长列表+左右拖动列表+吸顶的复杂排版效果
- 优化圆角边框绘制性能
- 扩展了更多的 css
适用场景
nvue 的组件和 API 写法与 vue 页面一致,其内置组件还比 vue 页面内置组件增加了更多,详见 (opens new window)。
如果你熟悉 weex 或 react native 开发,那么 nvue 是你的更优选择,能切实提升你的开发效率,降低成本。
如果你是 web 前端,不熟悉原生排版,那么建议你仍然以使用 vue 页面为主,在 App 端某些 vue 页面表现不佳的场景下使用 nvue 作为强化补充。这些场景如下:
- 需要高性能的区域长列表或瀑布流滚动。webview 的页面级长列表滚动是没有性能问题的(就是滚动条覆盖 webview 整体高度),但页面中某个区域做长列表滚动,则需要使用 nvue 的
list、recycle-list、waterfall等组件(详见 (opens new window))。这些组件的性能要高于 vue 页面里的区域滚动组件scroll-view。 - 复杂高性能的自定义下拉刷新。uni-app 的 pages.json 里可以配置原生下拉刷新,但引擎内置的下拉刷新样式只有雪花和 circle 圈 2 种样式。如果你需要自己做复杂的下拉刷新,推荐使用 nvue 的 refresh 组件。当然插件市场 (opens new window)里也有很多 vue 下的自定义下拉刷新插件,只要是基于 renderjs 或 wxs 的,性能也可以商用,只是没有 nvue 的
refresh组件更极致。 - 左右拖动的长列表。在 webview 里,通过
swiper+scroll-view实现左右拖动的长列表,前端模拟下拉刷新,这套方案的性能不好。此时推荐使用 nvue,比如新建 uni-app 项目时的新闻示例模板 (opens new window),就采用了 nvue,切换很流畅。 - 实现区域滚动长列表+左右拖动列表+吸顶的复杂排版效果,效果可参考 hello uni-app 模板里的
swiper-list。详见 (opens new window) - 如需要将软键盘右下角按钮文字改为“发送”,则需要使用 nvue。比如聊天场景,除了软键盘右下角的按钮文字处理外,还涉及聊天记录区域长列表滚动,适合 nvue 来做。
- 解决前端控件无法覆盖原生控件的层级问题。当你使用
map、video、live-pusher等原生组件时,会发现前端写的view等组件无法覆盖原生组件,层级问题处理比较麻烦,此时使用 nvue 更好。或者在 vue 页面上也可以覆盖一个 subnvue(一种非全屏的 nvue 页面覆盖在 webview 上),以解决 App 上的原生控件层级问题。详见 (opens new window) - 如深度使用
map组件,建议使用 nvue。除了层级问题,App 端 nvue 文件的 map 功能更完善,和小程序拉齐度更高,多端一致性更好。 - 如深度使用
video,建议使用 nvue。比如如下 2 个场景:video 内嵌到 swiper 中,以实现抖音式视频滑动切换,例子见插件市场 (opens new window);nvue 的视频全屏后,通过cover-view实现内容覆盖,比如增加文字标题、分享按钮。 - 直播推流:nvue 下有
live-pusher组件,和小程序对齐,功能更完善,也没有层级问题。 - 对 App 启动速度要求极致化。App 端如果首页使用 nvue 且在 manifest 里配置 fast 模式,那么 App 的启动速度可以控制在 1 秒左右。而使用 vue 页面的话,App 的启动速度一般是 3 秒起,取决于你的代码性能和体积。
但注意,在某些场景下,nvue 不如 vue 页面,如下:
canvas。nvue 的 canvas 性能不高,尤其是 Android App 平台,所以这个组件干脆没有内置,而是需要单独引入。操作 canvas 动画,最高性能的方式是使用 vue 页面的 renderjs 技术,在 hello uni-app 里的 canvas 示例就是如此。- 动态横竖屏。nvue 页面的 css 不支持媒体查询,所以横竖屏动态切换、动态适配屏幕是很困难的
纯原生渲染模式
uni-app 在 App 端,支持 vue 页面和 nvue 页面混搭、互相跳转。也支持纯 nvue 原生渲染。
启用纯原生渲染模式,可以减少 App 端的包体积、减少使用时的内存占用。因为 webview 渲染模式的相关模块将被移除。
在 manifest.json 源码视图的"app-plus"下配置"renderer":"native",即代表 App 端启用纯原生渲染模式。此时 pages.json 注册的 vue 页面将被忽略,vue 组件也将被原生渲染引擎来渲染。
如果不指定该值,默认是不启动纯原生渲染的。
// manifest.json
{
// ...
// App平台特有配置
"app-plus": {
"renderer": "native", //App端纯原生渲染模式
}
}
编译模式
weex 编译模式和 uni-app 编译模式
如你之前是 weex 开发者,可以继续查阅本章节,否则可以跳过看下一节[快速上手 (opens new window)](https://uniapp.dcloud.net.cn/tutorial/nvue- outline.html#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B)。
weex 的组件和 JS API,与 uni-app 不同。uni-app 与微信小程序相同。
考虑到 weex 用户的迁移,uni-app 也支持 weex 的代码写法。在 manifest.json 中可以配置使用weex 编译模式 或uni-app 编译模式 。选择 weex 编译模式时将不支持 uni-app 的组件和 jsapi,需要开发者参考 weex 官方文档的写法来写代码。 比如 weex 编译模式用<div>。uni-app 编译模式则使用<view>。
一般情况建议使用uni-app模式,除非历史 weex 代码较多,需要逐步过渡。同时注意 weex 编译模式的切换是项目级的,不支持同项目下某个 nvue 页面使用 weex 模式,另一个 nvue 页面使用 uni-app 模式。
weex 编译模式 | uni-app 编译模式 |
---|---|---
平台 | 仅 App | 所有端,包含小程序和 H5
组件 | weex 组件如 div | uni-app 组件如 view
生命周期 | 只支持 weex 生命周期 | 支持所有 uni-app 生命周期
JS API | weex API、uni API、Plus API | weex API、uni API、Plus API
单位 | 750px 是屏幕宽度,wx 是固定像素单位 | 750rpx 是屏幕宽度,px 是固定像素单位
全局样式 | 手动引入 | app.vue 的样式即为全局样式
页面滚动 | 必须给页面套或组件 | 默认支持页面滚动
在 manifest.json 中修改 2 种编译模式,manifest.json -> app-plus -> nvueCompiler 切换编译模式。
nvueCompiler 有两个值:
- weex
- uni-app
// manifest.json
{
// ...
// App平台特有配置
"app-plus": {
"nvueCompiler":"uni-app" //是否启用 uni-app 模式
}
}
在 manifest.json 配置文件中,HBuilderX2.4 之前版本,默认值为 weex 模式,HBuilderX2.4+版本默认值改为 uni-app 模式。
weex 编译模式不支持 onNavigationBarButtonTap 生命周期函数的写法。在 nvue 中监听原生标题栏按钮点击事件,详见:uni.onNavigationBarButtonTap (opens new window)。
weex 编译模式不支持 onShow 生命周期,但熟悉 5+的话,可利用监听 webview 的addEventListener show 事件实现 onShow 效果。
weex 编译模式不支持vuex。
nvue 的页面跳转,与 weex 不同,仍然遵循 uni-app 的路由模型。vue 页面和 nvue 页面之间不管怎么跳转,都遵循这个模型。包括 nvue 页面跳向 nvue 页面。每个页面都需要在 pages.json 中注册,调用 uni-app 的 路由 API (opens new window) 进行跳转。
原生开发没有页面滚动的概念,页面内容高过屏幕高度时,内容并不会自动滚动;只有将页面内容放在list、waterfall、scroll- view/scroller这几个组件下内容才可滚动。这不符合前端开发的习惯,所以在 nvue 编译为 uni-app模式时,uni-app框架会给 nvue 页面外层自动嵌套一个 scroller,从而实现页面内容的自动滚动。
注意:
uni-app框架仅对 nvue 页面嵌套scroller容器,不会给组件自动套scroller容器;- 若 nvue 页面有
recycle-list组件时,uni-app框架也不会自动给页面嵌套scroller容器 - 若你不希望自动嵌套
scroller容器,可在pages.json中通过如下配置进行关闭:
{
"path": "",
"style": {
"disableScroll": true // 不嵌套 scroller
}
}
weex 编译模式下支持使用 weex ui ,例子详见 (opens new window)。但相比 uni-app 插件市场及官方uni ui (opens new window)而言,weex 语法的组件生态还是比较欠缺的。
HBuilderX 3.1.0+ 开始支持新的样式编译模式
- weex 编译模式:老模式,样式支持与普通 weex 相同
- uni-app 编译模式:新模式,在 weex 原有样式基础上支持组合选择器(相邻兄弟选择器、普通兄弟选择器、子选择器、后代选择器)详见 (opens new window)
// manifest.json
{
// ...
// App平台特有配置
"app-plus": {
"nvueStyleCompiler": "uni-app"
}
}
快速上手
1.新建 nvue 页面
在 HBuilderX 的 uni-app 项目中,新建页面,弹出界面右上角可以选择是建立vue页面还是nvue页面,或者 2 个同时建。
不管是 vue 页面还是 nvue 页面,都需要在pages.json中注册。如果在 HBuilderX 中新建页面是会自动注册的,如果使用其他编辑器,则需要自行在 pages.json 里注册。
如果一个页面路由下同时有 vue 页面和 nvue 页面,即出现同名的 vue 和 nvue 文件。那么在 App 端,会仅使用 nvue 页面,同名的 vue 文件将不会被编译到 App 端。而在非 App 端,会优先使用 vue 页面。
如果不同名,只有 nvue 页面,则在非 app 端,只有 uni-app 编译模式的 nvue 文件才会编译
2.开发 nvue 页面
nvue 页面结构同 vue, 由 template、style、script 构成。
- template: 模板写法、数据绑定同 vue。组件支持 2 种模式,
- weex 组件,同 weex 写法,参考:weex 内置组件 (opens new window);
- uni-app 组件,同 uni-app 写法。
- App 端 nvue 专用组件,详见https://uniapp.dcloud.io/component/barcode。
- style:由于采用原生渲染,并非所有浏览器的 css 均支持,布局模型只支持 flex 布局 ,虽然不会造成某些界面布局无法实现,但写法要注意。详见:样式 (opens new window)
- script:写法同 vue,并支持 3 种 API:
- nvue API :仅支持 App 端,uni-app 编译模式也可使用。使用前需先引入对应模块,参考:模块引入 API (opens new window)
- uni API:https://uniapp.dcloud.io/api/README
- plus API:仅支持 App 端。http://www.html5plus.org/doc/h5p.html
3. 调试 nvue 页面
HBuilderX 内置了 weex 调试工具的强化版,包括审查界面元素、看 log、debug 打断点,详见 (opens new window)
nvue开发与vue开发的常见区别
基于原生引擎的渲染,虽然还是前端技术栈,但和web开发肯定是有区别的。
- nvue 页面控制显隐只可以使用
v-if不可以使用v-show - nvue 页面只能使用
flex布局,不支持其他布局方式。页面开发前,首先想清楚这个页面的纵向内容有什么,哪些是要滚动的,然后每个纵向内容的横轴排布有什么,按 flex 布局设计好界面。 - nvue 页面的布局排列方向默认为竖排(
column),如需改变布局方向,可以在manifest.json->app-plus->nvue->flex-direction节点下修改,仅在 uni-app 模式下生效。详情 (opens new window)。 - 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还是非常强大的。详见 (opens new window) - 不支持背景图。但可以使用
image组件和层级来实现类似web中的背景效果。因为原生开发本身也没有web这种背景图概念 - css选择器支持的比较少,只能使用 class 选择器。详见 (opens new window)
- 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包裹在条件编译 (opens new window)里,
APP-PLUS-NVUE - 不能在
style中引入字体文件,nvue 中字体图标的使用参考:加载自定义字体 (opens new window)。如果是本地字体,可以用plus.io的API转换路径。 - 目前不支持在 nvue 页面使用
typescript/ts。 - nvue 页面关闭原生导航栏时,想要模拟状态栏,可以参考文章 (opens new window)。但是,仍然强烈建议在nvue页面使用原生导航栏。nvue的渲染速度再快,也没有原生导航栏快。原生排版引擎解析
json绘制原生导航栏耗时很少,而解析nvue的js绘制整个页面的耗时要大的多,尤其在新页面进入动画期间,对于复杂页面,没有原生导航栏会在动画期间产生整个屏幕的白屏或闪屏
iOS 平台下拉组件 refresh 组件注意问题
iOS 平台默认情况下滚动容器组件(如list、waterfall组件)内容不足时,由于没有撑满容器的可视区域会导致无法上下滚动,此时无法操作下拉刷新功能,无法触发refresh组件的@refresh、@pullingdown事件。 此时可在容器组件中配置alwaysScrollableVertical属性值为true来设置支持上下滚动,支持下拉刷新操作。
用法
<list class="scroll-v list" enableBackToTop="true" scroll-y alwaysScrollableVertical="true">
<refresh class="refresh" @refresh="onrefresh()" @pullingdown="onpullingdown">
<!-- refresh content -->
</refresh>
<cell v-for="(newsitem,index) in list" :key="newsitem.id">
<!-- cell content -->
</cell>
</list>
Android 平台不存在此问题
样式
注意事项
- nvue的css仅支持flex布局 ,是webview的css语法的子集。这是因为操作系统原生排版不支持非flex之外的web布局。当然flex足以排布出各种页面,只是写法需要适应。
- class 进行绑定时只支持数组语法。
- 不支持媒体查询
- 不能在 style 中引入字体文件
- 不能使用百分比布局,如
width:100% - 不支持在css里写背景图
background-image,但可以使用image组件和层级来实现类似web中的背景效果。因为原生开发本身也没有web这种背景图概念 - 使用
image标签,支持使用base64,不支持svg格式图片 - nvue 的各组件在安卓端默认是透明的,如果不设置
background-color,可能会导致出现重影的问题 - 文字内容,必须只能在
text组件下,text组件不能换行写内容,否则会出现无法去除的周边空白 - 只有
text标签可以设置字体大小,字体颜色
盒模型
nvue盒模型基于 CSS 盒模型,每个 nvue 元素都可视作一个盒子。我们一般在讨论设计或布局时,会提到「盒模型」这个概念。
盒模型描述了一个元素所占用的空间。每一个盒子有四条边界:外边距边界 margin edge, 边框边界 border edge, 内边距边界 padding edge 与内容边界 content edge。这四层边界,形成一层层的盒子包裹起来,这就是盒模型大体上的含义。

nvue盒模型的
box-sizing默认为border-box,即盒子的宽高包含内容、内边距和边框的宽度,不包含外边距的宽度。
在 Android 平台,nvue只支持
overflow:hidden。
在 iOS 上,nvue支持
overflow:hidden和overflow:visible,默认是overflow:visible。
Flexbox
flexbox 属性均不支持 :如 order、flex-grow 、flex-shrink 、 flex- basis、align-content、align-self 等
在 nvue中,Flexbox 是默认且唯一的布局模型,所以你不需要手动为元素添加display: flex; 属性。
Flex 成员项暂不支持flex-shrink 、 flex-basis、align-content 属性。
该属性不支持 flex: flex-grow | flex-shrink | flex-basis 的简写
position 定位
Android 兼容性
如果定位元素超过容器边界,在 Android 下,超出部分将不可见,原因在于 Android 端元素 overflow 默认值为 hidden,但目前 Android 暂不支持设置 overflow: visible
伪类
| 参数名 | 描述 |
|---|---|
| active | 所有组件都支持 |
| focus | 只有 input 组件和 textarea 组件支持 |
| disabled | 只有 input 组件和 textarea 组件支持 |
| enabled | 只有 input 组件和 textarea 组件支持 |
注意
同时生效的时候,优先级高覆盖优先级低。 例如:
input:active:enabled和input:active同时生效,前者覆盖后者
线性渐变
所有组件均支持线性渐变。CSS3 渐变 (opens new window) 你可以通过 background- image属性创建线性渐变。
background-image:linear-gradient(to bottom right,#AD18F9,#05DFC7);
只支持两种颜色的渐变,渐变方向如下:
| 渐变方向 | 描述 |
|---|---|
| to right | 从左向右渐变 |
| to left | 从右向左渐变 |
| to bottom | 从上到下渐变 |
| to top | 从下到上渐变 |
| to bottom right | 从左上角到右下角 |
| to top left | 从右下角到左上角 |
注意
background-image优先级高于background-color,这意味着同时设置background-image和background-color,background-color被覆盖。background不支持简写。目前暂不支持 radial-gradient(径向渐变)。

阴影box-shadow
- Android平台 设置
box-shadow的组件需要让出阴影渲染位置,否则会出现阴影显示不全的问题。
<template>
<view class="wrapper">
<view>
<view class="box">
<text class="title" style="text-align: center">Hello World</text>
</view>
</view>
</view>
</template>
<style>
.box {
width: 300px;
height: 100px;
background-color: #FFE4C4;
border-radius: 10px;
box-shadow: 3px 5px 2px rgb(255, 69, 0);
margin: 10px;//让出阴影位置
}
</style>
文本样式
color {color}:文字颜色,支持如下字段:
- RGB( rgb(255, 0, 0) )
- RGBA( rgba(255, 0, 0, 0.5) )
- 十六进制( #ff0000 );
- 精简写法的十六进制( #f00 )
- 色值关键字(red)
只有
text标签可以设置字体颜色
font-size
font-size {number}:文字大小,只有text标签可以设置字体大小
font-weight
font-weight {string}:字体粗细程度。默认值: normal;
- 可选值:
normal,bold, 100, 200, 300, 400, 500, 600, 700, 800, 900 normal等同于 400,bold等同于 700;- iOS 支持 9 种
font-weight值;Android 仅支持 400 和 700, 其他值会设为 400 或 700 - 类似
lighter,bolder这样的值暂时不支持
text-overflow
text-overflow {string}:设置内容超长时的省略样式。
| 可选值 | 描述 |
|---|---|
| clip | 修剪文本 |
| ellipsis | 显示省略符号来代表被修剪的文本 |
只支持
text和richtext
API
DOM
对于那些不依赖 UI 交互的原生功能,nvue将其封装成模块,这是一种通过 javascript 调用原生能力的方法。
- uni-app默认内置集成原生模块,如:BindingX,animation, DOM.addRule等。 通过
uni.requireNativePlugin引入 App 原生插件
//使用方式
const PluginName = uni.requireNativePlugin(PluginName); // PluginName 为原生插件名称
- 支持项目nativeplugins目录下和插件市场原生云打包的第三方原生插件。你可以将已有原生模块移植到nvue平台也很方便。 使用方式:在manifest.json->App原生插件配置->选择本地插件或者云端插件->打自定义基座才能使用。详见 (opens new window)
- nvue还支持uni-app的js API接口,若无特殊说明,则表示vue文件和nvue文件均支持。详见 (opens new window)。
- nvue 里不支持的 uni-app API,详见 (opens new window)
addRule
Weex 提供 DOM.addRule 以加载自定义字体 。开发者可以通过指定 font-family加载 iconfont 和 custom font。开发者可以使用下面的代码加载自定义字体:
<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>

addRule(type, contentObject)
- @fontFace 协议名称,不可修改。
- @fontFamily
font-family的名称。 - @src 字体地址,url('') 是保留字段,其参数如下:
- http. 从HTTP请求加载, e.g.
url('http://at.alicdn.com/t/font_1469606063_76593.ttf') - https. 从HTTPS请求加载, e.g.
url('https://at.alicdn.com/t/font_1469606063_76593.ttf') - local, Android ONLY. 从assets目录读取, e.g. url('local://foo.ttf'), foo.ttf 是文件名在你的assets目录中.
- file. 从本地文件读取, e.g.
url('file://storage/emulated/0/Android/data/com.alibaba.weex/cache/http:__at.alicdn.com_t_font_1469606063_76593.ttf') - data. 从base64读取, e.g.
url('data:font/truetype;charset=utf-8;base64,AAEAAAALAIAAAwAwR1NVQrD+....'), 上述data字段不全。
- http. 从HTTP请求加载, e.g.
注意
addRule 方法里的 fontFamily 可以随意取。这个名字不是字体真正的名字。字体真正的名字(font- family),也就是注册到系统中的名字是保存在字体二进制文件中的。你需要确保你使用的字体的真正名字(font- family)足够特殊,否则在向系统注册时可能发生冲突,导致注册失败,你的字符被显示为‘?’。 如果你使用 http://www.iconfont.cn/ 来构建你的 iconfont。确保在项目设置中,设置一个特殊的 font-family 名字。默认是 “iconfont”,但极大可能发生冲突。 调用addRule 在 beforeCreate 中是被推荐的
scrollToElement
让页面滚动到 ref 对应的组件,这个 API 只能用于可滚动组件的子节点,例如 <scroller>,<list>, <waterfall> 等可滚动组件中。
scrollToElement(ref, options)
- @ref,要滚动到的那个节点。
- @options
- offset,一个到其可见位置的偏移距离,默认是 0。
- animated,是否需要附带滚动动画,默认是 true
<template>
<view class="wrapper">
<scroller class="scroller">
<view class="row" v-for="(name, index) in rows" :ref="'item'+index">
<text class="text" :ref="'text'+index">{{name}}</text>
</view>
</scroller>
<view class="group">
<text @click="goto10" class="button">Go to 10</text>
<text @click="goto20" class="button">Go to 20</text>
</view>
</view>
</template>
<script>
const dom = uni.requireNativePlugin('dom')
export default {
data() {
return {
rows: []
}
},
created() {
for (let i = 0; i < 30; i++) {
this.rows.push('row ' + i)
}
},
methods: {
goto10(count) {
const el = this.$refs.item10[0]
dom.scrollToElement(el, {})
},
goto20(count) {
const el = this.$refs.item20[0]
dom.scrollToElement(el, {
offset: 0
})
}
}
}
</script>
<style scoped>
.scroller {
width:700rpx;
height:500px;
border-width: 3px;
border-style: solid;
border-color: rgb(162, 217, 192);
margin:0 25rpx;
}
.row {
height: 100rpx;
flex-direction: column;
justify-content: center;
padding-left: 30rpx;
border-bottom-width: 2px;
border-bottom-style: solid;
border-bottom-color: #DDDDDD;
}
.text {
font-size: 45rpx;
color: #666666;
}
.group {
flex-direction: row;
justify-content: center;
margin-top: 60rpx;
}
.button {
width: 200rpx;
padding-top: 20rpx;
padding-bottom: 20rpx;
font-size: 40rpx;
margin-left: 30rpx;
margin-right: 30rpx;
text-align: center;
color: #41B883;
border-width: 2px;
border-style: solid;
border-color: rgb(162, 217, 192);
background-color: rgba(162, 217, 192, 0.2);
}
</style>

getComponentRect
获取某个元素 View 的外框。
getComponentRect(ref, callback)
- @ref,要获取外框的那个节点。
- @callback,异步方法,通过回调返回信息。
回调方法中的数据样例:
{
result: true,
size: {
bottom: 60,
height: 15,
left: 0,
right: 353,
top: 45,
width: 353
}
}
此方法需要在节点渲染后调用才能获取正确的信息,可在 mounted 或 更新数据后 updated 中调用
如果想要获取到 Weex 视口容器的布局信息,可以指定 ref 为字符串 'viewport',即 getComponentRect('viewport', callback)
animation
animation模块可以用来在组件上执行动画。JS-Animation可以对组件执行一系列简单的变换 (位置、大小、旋转角度、背景颜色和不透明度)。
举个例子,如果有一个image组件,通过动画你可以对其进行移动、旋转、拉伸或收缩等动作。
<template>
<view class="box">
<view ref="test" @click="move" class="box-item"></view>
</view>
</template>
<script>
const animation = uni.requireNativePlugin('animation')
export default {
methods: {
move() {
var testEl = this.$refs.test;
animation.transition(testEl, {
styles: {
backgroundColor: '#007AFF',
transform: 'translate(100px, 80px)',
transformOrigin: 'center center'
},
duration: 800, //ms
timingFunction: 'ease',
delay: 0 //ms
},()=>{
uni.showToast({
title: 'finished',
icon:'none'
});
})
}
}
}
</script>
<style scoped>
.box{
width:750rpx;
height:750rpx;
}
.box-item{
width: 250rpx;
height: 250rpx;
background-color: #00aaff;
}
</style>

transition
- @ref,将要执行动画的元素。例如指定动画的元素 ref 属性为 test,可以通过调用 this.$refs.test 来获取元素的引用。
- @options,动画参数。
下表列出了options所有合法的参数:
| 可选值 | 描述 |
|---|---|
| styles | 设置不同样式过渡效果的键值对 |
| duration | 指定动画的持续时间 (单位是毫秒),默认值是 0,表示瞬间达到动画结束状态。 |
| delay | 指定请求动画操作到执行动画之间的时间间隔 (单位是毫秒),默认值是 0,表示没有延迟,在请求后立即执行动画。 |
| needLayout | 动画执行是否影响布局,默认值是false。 |
| timingFunction | 描述动画执行的速度曲线,用于描述动画已消耗时间和动画完成进度间的映射关系。默认值是 linear,表示动画从开始到结束都拥有同样的速度。详见下 |
下表列出了styles所有合法的参数:
| 可选值 | 描述 |
|---|---|
| width | 表示动画执行后应用到组件上的宽度值。如果你需要影响布局,设置needLayout为true。默认值为computed width。 |
| height | 表示动画执行后应用到组件上的高度值。如果你需要影响布局,设置设置为 needLayout为true。默认值为computed width。 |
| backgroundColor | 动画执行后应用到组件上的背景颜色,默认值为computed backgroundColor。 |
| opacity | 表示动画执行后应用到组件上的不透明度值,默认值为computed opacity。 |
| transformOrigin | transformOrigin 定义变化过程的中心点,如transformOrigin: x-axis y-axis 参数 x-axis 可能的值为 left、center、right、长度值或百分比值,参数 y-axis 可能的值为 top、center、bottom、长度值或百分比。默认值为center center。 |
| transform | transform 变换类型,可能包含rotate,translate,scale及其他属性。默认值为空。详见下 |
transform
| 可选值 | 描述 |
|---|---|
| translate/translateX/translateY | 指定元素要移动到的位置。单位是长度或百分比,默认值是0. |
| rotate/rotateX/rotateY | v0.16+ 指定元素将被旋转的角度。单位是度 角度度,默认值是0 |
| scale/scaleX/scaleY | 按比例放大或缩小元素。单位是数字,默认值是1 |
| perspective | v0.16+ 观察者距离z=0平面的距离,在Android 4.1及以上有效。单位值数字,默认值为正无穷。 |
timingFunction
| 可选值 | 描述 |
|---|---|
| linear | 动画从头到尾的速度是相同的 |
| ease-in | 动画速度由慢到快 |
| ease-out | 动画速度由快到慢 |
| ease-in-out | 动画先加速到达中间点后减速到达终点 |
| cubic-bezier(x1, y1, x2, y2) | 在三次贝塞尔函数中定义变化过程,函数的参数值必须处于 0 到 1 之间。更多关于三次贝塞尔的信息请参阅 cubic-bezier 和 Bézier curve。 |
- @callback,callback是动画执行完毕之后的回调函数。在iOS平台上,你可以获取动画执行是否成功的信息。
注意
- iOS上可以获取
animation是否执行成功的信息,callback中的result参数会有两种,分别是Success与Fail。 - Android 的callback 函数不支持result参数。
如果需要使用CSS动画,参考[transition (opens new window)](https://uniapp.dcloud.net.cn/tutorial/nvue- css#transition)和transform (opens new window)。
nvue 里使用 BindingX
uni-app 是逻辑层和视图层分离的。此时会产生两层通信成本。比如拖动视图层的元素,如果在逻辑层不停接收事件,因为通信损耗会产生不顺滑的体验。
BindingX (opens new window) 是weex提供的一种预描述交互语法。由原生解析BindingX规则,按此规则处理视图层的交互和动效。不再实时去js逻辑层运行和通信。
BindingX是一种规则,解析快,但没有js那样足够强的编程灵活性。
uni-app 内置了 BindingX,可在 nvue 中使用 BindingX 完成复杂的动画效果。
- 从HBuilderX 2.3.4起,
uni-app编译模式可直接引用uni.requireNativePlugin('bindingx')模块,weex 模式还需使用 npm 方式引用。 - BindingX demo示例可参考 BindingX 示例里 vue 的相关示例,将相关 vue 代码拷贝到
nvue文件里即可。
注意
- 暂时不要在
expression内使用origin
<template>
<view class="container">
<view ref="b1" class="btn" style="background-color:#6A1B9A" @click="clickBtn">
<text class="text">A</text>
</view>
<view ref="b2" class="btn" style="background-color:#0277BD" @click="clickBtn">
<text class="text">B</text>
</view>
<view ref="b3" class="btn" style="background-color:#FF9800" @click="clickBtn">
<text class="text">C</text>
</view>
<view ref="main_btn" class="btn" @click="clickBtn">
<image class="image" ref="main_image" src="https://gw.alicdn.com/tfs/TB1PZ25antYBeNjy1XdXXXXyVXa-128-128.png" />
</view>
</view>
</template>
<script>
const Binding = uni.requireNativePlugin('bindingx');
module.exports = {
data() {
return {
isExpanded: false
}
},
methods: {
getEl: function(el) {
if (typeof el === 'string' || typeof el === 'number') return el;
if (WXEnvironment) {
return el.ref;
} else {
return el instanceof HTMLElement ? el : el.$el;
}
},
collapse: function() {
let main_btn = this.getEl(this.$refs.main_btn);
let main_image = this.getEl(this.$refs.main_image);
let b1 = this.getEl(this.$refs.b1);
let b2 = this.getEl(this.$refs.b2);
let b3 = this.getEl(this.$refs.b3);
let main_binding = Binding.bind({
eventType: 'timing',
exitExpression: 't>800',
props: [{
element: main_image,
property: 'transform.rotateZ',
expression: 'easeOutQuint(t,45,0-45,800)'
}, {
element: main_btn,
property: 'background-color',
expression: "evaluateColor('#607D8B','#ff0000',min(t,800)/800)"
}]
}, function(res) {
if (res.state === 'exit') {
Binding.unbind({
token: main_binding.token,
eventType: 'timing'
})
}
});
let btn_binding = Binding.bind({
eventType: 'timing',
exitExpression: 't>800',
props: [{
element: b1,
property: 'transform.translateY',
expression: "easeOutQuint(t,-150,150,800)"
}, {
element: b2,
property: 'transform.translateY',
expression: "t<=100?0:easeOutQuint(t-100,-300,300,700)"
}, {
element: b3,
property: 'transform.translateY',
expression: "t<=200?0:easeOutQuint(t-200,-450,450,600)"
}]
}, function(res) {
if (res.state === 'exit') {
Binding.unbind({
token: btn_binding.token,
eventType: 'timing'
})
}
})
},
expand: function() {
let main_btn = this.getEl(this.$refs.main_btn);
let main_image = this.getEl(this.$refs.main_image);
let b1 = this.getEl(this.$refs.b1);
let b2 = this.getEl(this.$refs.b2);
let b3 = this.getEl(this.$refs.b3);
let main_binding = Binding.bind({
eventType: 'timing',
exitExpression: 't>100',
props: [{
element: main_image,
property: 'transform.rotateZ',
expression: 'linear(t,0,45,100)'
}, {
element: main_btn,
property: 'background-color',
expression: "evaluateColor('#ff0000','#607D8B',min(t,100)/100)"
}]
}, function(res) {
if (res.state === 'exit') {
Binding.unbind({
token: main_binding.token,
eventType: 'timing'
})
}
});
let btn_binding = Binding.bind({
eventType: 'timing',
exitExpression: 't>800',
props: [{
element: b1,
property: 'transform.translateY',
expression: "easeOutBounce(t,0,0-150,800)"
}, {
element: b2,
property: 'transform.translateY',
expression: "t<=100?0:easeOutBounce(t-100,0,0-300,700)"
}, {
element: b3,
property: 'transform.translateY',
expression: "t<=200?0:easeOutBounce(t-200,0,0-450,600)"
}]
}, function(res) {
if (res.state === 'exit') {
Binding.unbind({
token: btn_binding.token,
eventType: 'timing'
})
}
})
},
clickBtn: function(e) {
if (this.isExpanded) {
this.collapse();
} else {
this.expand();
}
this.isExpanded = !this.isExpanded;
}
}
}
</script>
<style>
.container {
flex: 1;
}
.image {
width: 60px;
height: 60px;
}
.text {
color: #ffffff;
font-size: 30px;
}
.btn {
width: 100px;
height: 100px;
background-color: #ff0000;
align-items: center;
justify-content: center;
position: absolute;
border-radius: 50px;
bottom: 25px;
right: 25px;
}
</style>

nvue 和 vue 相互通讯
在 uni-app 中,nvue 和 vue 页面可以混搭使用。
推荐使用 uni.$on , uni.$emit 的方式进行页面通讯,旧的通讯方式(uni.postMessage及plus.webview.postMessageToUniNView)不再推荐使用
// 接收信息的页面
// $on(eventName, callback)
uni.$on('page-popup', (data) => {
console.log('标题:' + data.title)
console.log('内容:' + data.content)
})
// 发送信息的页面
// $emit(eventName, data)
uni.$emit('page-popup', {
title: '我是title',
content: '我是content'
});
使用此页面通讯时注意事项:要在页面卸载前,使用 uni.$off 移除事件监听器。参考 (opens new window)
vue 和 nvue 共享的变量和数据
除了通信事件,vue 和 nvue 页面之间还可以共享变量和存储。 uni-app提供的共享变量和数据的方案如下:
- vuex: 自HBuilderX 2.2.5起,nvue支持
vuex。这是vue官方的状态管理工具。
注意:不支持直接引入
store使用,可以使用mapState、mapGetters、mapMutations等辅助方法或者使用this.$store
- uni.storage:
- vue和nvue页面可以使用相同的
uni.storage存储。这个存储是持久化的。 比如登录状态可以保存在这里。 - App端还支持
plus.sqlite,也是共享通用的。
- vue和nvue页面可以使用相同的
- globalData: 小程序有
globalData机制,这套机制在uni-app里也可以使用,全端通用。 在App.vue文件里定义globalData,如下:
<script>
export default {
globalData: {
text: 'text'
},
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
- js中操作
globalData的方式如下:getApp().globalData.text = 'test' - 如果需要把
globalData的数据绑定到页面上,可在页面的onShow生命周期里进行变量重赋值
nvue 里使用 HTML5Plus API
nvue页面可直接使用plus的API,并且不需要等待plus ready
nvue 里不支持的 uni-app API
nvue 支持大部分 uni-app API ,下面只列举目前还不支持的 API
动画
| API | 说明 | 解决方案 |
|---|---|---|
| uni.createAnimation() | 创建一个动画实例 | animation (opens new window) |
滚动
| API | 说明 | 解决方案 |
|---|---|---|
| uni.pageScrollTo() | 将页面滚动到目标位置 | scrollToElement (opens new window) |
节点布局交互
| API | 说明 |
|---|---|
| uni.createIntersectionObserver() | 创建并返回一个 IntersectionObserver 对象实例 |
绘画
canvas API使用,详见canvas文档 (opens new window)。
事件
Weex 提供了通过事件触发动作的能力,例如在用户点击组件时执行 JavaScript。 下面列出了可被添加到 Weex 组件上以定义事件动作的属性:
事件穿透
Android和iOS下原生事件传递机制不同,这里仅针对iOS
当一个父View存在多个同级子View时,由于iOS会选择层级最高的View来响应事件,底层的View的事件永远都不会响应。
Weex在view组件中增加了eventPenetrationEnabled属性,当值为true(默认为false)时,View的子View仍能正常响应事件,但View自身将不会响应事件
View交互性
仅iOS支持
Weex在view组件中增加了userInteractionEnabled属性,当值为false(默认为true)时,View及其子View均不响应事件,事件向下层View传递。
longpress
如果一个组件被绑定了 longpress 事件,那么当用户长按这个组件时,该事件将会被触发。
事件对象
| key | value | 备注 |
|---|---|---|
| type | longpress | |
| target | 触发长按事件的目标组件 | |
| timestamp | 长按事件触发时的时间戳(不支持 H5) |
Appear
如果一个位于某个可滚动区域内的组件被绑定了 appear 事件,那么当这个组件的状态变为在屏幕上可见时,该事件将被触发。
事件对象
| key | value | 备注 |
|---|---|---|
| type | appear | |
| target | 触发 Appear 事件的组件对象 | |
| timestamp | 事件被触发时的时间戳(不支持 H5) | |
| direction | up或 down | 触发事件时屏幕的滚动方向 |
Disappear
如果一个位于某个可滚动区域内的组件被绑定了 disappear 事件,那么当这个组件被滑出屏幕变为不可见状态时,该事件将被触发。
事件对象
| key | value | 备注 |
|---|---|---|
| type | disappear | |
| target | 触发 Disappear 事件的组件对象 | |
| timestamp | 事件被触发时的时间戳(不支持 H5) | |
| direction | up或 down | 触发事件时屏幕的滚动方向 |
HTML5 Plus
uni-app App 端内置 HTML5+ (opens new window) 引擎,让 js 可以直接调用丰富的原生能力
条件编译调用 HTML5+
小程序及 H5 等平台是没有 HTML5+ 扩展规范的,因此在 uni-app 调用 HTML5+ 的扩展规范时,需要注意使用条件编译。否则运行到h5、小程序等平台会出现 plus is not defined错误。
// #ifdef APP-PLUS
var appid = plus.runtime.appid;
console.log('应用的 appid 为:' + appid);
// #endif
uni-app不需要 plus ready
在html中使用plus的api,需要等待plus ready。 而uni-app不需要等,可以直接使用。而且如果你调用plus ready,反而不会触发。
uni-app 中的事件监听
在普通的 H5+ 项目中,需要使用 document.addEventListener 监听原生扩展的事件。
uni-app 中,没有 document。可以使用 plus.globalEvent.addEventListener 来实现
// #ifdef APP-PLUS
// 监听新意图事件
plus.globalEvent.addEventListener('newintent', function(){});
// #endif
同理,在 uni-app 中使用 Native.js 时,一些 Native.js 中对于原生事件的监听同样需要按照上面的方法去实现
Native.js
https://uniapp.dcloud.net.cn/tutorial/native-js.html
renderjs
renderjs是一个运行在视图层的js。它比[WXS (opens new window)](https://uniapp.dcloud.net.cn/tutorial/miniprogram- subject#wxs)更加强大。它只支持app-vue和web。
renderjs的主要作用有2个:
- 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
- 在视图层操作dom,运行 for web 的 js库
平台差异说明
| App | H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 字节跳动小程序、飞书小程序 | QQ小程序 |
|---|---|---|---|---|---|---|
| √(2.5.5+,仅支持vue) | √ | x | x | x | x | x |
- nvue的视图层是原生的,无法运行js。但提供了bindingx技术来解决通信阻塞。详见 (opens new window)
- 微信小程序下替代方案是wxs,这是微信提供的一个裁剪版renderjs。详见 (opens new window)
- web下不存在逻辑层和视图层的通信阻塞,也可以直接操作dom,所以在web端使用renderjs主要是为了跨端复用代码。如果只开发web端,没有必要使用renderjs。
使用方式
设置 script 节点的 lang 为 renderjs
<script module="test" lang="renderjs">
export default {
mounted() {
// ...
},
methods: {
// ...
}
}
</script>
功能详解
- 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
uni- app的app端逻辑层和视图层是分离的,这种机制有很多好处,但也有一个副作用是在造成了两层之间通信阻塞。尤其是App的Android端阻塞问题影响了高性能应用的制作。
renderjs运行在视图层,可以直接操作视图层的元素,避免通信折损。
在hello uni- app的canvas示例中,App端使用了renderjs,由运行在视图层的renderjs直接操作视图层的canvas,实现了远超微信小程序的流畅canvas动画示例。具体在hello uni-app (opens new window)示例中体验,对比App端和小程序端的性能差异。
- 在视图层操作dom,运行for web的js库
官方不建议在uni-app里操作dom,但如果你不开发小程序,想使用一些操作了dom、window的库,其实可以使用renderjs来解决。
在app-vue环境下,视图层由webview渲染,而renderjs运行在视图层,自然可以操作dom和window。
这是一个基于renderjs运行echart完整版的示例:renderjs版echart (opens new window)
同理,f2、threejs等库都可以这么用
注意事项
- 目前仅支持内联使用。
- 不要直接引用大型类库,推荐通过动态创建 script 方式引用。
- 可以使用 vue 组件的生命周期(不支持 beforeDestroy、destroyed、beforeUnmount、unmounted),不可以使用 App、Page 的生命周期
- 视图层和逻辑层通讯方式与 WXS (opens new window) 一致,另外可以通过 this.$ownerInstance 获取当前组件的 ComponentDescriptor 实例。
- 注意逻辑层给数据时最好一次性给到渲染层,而不是不停从逻辑层向渲染层发消息,那样还是会产生逻辑层和视图层的多次通信,还是会卡
- 观测更新的数据在视图层可以直接访问到。
- APP 端视图层的页面引用资源的路径相对于根目录计算,例如:./static/test.js。
- APP 端可以使用 dom、bom API,不可直接访问逻辑层数据,不可以使用 uni 相关接口(如:uni.request)
- H5 端逻辑层和视图层实际运行在同一个环境中,相当于使用 mixin 方式,可以直接访问逻辑层数据。
