来龙去脉
公司有个工具类型 app 的开发需求,没有原生开发人员,这个重担就落在我这个前端头上。查阅了一些资料,目前主要有 React Native,Flutter,uni-app 三种跨端 app 开发框架。对比和参照之后,再结合公司情况,最后选择了 uni-app。
uni-app 的优势
无需原生开发人员,只需要前端,会 vue 就会 uni-app,操起键盘就是干。
踩坑之旅
CSS 样式
单位使用 upx,与微信小程序的 rpx 相似,基准宽度为 750upx,可以根据设计稿尺寸计算,计算公式:
设计稿 1px / 设计稿基准宽度 = uni-app样式 1upx / 750upx
最方便的做法是,把设计稿的宽度缩放到 750px,蓝湖上有这个功能,这样 1px = 1upx,省的换算了。
生命周期
大致上和 vue 的差不多,分成页面生命周期和应用生命周期,页面生命周期就是针对单页面的,应用生命周期就是针对整个 app 的,只能在 App.vue 中使用。
虽说同样兼容 vue 的生命周期,但是官方建议使用 uni-app 的生命周期代替。
官方推荐使用 uni-app 里的onLoad 代替 vue 里面的 created
官方推荐使用 uni-app 里的onReady 代替 vue 里面的 mounted
但是有一个坑,在组件中没有生命周期,onLoad,onShow,onReady 全部失效,所以在组件中,需要使用 created, mounted 等 vue 的生命周期。
引入 npm 包
- es6 和 commonjs 两种方式引入都支持,但是在用 es6 import 方式偶尔会出现引入不了的情况,这时就用 require 吧。
- 不能使用含有 dom 操作的库,比如 animejs、swiper 就不能用。
- 不能使用浏览器自带对象,比如 document、window、localStorage、cookie 等,我的项目中引用的 web3 库中调用了
window.XMLHttpRequest
,被迫修改源码。
使用路由
uni-app 无法使用 vue-router, 路由全部配置在 page.json 这个文件中,在页面中没有专门的 $route
和 $router
对象,仅能在页面的 onLoad 生命周期里面接受路由传参。
// 传参
let url = 'navigate/navigate?id=1&name=rou'
uni.navigateTo({ url })
// navigate.vue 页面接受参数
onLoad(option) { //option为object类型,会序列化上个页面传递的参数
console.log(option.id)
console.log(option.name)
}
使用 Vuex
常规的引入方式在 uni-app 中不行,this.$store
为undefined
,需要在 main.js 中的 vue 原型上挂载一次:
import store from './store'
Vue.prototype.$store = store
网络请求
由于前面提到的原因,uni-app 不支持使用axios,自带的 uni.request 方法又很难用,连promise 都不支持,所以我自己简单封装了一个:
import { HOST } from './config'
class Http {
/**
* get请求
* @param {*} url 请求url
* @param {*} config 请求配置项,可覆盖url
*/
get(url, data = {}, config) {
return this.init(Object.assign({}, {
url,
data,
method: 'GET'
}, config))
}
/**
* post请求
* @param {string} url 请求url
* @param {*} data 请求data
* @param {*} config 请求配置项,可覆盖url
*/
post(url, data = {}, config) {
return this.init(Object.assign({}, {
url,
data,
method: 'POST'
}, config))
}
/*@@@@@ 注意:以下方法不作为http实例使用 @@@@@*/
/**
* 初始化网络请求
* @param {*} options 请求配置
*/
init(options) {
if (options.loading !== false) {
uni.showLoading({
mask: true,
title: ''
})
}
return new Promise((resolve, reject) => {
const {
url,
data,
method
} = options
console.log('请求参数', url, data ? data : '空参数')
uni.request({
url: HOST + url,
data,
method,
success: (res) => {
uni.hideLoading()
console.log(res.data, '返回数据', url)
if (res.statusCode >= 200 && res.statusCode < 300) {
if (res.data !== null && res.data.result === 'success') {
resolve(res.data)
} else {
reject(res.data.error || res.data.message)
}
} else {
if (options.error !== false) {
uni.showToast({
title: msg,
icon: 'none',
duration: 2000
})
}
reject(msg)
}
},
fail: (err) => {
uni.hideLoading()
reject(err.errMsg)
}
})
})
}
}
export default new Http()
多语言国际化
可以使用 vue-i18n,但是 uni-app 不支持在取值表达式中直接调方法,因此,$t方法不可用,所以需要通过计算属性的方式:
<template>
<view class="uni-content">
<text>{{ i18n.invite }}</text>
<text>{{ i18n.game }}</text>
</view>
</template>
<script>
export default {
computed: {
i18n () {
return this.$t('index')
}
}
}
</script>
另外在 Tabbar 和 标题栏上的文字是在 pages.json 中写死的,翻译需要借助uni.setTabBarItem
和 uni.setNavigationBarTitle
方法:
onLoad() {
// 设置标题
uni.setNavigationBarTitle({
title: this.i18n.index.title
})
// 设置 Tabbar
uni.setTabBarItem({
index: 0,
text: this.i18n.index.assets,
})
uni.setTabBarItem({
index: 1,
text: this.i18n.index.me,
})
}
总结
对于一个功能简单不要求性能的 app 来说,uni-app 提供的功能已经足够用了,可以在不需要原生开发的情况下,迅速开发出安卓和 iOS app,虽然一些原生可以实现的功能实现不了,不过整体开发下来还是比较愉快,很多的坑还是因为多端不兼容,很适合小团队和个人开发者使用。