一、框架简介
1、目录结构
┌─components 组件目录
├─common 公共资源
├─hybrid 本地网页
├─platforms 各平台专用页面
├─pages 业务页面文件
├─static 静态资源
├─wxcomponents 存放小程序组件的目录
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期
├─manifest.json 配置应用名称、appid、logo、版本等打包信息
└─pages.json 配置页面路由、导航条、选项卡等页面类信息
2、应用生命周期 App.vue
函数名 | 说明 |
---|---|
onLaunch | 当uni-app 初始化完成时触发(全局只触发一次) |
onShow | 当 uni-app 启动,或从后台进入前台显示 |
onHide | 当 uni-app 从前台进入后台 |
onError | 当 uni-app 报错时触发 |
onUniNViewMessage | 对 nvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯 |
3、页面生命周期
函数名 | 说明 | 参数 |
onLoad | 监听页面加载 | 上个页面传递的数据,Object |
onReady | 监听页面初次渲染完成。若渲染速度快,会在页面进入动画完成前触发 | - |
onResize | 监听窗口尺寸变化 | - |
onPullDownRefresh | 监听用户下拉动作 | - |
onReachBottom | 页面上拉触底事件的处理函数 | - |
onTabItemTap | 点击 tab 时触发 | index,类型String,序号; pagePath,类型String,页面路径; text,类型String,按钮文字 |
onShareAppMessage | 用户点击右上角分享 | - |
onPageScroll | 监听页面滚动 | scrollTop,类型Number,垂直方向已滚动的距离px |
onNavigationBarButtonTap | 监听原生标题栏 按钮点击事件 | index,类型Number,标题栏按钮数组的下标 |
onBackPress | 监听页面返回 | from ,类型String,触发返回的来源:'backbutton':左上角导航栏按钮及安卓返回键;'navigateBack':uni.navigateBack() 方法。 |
onNavigationBarSearchInputChanged | 监听原生标题栏 搜索输入框 内容变化事件 | - |
onNavigationBarSearchInputConfirmed | 监听原生标题栏搜索框 搜索事件,用户点击软键盘上的“搜索”按钮时触发。 | - |
onNavigationBarSearchInputClicked | 监听原生标题栏 搜索框点击事件 | - |
onBackPress(options) {
console.log('from:' + options.from)
}
4、路由
两种路由跳转方式:使用navigator组件跳转、调用API跳转。
5、页面栈
框架以栈的形式管理当前所有页面, 当发生路由切换的时候,页面栈的表现如下:
路由方式 | 页面栈表现 | 触发时机 |
初始化 | 新页面入栈 | 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"/> |
if(process.env.NODE_ENV === 'development'){
console.log('开发环境')
}else{
console.log('生产环境')
}
b.判断平台
1.编译期判断,即条件编译,不同平台在编译出包后已经是不同的代码。
写法:以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。
#ifdef:仅在某平台存在
#ifndef:除了某平台均存在
%PLATFORM% 可取值如下:
值 | 平台 |
---|---|
APP-PLUS | 5+App(参考HTML5+ 规范) |
APP-PLUS-NVUE | 5+App nvue(参考Weex 规范) |
H5 | H5 |
MP-WEIXIN | 微信小程序 |
MP-ALIPAY | 支付宝小程序 |
MP-BAIDU | 百度小程序 |
MP-TOUTIAO | 头条小程序 |
MP-QQ | QQ小程序(目前仅cli版支持) |
MP | 微信小程序/支付宝小程序/百度小程序/头条小程序/QQ小程序 |
Android 和 iOS 平台不支持通过条件编译来区分,如果需要区分 Android、iOS 平台,请通过调用 uni.getSystemInfo 获取平台信息。
注意: 条件编译是利用注释实现的,在不同语法里注释写法不一样,js使用 // 注释、css 使用 /* 注释 */、vue/nvue 模板里使用 < !-- 注释 -->;
除了支持单个平台的条件编译外,还支持多平台同时编译,使用 || 来分隔平台名称。
不同平台下的特有功能,以及小程序平台的分包,都可以通过 pages.json 的条件编译来更好地实现。这样,就不会在其它平台产生多余的资源,进而减小包体积。
static 目录的条件编译
在不同平台,引用的静态资源可能也存在差异,通过 static 的的条件编译可以解决此问题,static 目录下新建不同平台的专有目录(目录名称同 %PLATFORM% 值域,但字母均为小写),专有目录下的静态资源只有在特定平台才会编译进去。
如以下目录结构,a.png 只有在微信小程序平台才会编译进去,b.png 在所有平台都会被编译。
┌─static
│ ├─mp-weixin
│ │ └─a.png
│ └─b.png
├─main.js
├─App.vue
├─manifest.json
└─pages.json
整体目录条件编译
如果想把各平台的页面文件更彻底的分开,也可以在uni-app项目根目录下创建platforms目录,然后在下面进一步创建APP-PLUS、MP-WEIXIN等子目录,存放不同平台的文件。
在项目里,正常都是复用的代码多,所以应该使用多端。而个性的代码可以放到不同平台的目录下,差异化维护。
2.运行期判断,代码已经打入包中,仍然需要在运行期判断平台,此时可使用 uni.getSystemInfoSync().platform 判断客户端环境。小程序返回值均为 devtools。
switch(uni.getSystemInfoSync().platform){
case 'android':
console.log('运行Android上')
break;
case 'ios':
console.log('运行iOS上')
break;
default:
console.log('运行在开发者工具上')
break;
}
二、页面样式与布局
1、尺寸单位
uni-app 支持的通用 css 单位包括 px、rpx
rpx 即响应式px。以750宽的屏幕为基准,屏幕变宽,rpx 实际显示效果会等比放大。
rem 默认根字体大小为 屏幕宽度/20(微信小程序、头条小程序、App、H5)。
nvue不支持百分比单位、rem、vh、vw。
App端,在 pages.json 里的 titleNView 或页面里写的 plus api 中涉及的单位,只支持 px。
nvue中,uni-app 模式可以使用 px 、rpx,表现与 vue 中一致。
weex 模式目前遵循weex的单位,它的单位比较特殊:
px:,以750宽的屏幕为基准动态计算的长度单位,与 vue 页面中的 rpx 理念相同。
wx:与设备屏幕宽度无关的长度单位,与 vue 页面中的 px 理念相同。
开发者可以通过设计稿基准宽度计算页面元素 rpx 值,设计稿 1px 与框架样式 1rpx 转换公式如下:
设计稿 1px / 设计稿基准宽度 = 框架样式 1rpx / 750rpx
换言之,页面元素宽度在 uni-app 中的宽度计算公式:
750 * 元素在设计稿中的宽度 / 设计稿基准宽度
举例说明:
若设计稿宽度为 750px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app 里面的宽度应该设为:750 * 100 / 750,结果为:100rpx。
若设计稿宽度为 640px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app 里面的宽度应该设为:750 * 100 / 640,结果为:117rpx。
若设计稿宽度为 375px,元素 B 在设计稿上的宽度为 200px,那么元素 B 在 uni-app 里面的宽度应该设为:750 * 200 / 375,结果为:400rpx。
rpx不支持动态横竖屏切换计算,使用rpx建议锁定屏幕方向。
如果设计稿不是750px,HBuilderX提供了自动换算的工具,详见:https://ask.dcloud.net.cn/article/35445。
早期 uni-app 提供了 upx ,目前已经推荐统一改为 rpx 。
2、css选择器
选择器 | 样例 | 样例描述 |
element | view | 选择所有 view 组件 |
::after | view::after | 在 view 组件后边插入内容 |
::before | view::before | 在 view 组件前边插入内容 |
在 uni-app 中不能使用 * 选择器。page 相当于 body 节点。nvue页面暂不支持全局样式。
3、内置 CSS 变量
CSS变量 | 描述 | 5+App | 小程序 | H5 |
--status-bar-height | 系统状态栏高度 | 系统状态栏高度、nvue不支持 | 25px | 0 |
--window-top | 内容区域距顶部的距离 | 0 | 0 | NavigationBar 的高度44px |
--window-bottom | 内容区域距底部的距离 | 0 | 0 | TabBar 的高度50px |
.status_bar {
height: var(--status-bar-height);/*状态栏*/
width: 100%;
}
由于在H5端,不存在原生导航栏和tabbar,也是前端div模拟。如果设置了一个固定位置的居底view,在小程序和App端是在tabbar上方,但在H5端会与tabbar重叠。此时可使用–window-bottom,不管在哪个端,都是固定在tabbar上方。
.toTop {
bottom: calc(var(--window-bottom) + 10px)/*距离底部tabbar上浮10px*/
}
目前 nvue 在App端,还不支持 --status-bar-height变量,替代方案是在页面onLoad时通过uni.getSystemInfoSync().statusBarHeight获取状态栏高度,然后通过style绑定方式给占位view设定高度。
<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>
4、背景图片
使用本地路径背景图片需注意:
图片小于 40kb,uni-app 会自动将其转化为 base64 格式
图片大于等于 40kb, 需开发者自己将其转换为 base64 格式使用,或将其挪到服务器上,从网络地址引用。
uni-app v3版本设定为不自动将图片转 base64 格式。
本地背景图片的引用路径推荐使用以 ~@ 开头的绝对路径。
.test2 {
background-image: url('~@/static/logo.png');
}
注意:
微信小程序不支持相对路径(真机不支持,开发工具支持)
其他端使用本地背景图片作为背景图没有限制
5、字体图标
uni-app 支持使用字体图标,使用方式与普通 web 项目相同,需要注意以下几点:
网络路径必须加协议头 https。
从 http://www.iconfont.cn 上拷贝的代码,默认是没加协议头的。
uni-app 本地路径图标字体支持情况:
字体文件小于 40kb,uni-app 会自动将其转化为 base64 格式;
字体文件大于等于 40kb, 需开发者自己转换,否则使用将不生效;
@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;
}
6、< template/> 和< block/>
uni-app 支持在 template 模板中嵌套 < template/> 和 < block/>,用来进行 列表渲染 和 条件渲染。它们仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
<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>
三、npm支持
1、初始化npm工程
若项目之前未使用npm管理依赖(项目根目录下无package.json文件),先在项目根目录执行命令初始化npm工程:
npm init -y
cli项目默认已经有package.json了。HBuilderX创建的项目默认没有,需要通过初始化命令来创建。
2、安装依赖
在项目根目录执行命令安装npm包:
npm install packageName --save
3、使用
安装完即可使用npm包,js中引入npm包:
import package from 'packageName'
const package = require('packageName')
注意:
为多端兼容考虑,建议优先从 uni-app插件市场 获取插件。直接从 npm 下载库很容易只兼容H5端。
非 H5 端不支持使用含有 dom、window 等操作的 vue 组件和 js 模块,安装的模块及其依赖的模块使用的 API 必须是 uni-app 已有的 API(兼容小程序 API),比如:支持高德地图微信小程序 SDK。类似jQuery 等库只能用于H5端。
支持安装 mpvue 组件,但npm方式不支持小程序自定义组件(如 wxml格式的vant-weapp)。
四、TypeScript 、小程序组件支持
1、TypeScript 支持
在 vue 文件的 script 节点声明 lang=“ts”
声明 lang=“ts” 后,该 vue 文件 import 进来的所有 vue 组件,均需要使用 ts 编写。
2、小程序组件支持
uni-app 支持在 App 和 小程序 中使用小程序自定义组件。
平台 | 支持情况 | 小程序组件存放目录 |
H5 | 不支持 | - |
App(不含nvue) | 支持微信小程序组件 | wxcomponents |
微信小程序 | 支持微信小程序组件 | wxcomponents |
支付宝小程序 | 支持支付宝小程序组件 | mycomponents |
百度小程序 | 支持百度小程序组件 | swancomponents |
头条小程序 | 支持头条小程序组件 | ttcomponents |
QQ小程序 | 支持QQ小程序组件 | wxcomponents |
使用方式:
在 pages.json 对应页面的 style -> usingComponents 引入组件:
{
"pages": [
{
"path": "index/index",
"style": {
"usingComponents": {
// #ifdef APP-PLUS || MP-WEIXIN || MP-QQ
"custom": "/wxcomponents/custom/index"
// #endif
// #ifdef MP-BAIDU
"custom": "/swancomponents/custom/index"
// #endif
// #ifdef MP-ALIPAY
"custom": "/mycomponents/custom/index"
// #endif
}
}
}
]
}
在页面中使用:
<view>
<custom name="uni-app">custom>
view>
注意:
1、小程序组件,HBuilderX 建立的工程 文件夹在 项目根目录下。vue-cli 建立的工程 文件夹在 src 目录下。可在 vue.config.js 中自定义其他目录。
2、小程序组件的性能,不如vue组件。使用小程序组件,需要自己手动setData,很难自动管理差量数据更新。而使用vue组件会自动diff更新差量数据。所以如无明显必要,建议使用vue组件而不是小程序组件。比如某些小程序ui组件,完全可以用更高性能的uni ui替代。
3、当需要在 vue 组件中使用小程序组件时,注意在 pages.json 的 globalStyle 中配置 usingComponents,而不是页面级配置。
4、注意数据和事件绑定的差异,使用时应按照 vue 的数据和事件绑定方式:例如,阻止事件冒泡 从 catch:tap=“xx” 改为 @tap.native.stop=“xx”。
5、事件命名用驼峰命名。
五、使用vue.js注意事项
相比Web平台, Vue.js 在 uni-app 中使用差异主要集中在两个方面:
新增:uni-app除了支持Vue实例的生命周期,还支持应用启动、页面显示等生命周期。
受限:相比web平台,在小程序和App端部分功能受限。
支持vue模板语法:https://cn.vuejs.org/v2/guide/syntax.html
1、class style
非H5端不支持 Vue Class 与 Style 绑定 中的 classObject 和 styleObject 语法。
class:
<view :class="{ active: isActive }">111view>
<view :class="{ active: isActive, 'text-danger': hasError }">222view>
<view :class="[activeClass, errorClass]">333view>
<view :class="[isActive ? activeClass : '', errorClass]">444view>
<view :class="[{ active: isActive }, errorClass]">555view>
style:
<view :style="{ color: activeColor, fontSize: fontSize + 'px' }">666view>
<view :style="[{ color: activeColor, fontSize: fontSize + 'px' }]">777view>
以:style=""这样的方式设置px像素值,其值为实际像素,不会被编译器转换
可用 computed 方法生成 class 或者 style 字符串,插入到页面中
<template>
<!-- 支持 -->
<view class="container" :class="computedClassStr"></view>
<view class="container" :class="{active: isActive}"></view>
<!-- 不支持 -->
<view class="container" :class="computedClassObject"></view>
</template>
<script>
export default {
data () {
return {
isActive: true
}
},
computed: {
computedClassStr () {
return this.isActive ? 'active' : ''
},
computedClassObject () {
return { active: this.isActive }
}
}
}
</script>
2、v-for
在H5平台,使用 v-for 循环整数时和其他平台存在差异,如 v-for="(item, index) in 10" 中,在H5平台 item 从 1 开始,其他平台 item 从 0 开始,可使用第二个参数 index 来保持一致。
在非H5平台,循环对象时不支持第三个参数。
3、事件处理器
// 事件映射表,左侧为 WEB 事件,右侧为 ``uni-app`` 对应事件
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
longtap: 'longtap',
input: 'input',
change: 'change',
submit: 'submit',
blur: 'blur',
focus: 'focus',
reset: 'reset',
confirm: 'confirm',
columnchange: 'columnchange',
linechange: 'linechange',
error: 'error',
scrolltoupper: 'scrolltoupper',
scrolltolower: 'scrolltolower',
scroll: 'scroll'
}
注意:
1、为兼容各端,事件需使用 v-on 或 @ 的方式绑定,请勿使用小程序端的bind 和 catch 进行事件绑定。
2、事件修饰符:
.stop:各平台均支持, 阻止事件冒泡,在非 H5 端同时也会阻止事件的默认行为
.prevent 仅在 H5 平台支持
.self:仅在 H5 平台支持
.once:仅在 H5 平台支持
.capture:仅在 H5 平台支持
.passive:仅在 H5 平台支持
3、若需要禁止蒙版下的页面滚动,可使用 @touchmove.stop.prevent=“moveHandle”,moveHandle 可以用来处理 touchmove 的事件,也可以是一个空函数。
< view class=“mask” @touchmove.stop.prevent=“moveHandle”>< /view>
4、按键修饰符:uni-app运行在手机端,没有键盘事件,所以不支持按键修饰符。
4、表单控件绑定
支持 Vue官方文档:表单控件绑定。
建议开发过程中直接使用 uni-app表单组件。eg:
H5 的select 标签用 picker 组件进行代替。
表单元素 radio 用 radio-group 组件进行代替
5、组件
uni-app只支持vue单文件组件(.vue 组件)。其他的诸如:动态组件,自定义 render,和< script type=“text/x-template”> 字符串模版等,在非H5端不支持。
详细的非H5端不支持列表:
slot(scoped 暂时还没做支持)
动态组件
异步组件
inline-template
X-Templates
keep-alive
transition (可使用 animation 或 CSS 动画替代)
全局组件:
支持配置全局组件,在 main.js 里进行全局注册,注册后就可在所有页面里使用该组件。
注意:
Vue.component 的第一个参数必须是静态的字符串。
nvue页面暂不支持全局组件。
import Vue from 'vue'
import pageHead from './components/page-head.vue'
Vue.component('page-head',pageHead)