uni-app项目中,一个页面就是一个符合Vue SFC规范的 vue 文件。
- 在 uni-app js 引擎版中,后缀名是
.vue文件或.nvue文件。 这些页面均全平台支持,差异在于当 uni-app 发行到App平台时,.vue文件会使用webview进行渲染,.nvue会使用原生进行渲染。一个页面可以同时存在vue和nvue,在pages.json的路由注册中不包含页面文件名后缀,同一个页面可以对应2个文件名。重名时优先级如下:- 在非app平台,先使用vue,忽略nvue
- 在app平台,使用nvue,忽略vue
- 在 uni-app x 中,后缀名是
.uvue文件- uni-app x 中没有js引擎和webview,不支持和vue页面并存。
- uni-app x 在app-android上,每个页面都是一个全屏activity,不支持透明。
页面管理 #
新建页面 #
uni-app中的页面,默认保存在工程根目录下的pages目录下。
每次新建页面,均需在pages.json中配置pages列表;未在pages.json -> pages 中注册的页面,uni-app会在编译阶段进行忽略。
通过HBuilderX开发 uni-app 项目时,在 uni-app 项目上右键“新建页面”,HBuilderX会自动在pages.json中完成页面注册,开发更方便。
新建页面时,可以选择是否创建同名目录。创建目录的意义在于:
- 如果你的页面较复杂,需要拆分多个附属的js、css、组件等文件,则使用目录归纳比较合适。
- 如果只有一个页面文件,大可不必多放一层目录。
删除页面 #
删除页面时,需做两件工作:
- 删除
.vue文件、.nvue、.uvue文件 - 删除
pages.json -> pages列表项中的配置 (如使用HBuilderX删除页面,会在状态栏提醒删除pages.json对应内容,点击后会打开pages.json并定位到相关配置项)
页面改名 #
操作和删除页面同理,依次修改文件和 pages.json。
pages.json #
pages.json 文件用来对 uni-app 进行全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等。
导航栏高度为 44px (不含状态栏),tabBar 高度为 50px (不含安全区)。
它类似微信小程序中app.json的页面管理部分。注意定位权限申请等原属于app.json的内容,在uni-app中是在manifest中配置。
{
"pages": [{
"path": "pages/component/index",
"style": {
"navigationBarTitleText": "组件"
}
}, {
"path": "pages/API/index",
"style": {
"navigationBarTitleText": "接口"
}
}, {
"path": "pages/component/view/index",
"style": {
"navigationBarTitleText": "view"
}
}],
"condition": { //模式配置,仅开发期间生效
"current": 0, //当前激活的模式(list 的索引项)
"list": [{
"name": "test", //模式名称
"path": "pages/component/view/index" //启动页面,必选
}]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "演示",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
"usingComponents":{
"collapse-tree-item":"/components/collapse-tree-item"
},
"renderingMode": "seperated", // 仅微信小程序,webrtc 无法正常时尝试强制关闭同层渲染
"pageOrientation": "portrait", //横屏配置,全局屏幕旋转设置(仅 APP/微信/QQ小程序),支持 auto / portrait / landscape
"rpxCalcMaxDeviceWidth": 960,
"rpxCalcBaseDeviceWidth": 375,
"rpxCalcIncludeWidth": 750
},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"height": "50px",
"fontSize": "10px",
"iconWidth": "24px",
"spacing": "3px",
"iconfontSrc":"static/iconfont.ttf", // app tabbar 字体.ttf文件路径 app 3.4.4+
"list": [{
"pagePath": "pages/component/index",
"iconPath": "static/image/icon_component.png",
"selectedIconPath": "static/image/icon_component_HL.png",
"text": "组件",
"iconfont": { // 优先级高于 iconPath,该属性依赖 tabbar 根节点的 iconfontSrc
"text": "\ue102",
"selectedText": "\ue103",
"fontSize": "17px",
"color": "#000000",
"selectedColor": "#0000ff"
}
}, {
"pagePath": "pages/API/index",
"iconPath": "static/image/icon_API.png",
"selectedIconPath": "static/image/icon_API_HL.png",
"text": "接口"
}],
"midButton": {
"width": "80px",
"height": "50px",
"text": "文字",
"iconPath": "static/image/midButton_iconPath.png",
"iconWidth": "24px",
"backgroundImage": "static/image/midButton_backgroundImage.png"
}
},
"easycom": {
"autoscan": true, //是否自动扫描组件
"custom": {//自定义扫描规则
"^uni-(.*)": "@/components/uni-$1.vue"
}
},
"topWindow": {
"path": "responsive/top-window.vue",
"style": {
"height": "44px"
}
},
"leftWindow": {
"path": "responsive/left-window.vue",
"style": {
"width": "300px"
}
},
"rightWindow": {
"path": "responsive/right-window.vue",
"style": {
"width": "300px"
},
"matchMedia": {
"minWidth": 768
}
}
}
easycom #
HBuilderX 2.5.5起支持easycom组件模式。
传统vue组件,需要安装、引用、注册,三个步骤后才能使用组件。easycom将其精简为一步。
只要组件路径符合规范(具体见下),就可以不用引用、注册,直接在页面中使用。如下:
<template>
<view class="container">
<comp-a></comp-a>
<uni-list>
</uni-list>
</view>
</template>
<script>
// 这里不用import引入,也不需要在components内注册uni-list组件。template里就可以直接用
export default {
data() {
return {}
}
}
</script>
路径规范指:
- 安装在项目根目录的components目录下,并符合
components/组件名称/组件名称.vue - 安装在uni_modules下,路径为
uni_modules/插件ID/components/组件名称/组件名称.vue
┌─components
│ └─comp-a
│ └─comp-a.vue 符合easycom规范的组件
└─uni_modules [uni_module](/plugin/uni_modules.md)中符合easycom规范的组件
└─uni_modules
└─uni-list
└─components
└─uni-list
└─ uni-list.vue
不管components目录下安装了多少组件,easycom打包会自动剔除没有使用的组件。
easycom是自动开启的,不需要手动开启,有需求时可以在pages.json的easycom节点进行个性化设置,如关闭自动扫描,或自定义扫描匹配组件的策略。设置参数如下:
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| autoscan | Boolean | true | 是否开启自动扫描,开启后将会自动扫描符合components/组件名称/组件名称.vue目录结构的组件 |
| custom | Object | - | 以正则方式自定义组件匹配规则。如果autoscan不能满足需求,可以使用custom自定义匹配规则 |
如果你的组件,不符合easycom前述的路径规范。可以在pages.json的easycom节点中自行定义路径规范。
如果需要匹配node_modules内的vue文件,需要使用packageName/path/to/vue-file-$1.vue形式的匹配规则,其中packageName为安装的包名,/path/to/vue-file-$1.vue为vue文件在包内的路径。
"easycom": {
"autoscan": true,
"custom": {
"^uni-(.*)": "@/components/uni-$1.vue", // 匹配components目录内的vue文件
"^vue-file-(.*)": "packageName/path/to/vue-file-$1.vue" // 匹配node_modules内的vue文件
}
}
easycom方式引入的组件无需在页面内import,也不需要在components内声明,即可在任意页面使用。easycom方式引入组件不是全局引入,而是局部引入。例如在H5端只有加载相应页面才会加载使用的组件。- 在组件名完全一致的情况下,
easycom引入的优先级低于手动引入(区分连字符形式与驼峰形式)。 - 考虑到编译速度,直接在
pages.json内修改easycom不会触发重新编译,需要改动页面内容触发。 easycom只处理vue组件,不处理小程序专用组件(如微信的wxml格式组件)。不处理后缀为.nvue的组件。因为nvue页面引入的组件也是.vue组件。可以参考uni ui,使用vue后缀,同时兼容nvue页面。nvue页面里引用.vue后缀的组件,会按照nvue方式使用原生渲染,其中不支持的css会被忽略掉。这种情况同样支持easycom。
页面内容构成 #
uni-app 页面基于 vue 规范。一个页面内,有3个根节点标签:
- 模板组件区
<template> - 脚本区
<script> - 样式区
<style>
<template>
<view class="content">
<button @click="buttonClick">{{title}}</button>
</view>
</template>
<script>
export default {
data() {
return {
title: "Hello world", // 定义绑定在页面上的data数据
}
},
onLoad() {
// 页面启动的生命周期,这里编写页面加载时的逻辑
},
methods: {
buttonClick: function () {
console.log("按钮被点了")
},
}
}
</script>
<style>
.content {
width: 750rpx;
background-color: white;
}
</style>
template模板区 #
template中文名为模板,它类似html的标签。但有2个区别:
- html中
script和style是 html 的二级节点。但在 vue 文件中,template、script、style这3个是平级关系。 - html 中写的是 web 标签,但 vue 的
template中写的全都是 vue 组件,每个组件支持属性、事件、 vue 指令,还可以绑定 vue 的 data 数据。
在vue2中,template 的二级节点只能有一个节点,一般是在一个根 view 下继续写页面组件(如上示例代码)。
但在vue3中,template可以有多个二级节点,省去一个层级。
可以在 manifest 中切换使用 Vue2 还是 Vue3。
script 脚本区 #
script中编写脚本,可以通过lang属性指定脚本语言。
- 在vue和nvue中,默认是js,可以指定ts。
- 在uvue中,仅支持uts,不管script的lang属性写成什么,都按uts编译。
页面生命周期 #
uni-app 页面除支持 Vue 组件生命周期外还支持下方页面生命周期函数。
| 函数名 | 说明 | 平台差异说明 | 最低版本 |
|---|---|---|---|
| onInit | 监听页面初始化,其参数同 onLoad 参数,为上个页面传递的数据,参数类型为 Object(用于页面传参),触发时机早于 onLoad | 百度小程序 | 3.1.0+ |
| onLoad | 监听页面加载,该钩子被调用时,响应式数据、计算属性、方法、侦听器、props、slots 已设置完成,其参数为上个页面传递的数据,参数类型为 Object(用于页面传参)。 | ||
| onShow | 监听页面显示,页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面 | ||
| onReady | 监听页面初次渲染完成,此时组件已挂载完成,DOM 树($el)已可用,注意如果渲染速度快,会在页面进入动画完成前触发 | ||
| onHide | 监听页面隐藏 | ||
| onUnload | 监听页面卸载 | ||
| onResize | 监听窗口尺寸变化 | App、微信小程序、快手小程序 | |
| onPullDownRefresh | 监听用户下拉动作,一般用于下拉刷新 | ||
| onReachBottom | 页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据。具体见下方注意事项 | ||
| onTabItemTap | 点击 tab 时触发,参数为Object,具体见下方注意事项 | 微信小程序、QQ小程序、支付宝小程序、百度小程序、H5、App、快手小程序、京东小程序 | |
| onShareAppMessage | 用户点击右上角分享 | 微信小程序、QQ小程序、支付宝小程序、抖音小程序、飞书小程序、快手小程序、京东小程序 | |
| onPageScroll | 监听页面滚动,参数为Object | nvue不支持 | |
| onNavigationBarButtonTap | 监听原生标题栏按钮点击事件,参数为Object | App、H5 | |
| onBackPress | 监听页面返回,返回 event = {from:backbutton、 navigateBack} ,backbutton 表示来源是左上角返回按钮或 android 返回键;navigateBack表示来源是 uni.navigateBack; | 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 |
| onShareTimeline | 监听用户点击右上角转发到朋友圈 | 微信小程序 | 2.8.1+ |
| onAddToFavorites | 监听用户点击右上角收藏 | 微信小程序、QQ小程序 | 2.8.1+ |
页面调用接口 #
getApp() #
getApp() 函数用于获取当前应用实例,一般用于获取globalData。也可通过应用实例调用 App.vue methods 中定义的方法。
const app = getApp()
console.log(app.globalData)
app.doSomething() // 调用 App.vue methods 中的 doSomething 方法
getCurrentPages() #
getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,数组中的元素为页面实例,第一个元素为首页,最后一个元素为当前页面。
getCurrentPages() 仅用于展示页面栈的情况,请勿修改页面栈,以免造成页面状态错误。页面关闭时,对应页面实例会在页面栈中删除。
页面通讯 #
uni.$emit(eventName,OBJECT) #
触发全局的自定义事件。附加参数都会传给监听器回调。
uni.$emit('update',{msg:'页面更新'})
| 属性 | 类型 | 描述 |
|---|---|---|
| eventName | String | 事件名 |
| OBJECT | Object | 触发事件携带的附加参数 |
uni.$on(eventName,callback) #
监听全局的自定义事件。事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数。
uni.$on('update',function(data){
console.log('监听到事件来自 update ,携带参数 msg 为:' + data.msg);
})
| 属性 | 类型 | 描述 |
|---|---|---|
| eventName | String | 事件名 |
| callback | Function | 事件的回调函数 |
uni.$once(eventName,callback) #
监听全局的自定义事件。事件可以由 uni.$emit 触发,但是只触发一次,在第一次触发之后移除监听器。
uni.$once('update',function(data){
console.log('监听到事件来自 update ,携带参数 msg 为:' + data.msg);
})
| 属性 | 类型 | 描述 |
|---|---|---|
| eventName | String | 事件名 |
| callback | Function | 事件的回调函数 |
uni.$off(eventName, callback) #
移除全局自定义事件监听器。
| 属性 | 类型 | 描述 |
|---|---|---|
| eventName | String | 事件名 |
| callback | Function | 事件的回调函数 |
路由 #
uni-app页面路由为框架统一管理,开发者需要在pages.json里配置每个路由页面的路径及页面样式。类似小程序在 app.json 中配置页面路由一样。所以 uni-app 的路由用法与 Vue Router 不同,如仍希望采用 Vue Router 方式管理路由,可在插件市场搜索Vue-Router。
路由跳转 #
uni-app 有两种页面路由跳转方式:使用navigator组件跳转、调用API跳转。
页面返回时会自动关闭 loading 及 toast, modal 及 actionSheet 不会自动关闭。
页面关闭时,只是销毁了页面实例,未完成的网络请求、计时器等副作用需开发者自行处理。
页面栈 #
框架以栈的形式管理当前所有页面, 当发生路由切换的时候,页面栈的表现如下:
| 路由方式 | 页面栈表现 | 触发时机 |
|---|---|---|
| 初始化 | 新页面入栈 | uni-app 打开的第一个页面 |
| 打开新页面 | 新页面入栈 | 调用 API uni.navigateTo 、使用组件 |
| 页面重定向 | 当前页面出栈,新页面入栈 | 调用 API uni.redirectTo 、使用组件 |
| 页面返回 | 页面不断出栈,直到目标返回页 | 调用 API uni.navigateBack 、使用组件 |
| Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 | 调用 API uni.switchTab 、使用组件 |
| 重加载 | 页面全部出栈,只留下新的页面 | 调用 API uni.reLaunch 、使用组件 |
nvue 开发与 vue 开发的常见区别 #
基于原生引擎的渲染,虽然还是前端技术栈,但和 web 开发肯定是有区别的。
- 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中引入字体文件。如果是本地字体,可以用plus.io的 API 转换路径。 - 目前不支持在 nvue 页面使用
typescript/ts。 - nvue 页面关闭原生导航栏时,想要模拟状态栏。但是,仍然强烈建议在 nvue 页面使用原生导航栏。nvue 的渲染速度再快,也没有原生导航栏快。原生排版引擎解析
json绘制原生导航栏耗时很少,而解析 nvue 的 js 绘制整个页面的耗时要大的多,尤其在新页面进入动画期间,对于复杂页面,没有原生导航栏会在动画期间产生整个屏幕的白屏或闪屏。