一. 初始uni-app
是使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。
1. 开发环境搭建,nodejs,hbuilderx
2. 应用开发流程
2.1 DCloud开发者中心注册账号并创建应用,获取AppID,去注册
2.2 创建uniapp项目
2.3 按照规范编写源代码
├── api # 所有请求
├── assets # 主题 字体等静态资源
├── common # 全局通用工具类
├── components # 全局公用组件
├── language # 国际化 language
├── store # 全局 store管理
├── pages # views 所有页面
├── ├── index # 视图模块名
├── ├── ├── index.vue # 模块入口页面
├── static # 存放应用引用静态资源(如图片、视频等)的目录
├── wxcomponents # 小程序组件的目录
├── App.vue # 入口页面,应用配置,配置App全局样式以及监听
├── main.js # 入口文件 加载组件 初始化等
├── package.json # package.json
├── manifest.json # 配置应用名称、appid、logo、版本等打包信息
└── pages.json # 配置页面路由、导航条、选项卡等页面类信息
2.4 应用调试运行
2.5 应用发布
- 云打包
- IOS离线打包:https://ask.dcloud.net.cn/article/41
- Android离线打包:https://ask.dcloud.net.cn/article/508
二. 知识点梳理
1. 应用生命周期
<script>
export default {
// 当uniapp初始化完成时进行触发,全局只触发一次
onLaunch: function() {
console.log('App Launch')
},
// 当uniapp启动或者从后台进入前台显示
onShow: function() {
console.log('App Show')
},
// 当uniapp从前台进入后台
onHide: function() {
console.log('App Hide')
}
}
</script>
2. 页面生命周期
<script>
export default {
onLoad(option) {}, // 监听页面加载,option为上个页面传递的数据,参数类型为Object
onReady() {}, // 监听页面初次渲染完成
onShow() {}, // 监听页面显示。页面每次出现在屏幕上都触发
onHide() {}, // 监听页面隐藏
onUnload() {}, // 监听页面卸载
onResize() {}, // 监听窗口尺寸变化
onPullDownRefresh() {},
onReachBottom() {},
onTabItemTap() {}
}
</script>
注意:
(1)不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你做预期的 Vue 实例,且 this.a 或 this.myMethod 也会是未定义的。
(2)建议使用 uni-app 的 onReady代替 vue 的 mounted。
(3)建议使用 uni-app 的 onLoad 代替 vue 的 created。
3. 页面路由配置及跳转
需要在pages.json里配置每个路由页面的路径及页面样式,类似小程序在app.json中配置页面路由一样。
{
"pages": [ //pages数组中第一项表示应用启动页
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页", // 导航标题
"navigationBarBackgroundColor": "#F8F8F8", // 导航背景颜色
"navigationBarTextStyle": "black", // 导航字体颜色
"enablePullDownRefresh": true, // 下拉刷新
"app-plus": { // App节点配置项
"titleNView": {},
"animationType": "fade-in",
"animationDuration": 300
}
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black", // 导航字体颜色
"navigationBarTitleText": "uni-app", // 导航标题
"navigationBarBackgroundColor": "#F8F8F8", // 导航背景颜色
"backgroundColor": "#F8F8F8" // 页面背景颜色
},
"tabBar": {
"color": "#333333",
"borderStyle": "black",
"backgroundColor": "#FFFFFF",
"selectedColor": "#333333",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "",
"selectedIconPath": "",
"text": "首页"
},
{
"pagePath": "pages/my/my",
"iconPath": "",
"selectedIconPath": "",
"text": "我的"
}
]
}
}
1、初始化
2、打开新页面
uni.navigateTo 、 <navigator open-type="navigate"/>
3、页面重定向
uni.redirectTo 、 <navigator open-type="redirectTo"/>
4、页面返回
uni.navigateBack 、<navigator open-type="navigateBack"/> 、用户按左上角返回按钮、安卓用户点击物理back按键
5、Tab 切换
uni.switchTab 、 <navigator open-type="switchTab"/> 、用户切换 Tab
6、重加载
uni.reLaunch 、<navigator open-type="reLaunch"/>
注意:
(1)navigateTo, redirectTo 只能打开非 tabBar 页面。
(2)switchTab 只能打开 tabBar 页面。
(3)reLaunch 可以打开任意页面。
(4)页面底部的 tabBar 由页面配置决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
(5)不能在 App.vue 里面进行页面跳转。
4. 页面布局
为支持跨平台,框架建议使用Flex布局
Flex 布局教程:语法篇Flex 布局教程:实例篇
5. 字体图标
uni-app 支持使用字体图标,使用方式与普通 web 项目相同,需要注意以下几点:
- 支持 base64 格式字体图标。
- 支持网络路径字体图标。
- 网络路径必须加协议头 https。
- 从 http://www.iconfont.cn 上拷贝的代码,默认是没加协议头的。
@font-face {
font-family: test1-icon;
src: url('~@/static/iconfont.ttf');
}
6. 基础组件
- 视图容器:view,scroll-view,swiper
- 基础内容:icon,text,rich-text,progress
- 表单组件:button,check-box,form,input,textarea,picker,radio,switch
- 导航:navigator
- 媒体组件:audio,camera,image,video
- 地图:map
- 画布:canvas
- webview:web-view
- 广告:ad
- 开放能力组件:
- App nvue 专用组件:list,recycle-list
- 扩展组件:uni-ui
7. 应用配置
- manifest.json
manifest.json 文件是应用的配置文件,用于指定应用的名称、图标、权限等。
三. 进阶知识点
1. 条件编译
条件编译是里用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。
- API 的条件编译
// #ifdef %PLATFORM%
平台特有的API实现
// #endif
说明:
以 #ifdef 或 #ifndef 加** %PLATFORM%** 开头,以 #endif 结尾。
%PLATFORM%:APP-PLUS,APP-PLUS-NVUE,H5,MP-WEIXIN...
- 组件的条件编译
<template>
<view class="content">
// #ifdef MP-WEIXIN
<page-loading></page-loading>
// #endif
</view>
</template>
- 样式的条件编译
<style lang="scss">
/* #ifdef MP-WEIXIN */
.wx-bg-login {
background-color:#11ff11;
}
/* #endif */
</style>
- pages.json 的条件编译
// #ifdef MP-WEIXIN
{
"path": "pages/speech/speech",
"style": {
"navigationBarTitleText": "语音识别"
}
}
// #endif
- static 目录的条件编译
┌─static
│ ├─mp-weixin
│ │ └─a.png
│ └─b.png
├─main.js
├─App.vue
├─manifest.json
└─pages.json
2. vuex使用
import Vue from 'vue'
import Vuex from 'vuex'
import api from '@/api'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
language: uni.getStorageSync('language') || 0,
isLogin: !!uni.getStorageSync('isLogin'),
loginInfo: {},
userInfo: {}
},
mutations: {
// 语言信息
SET_LANGUAGE: (state, value) => {
state.language = value
},
// 用户登录
SET_LOGIN: (state, value) => {
state.isLogin = value
},
// 用户登录信息
SET_LOGININFO: (state, value) => {
state.loginInfo = value
},
// 用户信息
SET_USERINFO: (state, value) => {
state.userInfo = value
}
},
actions: {
// 设置语言信息
SetLanguage({
dispatch,
commit
}, value) {
uni.setStorageSync('language', value);
commit('SET_LANGUAGE', value);
dispatch('SetLang', value);
},
// 用户登录
Login({
dispatch,
commit
}, data) {
return new Promise((resolve, reject) => {
api.login(data).then(response => {
if (response.status === 1) {
uni.setStorageSync('loginInfo', response.data);
uni.setStorageSync('isLogin', true);
dispatch('ParseLoginInfo');
}
resolve(response)
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetUserInfo({
dispatch,
commit
}) {
return new Promise((resolve, reject) => {
api.getUserInfo(this.state.loginInfo.mobile).then(response => {
if (response.status === 1) {
uni.setStorageSync('userInfo', response.data);
dispatch('ParseUserInfo');
}
resolve(response)
}).catch(error => {
reject(error)
})
})
},
// 解析登录信息
ParseLoginInfo({
dispatch,
commit
}) {
const loginInfo = uni.getStorageSync('loginInfo') || {};
commit('SET_LOGININFO', loginInfo);
commit('SET_LOGIN', Object.keys(loginInfo).length > 0);
dispatch('ParseUserInfo');
},
// 解析用户信息
ParseUserInfo({
dispatch,
commit
}) {
const userInfo = uni.getStorageSync('userInfo') || {};
commit('SET_USERINFO', userInfo);
},
// 退出登录
Logout({
dispatch,
commit
}) {
uni.clearStorageSync();
}
}
})
export default store
3. npm支持
uni-app支持使用npm安装第三方包。
4. 小程序组件支持
uni-app 支持在 App 和 小程序 中使用小程序自定义组件,从HBuilderX2.4.7起,H5端也可以运行微信小程序组件。
平台 | 支持情况 | 小程序组件存放目录 |
H5 | 支持微信小程序组件(2.4.7+) | wxcomponents |
App(不含nvue | 支持微信小程序组件 | wxcomponents |
微信小程序 | 支持微信小程序组件 | wxcomponents |
支付宝小程序 | 支持支付宝小程序组件 | mycomponents |
百度小程序 | 支持百度小程序组件 | swancomponents |
头条小程序 | 支持头条小程序组件 | ttcomponents |
QQ小程序 | 支持QQ小程序组件 | wxcomponents |
┌─wxcomponents 微信小程序自定义组件存放目录
│ └──custom 微信小程序自定义组件
│ ├─index.js
│ ├─index.wxml
│ ├─index.json
│ └─index.wxss
├─mycomponents 支付宝小程序自定义组件存放目录
│ └──custom 支付宝小程序自定义组件
│ ├─index.js
│ ├─index.axml
│ ├─index.json
│ └─index.wxss
├─swancomponents 百度小程序自定义组件存放目录
│ └──custom 百度小程序自定义组件
│ ├─index.js
│ ├─index.swan
│ ├─index.json
│ └─index.wxss
├─pages
│ └─index
│ └─index.vue
│
├─static
├─main.js
├─App.vue
├─manifest.json
└─pages.json
在 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
}
}
}
]
}
5. 使用nvue
uni-app在App端,支持vue页面和nvue页面混搭、互相跳转。也支持纯nvue项目。
四. 注意事项
1. data 属性
data 必须声明为返回一个初始数据对象的函数;否则页面关闭时,数据不会自动销毁,再次打开该页面时,会显示上次数据。
//正确用法,使用函数返回对象
data() {
return {
title: 'Hello'
}
}
//错误写法,会导致再次打开页面时,显示上次数据
data: {
title: 'Hello'
}
2. 全局变量
- 公用模块封装
- globalData
小程序中有个globalData概念,可以在 App 上声明全局变量。 Vue 之前是没有这类概念的,但 uni-app 引入了globalData概念,并且在包括H5、App等平台都实现了。
在 App.vue 可以定义 globalData ,也可以使用 API 读写这个值。
globalData支持vue和nvue共享数据。
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>
<style>
/*每个页面公共css */
</style>
js中操作globalData的方式如下:
赋值:getApp().globalData.text = ‘test’
取值:console.log(getApp().globalData.text)
如果需要把globalData的数据绑定到页面上,可在页面的onshow声明周期里进行变量重赋值。HBuilderX 2.0.3起,nvue页面在uni-app编译模式下,也支持onshow。
- Vuex
3. Class 与 Style 绑定
- class 支持的语法:
<view :class="{ active: isActive }">111</view>
<view class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }">222</view>
<view class="static" :class="[activeClass, errorClass]">333</view>
<view class="static" v-bind:class="[isActive ? activeClass : '', errorClass]">444</view>
<view class="static" v-bind:class="[{ active: isActive }, errorClass]">555</view>
- style 支持的语法:
<view v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">666</view>
<view v-bind:style="[{ color: activeColor, fontSize: fontSize + 'px' }]">777</view>
非H5端不支持 Vue官方文档:Class 与 Style 绑定 中的 classObject 和 styleObject 语法。
<template>
<view :class="[activeClass]" :style="[baseStyles,overridingStyles]"></view>
</template>
<script>
export default {
data() {
return {
activeClass: {
'active': true,
'text-danger': false
},
baseStyles: {
color: 'green',
fontSize: '30px'
},
overridingStyles: {
'font-weight': 'bold'
}
}
}
}
</script>
注意:以: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>
用在组件上
非H5端暂不支持在自定义组件上使用 Class 与 Style 绑定
4. 列表循环
在H5平台 使用 v-for 循环整数时和其他平台存在差异,
- 如 v-for=“(item, index) in 10” 中,在H5平台 item 从 1 开始,其他平台 item 从 0 开始,可使用第二个参数 index 来保持一致。
- 在非H5平台 循环对象时不支持第三个参数,如 v-for=“(value, name, index) in object” 中,index 参数是不支持的。
5. 事件处理器
// 常用事件映射表,左侧为 WEB 事件,右侧为 ``uni-app`` 对应事件
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
longtap: 'longtap', //推荐使用longpress代替
input: 'input',
change: 'change',
submit: 'submit',
blur: 'blur',
focus: 'focus',
reset: 'reset',
confirm: 'confirm',
scroll: 'scroll'
}
- 为兼容各端,事件需使用 v-on 或 @ 的方式绑定,请勿使用小程序端的bind 和 catch 进行事件绑定。
- 若需要禁止蒙版下的页面滚动,可使用
@touchmove.stop.prevent="moveHandle"
,moveHandle 可以用来处理 touchmove 的事件,也可以是一个空函数。
-<view class="mask" @touchmove.stop.prevent="moveHandle"></view>
- v-html指令
App端(vue页面V3编译模式)和H5端支持v-html,其他端不支持v-html。
跨端的富文本处理方案详见:https://ask.dcloud.net.cn/article/35772
五. 消息推送-UniPush
UniPush是DCloud联合个推公司推出的集成型统一推送服务,内建了苹果、华为、小米、OPPO、VIVO、魅族等手机厂商的系统级推送和个推等第三方推送。
uniPush推送开发指南
1. 推送平台开通
2. 模块权限配置
3. 厂商推送设置
4. 主要代码
// 初始化推送
initPush() {
// #ifdef APP-PLUS
let _self = this;
// 点击状态栏消息
plus.push.addEventListener('click', function(msg) {
// 收到消息点击逻辑处理,跳转页面
}, false);
// receive事件
plus.push.addEventListener('receive', function(msg) {
_self.createLocalPushMsg(msg); //创建本地消息
}, false);
// #endif
},
//创建本地消息
createLocalPushMsg(msg) {
if (msg.payload === 'LocalMSG') {
return;
}
let options = {
cover: false
};
plus.push.createMessage(msg.content, 'LocalMSG', options);
},
4. 获取clidentid
plus.push.getClientInfo().clientid
六. 开放生态
- uni-app插件市场,有数千款插件,支持前端组件、js sdk、页面模板、项目模板、原生插件等多种类型
- 兼容微信小程序 JS SDK,丰富的小程序生态内容可直接引入uni-app,并且在App通用
- 兼容微信小程序自定义组件,并且App通用
- 支持 mpvue 项目及组件,全端通用
- 支持 NPM 包管理系统
- 图表uCharts
- 丰富样式库ColorUI
- 官方UI组件库uni-ui