博主:_LJaXi Or 東方幻想郷
专栏: uni-app | 小程序开发
开发工具:HBuilderX
小程序开发页面篇
- 小程序组件规范
- 小程序介绍
- 小程序规范
- 代码编写规范
- 须遵循的开发规范
- 运行特性
- 编译器选择
- 编译规则
- 工程目录结构
- `static目录` 使用注意
- `static目录` 条件编译
- 整体目录条件编译
- 小程序页面规范
- 新建页面
- 通过 HBuilderX 创建页面
- 删除页面
- 应用/小程序 首页
- 页面生命周期
- onlnit | 监听页面初始化
- onReachBottom | 页面滚动到底部的事件
- onBackPress | 监听页面返回
- onTabItemTap | 点击 tab 时触发
- 组件生命周期
- 页面调用接口
- getApp()
- getCurrentPages()
- $getAppWebview()
- 页面通迅
- uni.$emit(eventName,OBJECT)
- uni.$on(eventName,callback)
- uni.$once(eventName,callback)
- uni.$off([eventName, callback])
- 路由
- uni-app的路由跳转
- 编程式导航示例
- 声明式导航示例
- 页面代码规范介绍
小程序组件规范
reproduction-and-adaptation-author: _LJaXi
update-time: 2023-8-7 11:28:36
tip: 本人做了一些轻微改动,改动目的是为了更加方便的进行观看
小程序介绍
uni-app
是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台
小程序规范
以下二级 tip 介绍规范内容
代码编写规范
uni-app代码编写,基本语言包括js、vue、css。以及ts、scss等css预编译器
须遵循的开发规范
- 页面文件遵循 Vue 单文件组件 (SFC) 规范,即每个页面是一个.vue文件
- 组件标签靠近小程序规范,详见uni-app 组件规范
- 接口能力(JS API)靠近小程序规范,但需将前缀
wx
、my
等替换为uni
,详见uni-app接口规范 - 数据绑定及事件处理同
Vue.js
规范,同时补充了应用生命周期及页面的生命周期 - 如需兼容app-nvue平台,建议使用flex布局进行开发
运行特性
uni-app分
编译器
和运行时(runtime)
。uni-app能实现一套代码、多端运行,是通过这2部分配合完成的
编译器选择
编译器运行在电脑开发环境。一般是内置在HBuilderX工具中,也可以使用独立的cli版
在 vs code
等编辑器中使用需注意添加一些 插件 以及 setting.json
的配置
Vs Code 下载插件
WXML - Language Service
小程序开发助手
setting.json配置如下
// 小程序格式化
"minapp-vscode.wxmlFormatter": "prettier", // 指定格式化工具
"minapp-vscode.prettier": { // prettier 更多参考 https://prettier.io/docs/en/options.html
"useTabs": false,
"tabWidth": 2,
"printWidth": 100,
"singleQuote": false
},
"[wxml]": {
"editor.defaultFormatter": "qiu8310.minapp-vscode"
}
由于 Vs Code 不支持运行预览,可使用
微信开发者工具
进行项目预览
编译规则
-
在web平台,将.vue文件编译为js代码。与普通的vue cli项目类似
-
在微信小程序平台,编译器将.vue文件拆分生成wxml、wxss、js等代码
-
在app平台,将.vue文件编译为js代码。进一步,如果涉及uts代码:
- 在Android平台,将.uts文件编译为kotlin代码
- 在iOS平台,将.uts文件编译为swift代码
-
编译器分vue2版和vue3版
- vue2版:基于
webpack
实现 - vue3版:基于
vite
实现,性能更快
- vue2版:基于
-
编译器支持条件编译,即可以指定某部分代码只编译到特定的终端平台。从而将公用和个性化融合在一个工程中
-
// #ifdef App console.log("这段代码只有在App平台才会被编译进去。非App平台编译后没有这段代码") // #endif
-
工程目录结构
┌─uniCloud 云空间目录,阿里云为uniCloud-aliyun,腾讯云为uniCloud-tcb(详见uniCloud)
│─components 符合vue组件规范的uni-app组件目录
│ └─comp-a.vue 可复用的a组件
├─utssdk 存放uts文件
├─pages 业务页面文件存放的目录
│ ├─index
│ │ └─index.vue index页面
│ └─list
│ └─list.vue list页面
├─static 存放应用引用的本地静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此
├─uni_modules 存放[uni_module](/uni_modules)。
├─platforms 存放各平台专用页面的目录
├─nativeplugins App原生语言插件
├─nativeResources App端原生资源目录
│ ├─android Android原生资源目录
| └─ios iOS原生资源目录
├─hybrid App端存放本地html文件的目录
├─wxcomponents 存放小程序组件的目录
├─unpackage 非工程代码,一般存放运行或发行的编译结果
├─AndroidManifest.xml Android原生应用清单文件
├─Info.plist iOS原生应用配置文件
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期
├─manifest.json 配置应用名称、appid、logo、版本等打包信息
├─pages.json 配置页面路由、导航条、选项卡等页面类信息
└─uni.scss 这里是uni-app内置的常用样式变量
static目录
使用注意
编译到任意平台时,
static
目录下除了不满足条件编译的文件,会直接复制到最终的打包目录,不会打包编译,非static
目录下的文件(vue、js、css 等)只有被引用时,才会被打包编译
static 目录
只能存放静态资源,如(视频,图片等)
static目录
条件编译
在不同平台,引用的静态资源可能也存在差异,通过 static 的条件编译可以解决此问题,
static 目录
下新建不同平台的专有目录
┌─static
│ ├─mp-weixin
│ │ └─a.png
│ └─b.png
├─main.js
├─App.vue
├─manifest.json
└─pages.json
如以上目录结构,
a.png
指头在小程序平台才能被编译,b.png
在所有平台都会被编译
整体目录条件编译
如果想把各平台的页面文件更彻底的分开,也可以在uni-app项目根目录创建
platforms
目录,然后在下面进一步创建app-plus
、mp-weixin
等子目录,存放不同平台的文件
warning:
platforms目录
下只支持放置页面文件(即页面vue文件),如果需要对其他资源条件编译,建议使用static 目录的条件编译(即static目录
条件编译)
小程序页面规范
介绍 除 nvue
uni-app项目中,一个页面就是一个符合
Vue SFC规范
的.vue
文件,.vue
文件,均为全平台支持
新建页面
uni-app
中的页面,通常会保存在工程根目录下的pages
目录下。每次新建页面,均需在
pages.json
中配置pages
列表;未在pages.json -> pages
中配置的页面,uni-app
会在编译阶段进行忽略
pages.json的完整配置参考:
全局文件 内有详细介绍页面配置
通过 HBuilderX 创建页面
通过HBuilderX开发
uni-app
项目时,在uni-app
项目上右键“新建页面”,HBuilderX会自动在pages.json
中完成页面注册,开发更方便同时,HBuilderX 还内置了常用的页面模板(如图文列表、商品列表等),选择这些模板,可以大幅提升你的开发效率
删除页面
删除页面时,需做两件工作:
- 删除
.vue
文件或.nvue
文件- 删除
pages.json -> pages
列表项中的配置
应用/小程序 首页
可以理解为:
uni-app
会将pages.json -> pages
配置项中的第一个页面,作为当前工程的首页(启动页)
{
// 可以理解为,pages/component/index 这个页面就是小程序的启动页
"pages": [{
"path": "pages/component/index",
"style": {
"navigationBarTitleText": "组件"
}
},
{
"path": "pages/component/view/index",
"style": {
"navigationBarTitleText": "view"
}
}]
}
页面生命周期
uni-app
页面除支持 Vue 组件生命周期外还支持下方页面生命周期函数,当以组合式 API 使用时,在 Vue2 和 Vue3 中存在一定区别,请分别参考:Vue2 组合式 API 使用文档 和 Vue3 组合式 API 使用文档
函数名 | 说明 | 平台差异说明 | 最低版本 |
---|---|---|---|
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+ |
onlnit | 监听页面初始化
注意
- 仅百度小程序基础库 3.260 以上支持 onInit 生命周期
- 其他版本或平台可以同时使用 onLoad 生命周期进行兼容,注意避免重复执行相同逻辑
- 不依赖页面传参的逻辑可以直接使用 created 生命周期替代
onReachBottom | 页面滚动到底部的事件
参数说明
属性 | 类型 | 说明 |
---|---|---|
scrollTop | Number | 页面在垂直方向已滚动的距离(单位px) |
onPageScroll : function(e) { //nvue暂不支持滚动监听,可用bindingx代替
console.log("滚动距离为:" + e.scrollTop);
},
注意
- 可在pages.json里定义具体页面底部的触发距离onReachBottomDistance,比如设为50,那么滚动页面到距离底部50px时,就会触发onReachBottom事件。
- 如使用scroll-view导致页面没有滚动,则触底事件不会被触发。scroll-view滚动到底部的事件请参考scroll-view的文档
onPageScroll
里不要写交互复杂的js,比如频繁修改页面。因为这个生命周期是在渲染层触发的,在非h5端,js是在逻辑层执行的,两层之间通信是有损耗的。如果在滚动过程中,频发触发两层之间的数据交换,可能会造成卡顿。- 如果想实现滚动时标题栏透明渐变,在App和H5下,可在pages.json中配置titleNView下的type为transparent,参考。
- 如果需要滚动吸顶固定某些元素,推荐使用css的粘性布局,参考插件市场。插件市场也有其他js实现的吸顶插件,但性能不佳,需要时可自行搜索。
- 在App、微信小程序、H5中,也可以使用wxs监听滚动,参考;在app-nvue中,可以使用bindingx监听滚动,参考。
onBackPress | 监听页面返回
参数说明
属性 | 类型 | 说明 |
---|---|---|
from | String | 触发返回行为的来源:‘backbutton’——左上角导航栏按钮及安卓返回键;‘navigateBack’——uni.navigateBack() 方法。支付宝小程序端不支持返回此字段 |
export default {
onBackPress(options) {
console.log('from:' + options.from)
}
}
注意
onBackPress
上不可使用async
,会导致无法阻止默认返回- 支付宝小程序只有真机可以监听到非
navigateBack
引发的返回事件(使用小程序开发工具时不会触发onBackPress
),不可以阻止默认返回行为
详细说明及使用:onBackPress 详解
onTabItemTap | 点击 tab 时触发
参数说明
属性 | 类型 | 说明 |
---|---|---|
index | Number | 原生标题栏按钮数组的下标 |
onNavigationBarButtonTap : function (e) {
console.log(e);
// e的返回格式为json对象:{"text":"测试","index":0}
}
注意
- nvue 页面weex编译模式支持的生命周期同weex,具体参考:weex生命周期介绍
组件生命周期
uni-app
组件支持的生命周期,与vue标准组件的生命周期相同。这里没有页面级的onLoad等生命周期
函数名 | 说明 | 平台差异说明 | 最低版本 |
beforeCreate | 在实例初始化之前被调用。详见 | ||
created | 在实例创建完成后被立即调用。详见 | ||
beforeMount | 在挂载开始之前被调用。详见 | ||
mounted | 挂载到实例上去之后调用。详见 注意:此处并不能确定子组件被全部挂载,如果需要子组件完全挂载之后在执行操作可以使用$nextTick Vue官方文档 | ||
beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。详见 | 仅H5平台支持 | |
updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。详见 | 仅H5平台支持 | |
beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用。详见 | ||
destroyed | Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。详见 |
页面调用接口
getApp()
getApp()
函数用于获取当前应用实例,一般用于获取globalData。也可通过应用实例调用App.vue methods
中定义的方法
const app = getApp()
console.log(app.globalData)
app.doSomething() // 调用 App.vue methods 中的 doSomething 方法
注意
- 不要在定义于
App()
内的函数中,或调用App
前调用getApp()
,可以通过this.$scope
获取对应的app实例 - 通过
getApp()
获取实例之后,不要私自调用生命周期函数
getCurrentPages()
getCurrentPages()
函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,数组中的元素为页面实例,第一个元素为首页,最后一个元素为当前页面
每个页面实例的方法属性列表:
方法 | 描述 | 平台说明 |
---|---|---|
page.$getAppWebview() | 获取当前页面的webview对象实例 | App |
page.route | 获取当前页面的路由 |
注意
getCurrentPages()
仅用于展示页面栈的情况,请勿修改页面栈,以免造成页面状态错误。
页面关闭时,对应页面实例会在页面栈中删除
Tips:
navigateTo
,redirectTo
只能打开非 tabBar 页面switchTab
只能打开tabBar
页面reLaunch
可以打开任意页面- 页面底部的
tabBar
由页面决定,即只要是定义为tabBar
的页面,底部都有tabBar
- 不能在
App.vue
里面进行页面跳转
$getAppWebview()
uni-app
在getCurrentPages()
获得的页面里内置了一个方法$getAppWebview()
可以得到当前webview的对象实例,从而实现对 webview 更强大的控制。在 html5Plus 中,plus.webview具有强大的控制能力,可参考:WebviewObject
但
uni-app
框架有自己的窗口管理机制,请不要自己创建和销毁webview,如有需求覆盖子窗体上去,请使用原生子窗体subNvue
注意:
此方法仅 App 支持
示例:
获取当前页面 webview
的对象实例
export default {
data() {
return {
title: 'Hello'
}
},
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
}
}
获取指定页面 webview
的对象实例
getCurrentPages()
可以得到所有页面对象,然后根据数组,可以取指定的页面webview对象
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
uni-app自带的web-view组件,是页面中新插入的一个子webview。获取该对象的方法见:https://ask.dcloud.net.cn/article/35036
页面通迅
uni.$emit(eventName,OBJECT)
触发全局的自定义事件, 附加参数都会传给监听器回调
属性 | 类型 | 描述 |
---|---|---|
eventName | String | 事件名 |
OBJECT | Object | 触发事件携带的附加参数 |
代码示例
uni.$emit('update',{msg:'页面更新'})
uni.$on(eventName,callback)
监听全局的自定义事件。事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数。
属性 | 类型 | 描述 |
---|---|---|
eventName | String | 事件名 |
callback | Function | 事件的回调函数 |
代码示例
uni.$on('update',function(data){
console.log('监听到事件来自 update ,携带参数 msg 为:' + data.msg);
})
uni.$once(eventName,callback)
监听全局的自定义事件。事件可以由 uni.$emit 触发,但是只触发一次,在第一次触发之后移除监听器。
属性 | 类型 | 描述 |
---|---|---|
eventName | String | 事件名 |
callback | Function | 事件的回调函数 |
代码示例
uni.$once('update',function(data){
console.log('监听到事件来自 update ,携带参数 msg 为:' + data.msg);
})
uni.$off([eventName, callback])
移除全局自定义事件监听器
属性 | 类型 | 描述 |
---|---|---|
eventName | Array<String> | 事件名 |
callback | Function | 事件的回调函数 |
Tips
- 如果没有提供参数,则移除所有的事件监听器;
- 如果只提供了事件,则移除该事件所有的监听器;
- 如果同时提供了事件与回调,则只移除这个回调的监听器;
- 提供的回调必须跟$on的回调为同一个才能移除这个回调的监听器;
代码示例
$emit
、$on
、$off
常用于跨页面、跨组件通讯,这里为了方便演示放在同一个页面
<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>
注意事项
- uni. e m i t 、 u n i . emit、 uni. emit、uni.on 、 uni. o n c e 、 u n i . once 、uni. once、uni.off 触发的事件都是 App 全局级别的,跨任意组件,页面,nvue,vue 等
- 使用时,注意及时销毁事件监听,比如,页面 onLoad 里边 uni. o n 注册监听, o n U n l o a d 里边 u n i . on 注册监听,onUnload 里边 uni. on注册监听,onUnload里边uni.off 移除,或者一次性的事件,直接使用 uni.$once 监听
扩展阅读:
路由
uni-app
页面路由为框架统一管理,开发者需要在pages.json里配置每个路由页面的路径及页面样式。类似小程序在 app.json 中配置页面路由一样。所以uni-app
的路由用法与Vue Router
不同,如仍希望采用Vue Router
方式管理路由,可在插件市场搜索 Vue-Router
uni-app的路由跳转
uni-app
有两种页面路由跳转方式:使用navigator组件跳转、调用API跳转,可以携带参数
编程式导航示例
// 在某个页面中使用编程式导航跳转到另一个页面
uni.navigateTo({
url: '/pages/destinationPage/destinationPage'
})
声明式导航示例
<!-- hover-class 是 uni-app 中 <navigator> 组件的一个属性,用于指定在按钮被按下或悬停时应用的样式类 -->
<navigator url="navigate/navigate?title=navigate" hover-class="navigator-hover">
<button type="default">跳转到新页面</button>
</navigator>
页面代码规范介绍
uni-app
支持在 template 模板中嵌套<template/>
和<block/>
,用来进行 列表渲染 和 条件渲染。
<template/>
和<block/>
并不是一个组件,它们仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
<block/>
在不同的平台表现存在一定差异,推荐统一使用/<template/>
代码示例
<template>
<view>
<template v-if="test">
<view>test 为 true 时显示</view>
</template>
<template v-else>
<view>test 为 false 时显示</view>
</template>
</view>
</template>
<template>
<view>
<block v-for="(item,index) in list" :key="index">
<view>{{item}} - {{index}}</view>
</block>
</view>
</template>