计划一篇文章把所有知识点都写一下,但是写着过程中,发现内容太多,所有拆分了成了几篇:
- 主要介绍路由、通信以及开发中遇到的问题和解决方案;
- 主要介绍组件开发以及自己写的几个组件插件;
- 主要介绍canvas以及如何自己实现饼状图、柱状图;
- 主要介绍上线的流程以及上线过程可能会遇到的问题
uni-app是一个基于Vue.js的跨平台的开发的框架,这意味着只要你会vue就可以使用该框架编写一套代码,发布到ios、android、web、各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台。
本文主要是分享我使用uni-app的一些经验和总结,所以有关该框架的详细资料,可以通过uni-app官方网站查阅该框架的详细介绍,通过安装官网提供的开发工具HBuilder X来开发项目。
本文主要针对我开发时常用到的方法,举的例子都是对于App来说的,当然也适用于小程序
应用生命周期
- onLaunch
- 初始化完成时触发: 刚打开app,但是未见到里面的内容,可以在该方法里面进行版本更新提示
- onShow
- 启动或从后台进入前台显:app启动看到里面的内容
- onHide
- 从前台进入后台:应用在后台(比如打开了其他app、电话来了等)
路由
对于app来说,每个页面对应一个路由,在uni-app中路由需要在根目录下pages.json的pages字段进行配置,具体查看pages.json配置列表,注意:pages节点的第一项为应用入口页(即首页)
路由跳转
官方api地址:https://uniapp.dcloud.io/api/router
- uni.navigateTo: 保留当前页面,跳转到应用内的某个页面,url可带参,类似于vue-router中的router.push
- uni.redirectTo: 关闭当前页面,跳转到应用内的某个页面,url可带参,类似于vue-router中router.replace
- uni.reLaunch:关闭所有页面,打开到应用内的某个页面,url可带参
- uni.switchTab:跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面,url不可带参,
- uni.navigateBack:关闭当前页面,返回上一页面或多级页面,类似于vue-router中的router.go(-n)
注意
url有长度限制,太长的字符串会传递失败,可通过窗口通信(下面会介绍)、全局变量或encodeURIComponent等多种方式解决
示例:
//pageA页面
const data = {
//多个参数
}
uni.navigateTo({
url: `/pages/pageB/pageB?data=encodeURIComponent(JSON.stringify(data))`
})
//pageB页面
onLoad(option){
const data = JSON.parse(decodeURIComponent(option.data));
}
页面生命周期
-
onLoad
- 监听页面加载,其参数为上个页面传递的数据,常用于获取上一个页面传过来的参数
-
onShow
- 监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面返回当前页面
-
onReady
- 监听页面初次渲染完成。注意如果渲染速度快,会在页面进入动画完成前触发
-
onHide
- 监听页面隐藏。uni.navigateTo跳转到下个页面或者app后台运行
-
onUnload
- 监听页面卸载。当前页面中执行uni.navigateBack
-
onPullDownRefresh
- 监听用户下拉动作,一般用于下拉刷新。
-
onReachBottom
- 页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据
-
onTabItemTap
- 点击 tab或者执行uni.switchTab 时触发,参数为Object
-
onBackPress
- 监听页面返回,返回 event = {from:backbutton、 navigateBack} ,backbutton 表示来源是左上角返回按钮或 android 返回键;navigateBack表示来源是 uni.navigateBack。在回调函数中如果想进行一些逻辑判断的话,可以通过动态的设置return true禁止返回上一个页面,return false返回上一个页面
onBackPress(event){ const { from } = event; if(from === 'backbutton'){ //逻辑判断 return true; }else if(from === 'navigateBack'){ return false; } }
组件生命周期
组件生命周期同vue
详细资料查阅:
- vue官网生命周期
- uni-app组件生命周期
通信
globalData
App.vue 中定义globalData的相关配置:
//App.vue
//page中使用
//获取数据
getApp().globalData.msg
//更改数据
getApp().globalData.msg = 'uni-app!'
路由跳转传参
//pageA页面
uni.navigateTo({
url: `/pages/pageB/pageB?name=uni-app&age=18`
})
//pageB页面
onLoad(option){
const { name, age } = option;
}
EventChannel
多用于页面间事件通信,页面A跳转到B传参,B页面返回A页面传参
//pageA
uni.navigateTo({
url: '/pages/pageB/pageB',
events:{
fromBackPageData:function(data){
console.log('fromBackPageData',data);
}
},
success(res){
res.eventChannel.emit('passToNextPage',{name: 'pageA'})
}
})
//pageB
onLoad(){
const eventChannel = this.getOpenerEventChannel();
eventChannel.on('passToNextPage',data => {
console.log('passToNextPage', data);
})
eventChannel.emit('fromBackPageData',{name: 'backData'})
}
页面通信
-
uni.$emit
(事件名,参数),触发全局的自定事件; -
uni.$on
(事件名,回调函数),监听全局的自定义事件,事件可以由uni.$emit
触发; -
uni.$once
(事件名,回调函数),监听全局的自定义事件,事件可以由uni.$emit
触发,但是只触发一次,在第一次触发之后移除监听器; -
uni.$off
(事件名,回调函数),移除全局自定义事件监听器
注意:
常用于跨页面、跨组件通讯;
要及时销毁事件监听
示例:
//pageA
//pageB
组件间通信也可以这么使用,看情况处理
其他与vue通信方式一致
1.事件总线:跨页面和组件
class Bus{
constructor(){
this.callbacks = {};
}
$on(eventName,cb){
this.callbacks[eventName] = this.callbacks[name] || [];
this.callbacks[name].push(cb);
}
$emit(eventName,args){
if(this.callbacks){
this.callbacks[name].forEach(cb => cb(args))
}
}
}
// main.js
Vue.prototype.$bus = new Bus()
或
Vue.prototype.$bus = new Vue()
//pageA或componentA
this.$bus.$emit(事件名,参数)
//pageB或componentB
this.$bus.$on(事件名,回调函数)
2.vuex: 跨页面和组件,创建唯一的全局数据管理者store,通过它管理数据并通知组件状态变更
const store = new Vuex.Store({
state: {},
getters: {
},
actions: {
},
mutations: {
}
})
3.props:属性传值,父给子传值
//父组件
//子组件
{{ name }}
4.自定义事件:子给父传值
//父组件
父组件
//子组件
5.插槽
匿名插槽
//父组件
hello world! uni-app!!!
//子组件
具名插槽
//父组件
头部
hello world! uni-app!!!
尾部
//子组件
作用域插槽
//父组件
{{user.name}}
- {{item}}
//子组件
$refs:获取子节点引用:父传子
//父组件
//子组件
$parent
和$root
:兄弟组件之间通信可通过共同祖辈搭桥
//父组件
//子组件child1
//子组件child2
$children
:父组件可以通过$children
访问子组件实现父子通信
//父组件
//子组件child
{{ title }}
注意:$children不能保证子元素顺序
$attrs
和$listeners
:父子组件
$attrs
:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
示例:
//父组件
export default {
components: {
Child
},
data() {
return {
name: 'vue'
}
}
};
//子组件
来自$attrs: {{ $attrs.title }}
来自prop:{{ name }}
$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
//父组件
//子组件
provide和inject 能够实现祖先和后代之间传值
//祖先组件
export default {
provide(){
return {
name: 'uni-app'
}
}
}
//后代组件
{{ name }}
export default {
inject: ['name']
}
在接下来的组件章节会做更进一步的介绍
开发过程中遇到的问题及解决方案
1. picker在各平台的实现是有UI差异的,如何保持统一?
使用picker-view 组件替代
2.app端利用vue开发自定义弹窗组件,无法遮挡navigationBar和tabbar
原因:小程序和App的vue页面,主体是webview渲染的,也就是嵌套了的html页面,navigationBar和tabbar都是原生组件,在uni-app中原生组件脱离在 WebView 渲染流程,层级最高,所以vue开发的前端组件无法遮挡navigationBar和tabbar
解决方案:
- 可以使用nvue开发,全部是原生渲染,不涉及层级问题;
- 具体开发请查阅uni-app官方提供的nvue教程
- plus.nativeObj.view,它是一个原生的类画布的控件,可以画出任何界面;
- 不建议使用,有3个弊端:1. api很底层,开发比较复杂;2. 不支持动画;3. 不支持内部滚动
- subNVue是原生渲染的nvue子窗体,把一个nvue页面以半屏的方式覆盖在vue页面上;
- subNVue的官方的详细介绍:https://ask.dcloud.net.cn/article/35948
- 弹出部分区域透明的nvue页面,即看起来是在本窗体弹出一个元素,但实际上是弹出了一个部分区域蒙灰透明的新窗体。
- 使用方式可以参考官方建议的插件:https://ext.dcloud.net.cn/plugin?id=953
3.自定义loading,带动图效果
当请求接口时,为了避免用户等待,在数据未返回是页面展示loading,uni-app提供了uni.showLoading这个api来使用,但是如果产品经理可能会觉得很丑,我们不得不自定义,但是官方没有提供相关接口,于是我们想到要自定义loading组件,但是在uni-app中要想每个页面使用loading组件需要在每个页面引入,可能还需要通过一些方法来控制它的显示和隐藏,如果一个页面并发多个请求时可能还会出现问题(我就经历过,ios没问题,安卓有问题,猜测和渲染的时机有关),通过查阅资料,可以通过html5+提供的方法plus.nativeUI.showWaiting来解决,具体查阅:https://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.showWaiting
示例:
#ifdef APP-PLUS
let loading;
// #ifdef APP-PLUS
loading = plus.nativeUI.showWaiting('数据加载中...', {//请求开始
background: 'rgba(0,0,0,0.3)',
color: '#fff',
width:'100%',
height: '100%',
size: '15px',
loading: {
display: 'block',
icon: '/static/11.png',//自定义图片地址
width: '80px',//图片宽高
height: '80px',
interval: '500ms'
}
});
//请求成功或失败
loading.close()
// #endif
注意:
自定义loading图标的路径,png格式,并且必须是本地资源地址; loading图要求宽是高的整数倍,显示等待框时按照图片的高横向截取每帧刷新。例如,一个gif图有10部分组成,要把这10个图拼成一个高为100px,宽10*100的png格式图片,当loading显示时,图片会通过设置好的interval时间进行动画,类似于css中的不断改变background-position值。
4.把页面内容生成图片
使用html2canvas第三方库
只适用于h5和app端,其他端请参考相关平台的api进行实现
可能有人要说这个库是基于dom的,在h5端可以使用,而app端没有dom不能使用,那么接下来就主要介绍下在app端如何使用的:
renderjs
示例:
下载html2canvas.min.js这个库,放到项目目录下的static文件夹下
renderjs示例
示例代码地址: https://github.com/BoryLee/uniapp-renderjs
html5+截屏绘制
参考资料:http://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewDrawOptions
renderjs示例
plus图片
示例代码地址: https://github.com/BoryLee/uniapp-renderjs