uni-app最近超级火,各大招聘要求都把uni-app添加到任职需求,所以自己作死把公司的一个微信公众号项目用uni-app开发。
本文适合有Vue开发经验者阅读。
一、准备工作
1、下载HBuilderX编辑器
下载HBuilderX编辑器,HBuilderX是uni-app官方推荐,其中包含可视化搭建uni-app项目、运行项目、打包编译项目。
下载地址点这里,因为我只开发微信公众号项目,也是就H5项目,所有只下载标准版的就可以。
2、解决HBuilderX编辑器无法对less文件格式化问题
我是使用less作为css的预编译语言,如果你选择的是sass作用css的预编译语言,可以跳过这步。
修改这两个文件的配置,就可以解决这个问题。
把红框的内容添加进去即可。
二、创建uni-app项目
在HBuilderX编辑器中创建项目非常简单,只需要两步。
推荐选择内置uni-ui项目模板,该模板已内置大量常用组件,而且不要删除太多无用文件和代码。
创建好后项目的目录结构如图所示
- components 放置内置组件。
- pages 放置开发页面。
- static 静态资源。
- App.vue 项目页面主入口 相当Vue项目的App.vue。
- main.js 相当Vue项目的main.js。
- manifest.json 一些打包编译相关的配置。
- pages.json 配置 路由、窗口样式、原生导航栏、底部原生tabbar。
- uni.scss 配置uni-app内置的常用样式变量。
三、启动uni-app项目
启动uni-app项目,在HBuilderX编辑器中也非常简单,一步搞定。
开始编译
编译成功
在浏览器打开http://loaclhost:8080/,如果页面如下图所示,说明项目启动成功。
四、开发阶段
1、怎么去除原生导航栏
我这个项目原型中是没有原生导航栏的,所以要把它先去掉。
方法在文档这个地方这个地方。
当navigationStyle
设为custom
或titleNView
设为false
时,原生导航栏不显示。所以去除H5端的原生导航栏有两种方法。
都是在pages.json文件中
统一去除原生导航栏
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "uni-ui基础项目"
}
}
],
或者,只针对H5端去除原生导航栏。
"pages": [
{
"path": "pages/index/index",
"style": {
"h5":{
"titleNView":false
}
}
}
],
下图就是去原生导航栏后的效果
2、路由配置
目前前端项目大多都是用路由来控制页面跳转,所以入坑一个新框架,先要属性一下这个框架的路由怎么配置。
uni-app的路由配置方式和小程序很像,如果你会小程序,配置起来更容易了。
uni-app项目的路由还是pages.json中pages属性中配置,其实上面已经应用到了。
pages属性的值是个数组,数组每项是个json对象,每个路由就配置在json对象中,其中path属性是页面路径,style是配置页面的样式,我们先不看。
在uni-app项目中,我们一般把页面写在pages文件夹下,例如在pagse文件夹下添加一个叫home.vue
的文件,文件内容如下
<template>
<view>我是home页面</view>
</template>
<script>
</script>
<style>
</style>
那么我们要在pages.json中这么配置。
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/home",
"style": {
"navigationStyle": "custom"
}
}
],
刷新页面,你会发现浏览器上没有 我是home页面
。
当然这里不是配置错了。只是在uni-app有个规定pages节点的第一项为应用入口页(即首页)。所以我们要改一下。
"pages": [
{
"path": "pages/home",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom"
}
},
],
这时你会发现,编译失败了。
原因就是在uni-app中配置文件,每一项结束尾巴都不能加,
(逗号)。,把逗号去掉,在编译,成功后刷新页面,就会看到home.vue
这个页面里面的内容。
现在我们把home.vue
这个文件删掉,在编译,会报错,编译不成功。
提示我们找不到home.vue
这个文件,所以在新增/减少页面,都需要对 pages 数组进行修改!
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom"
}
},
],
重新编译,编译成功。
3、怎么引入阿里图标
阿里图标这东西,几乎在每个项目中都会使用到。
在Vue项目,我们引入阿里图标是在public文件夹下的index.html文件,通过link引入的。
但是在uni-app项目中没有index.html文件,那么要在哪里引入呢。
在前面有提到过App.vue是项目页面主入口,那我们可以尝试在这里引入。
在Vue项目中.vue文件中我们是这么引入样式
<style lang="less" scoped>
@import "./css/index.less";
</style>
去MDN查询@import的用法(入口),发现 @import可以这么用。
@import url("chrome://communicator/skin/");
那么尝试在App.vue这么引入阿里图标
<style>
@import url("https://at.alicdn.com/t/font_1811528_5xzkmbymkb7.css");
</style>
然后在pages/index/index.vue文件中这么使用
<template>
<view class="container">
<text class="iconfont iconSIMCard"></text>
</view>
</template>
刷新页面,发现引入成功。
4、关于标签的使用
如果你会小程序开发,这一点可以忽略不看。
我们之前开发微信公众号,是用H5开发的,自然可以使用html5的标签。如常用的div
、span
、img
。
那么在uni-app项目中只能用它自带的组件标签,如<view></view>
就相当<div></div>
,<text></text>
相当<span></span>
,,<image></image>
相当<img/>
,具体可点这里这里看。
5、关于图片路径问题
说到这个路径,我们首先要搞懂几个常识。
~
表示Web应用程序根目录,/
也是表示根目录,../
表示当前目录的上一级目录,./
表示当前目录。
在uni-app项目引入图片,要用内置组件标签<image></image>
,其src属性就是配置图片路径,仅支持相对路径、绝对路径,支持 base64码,但自定义组件里面使用<image>
时,若 src 使用相对路径可能出现路径查找失败的情况,故建议使用绝对路径。
在项目中我是把图片资源放在根目录的assets/images文件夹中。如果你的页面在pages文件夹中的路径很深,例如
那你是不是要在页面上这么写
<template>
<view>
<image src="../../../../assets/images/indeximg1.png"></image>
</view>
</template>
其实我们可以利用~
来改造一下。
<template>
<view>
<image src="~assets/images/indeximg1.png"></image>
</view>
</template>
在less中使用background也可以利用~
.home{
background:url(~images/card/home.png) no-repeat;
}
这样写可以避免因为少些或者多些../
导致的路径错误,配置起来也轻松。
另外我们可以像Vue项目中在根目录下添加一个vue.config.js文件,来配置webpack,当然在uni-app项目中配置和在Vue项目中配置是有差异,可以点这里看。
我们在vue.config.js文件配置一个路径别名,配置方法和在Vue项目中一样。
const path = require('path');
function resolve(dir) {
return path.resolve(__dirname, dir)
}
module.exports = {
configureWebpack: config => {
return baseConfig = {
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'assets': resolve('assets'),
'css': resolve('assets/css'),
'images': resolve('assets/images'),
}
},
}
},
}
6、关于第三方组件的使用
在uni-app中使用第三方组件都会下载到components文件夹中。所以引入第三方组件的做法和Vue项目开发一样。
如要使用 Popup 弹出层 ,这么引入
components: {
uniPopup: () => import('@/components/uni-popup/uni-popup.vue'),
uniPopupDialog: () => import('@/components/uni-popup/uni-popup-dialog.vue'),
},
7、关于页面的跳转
- 页面传参
在uni.navigateTo()
中url
参数为路径,其后可以带参数,用于页面传参,参数在新页面中onLoad
钩子函数接收,如下示例。
uni.navigateTo({
url: 'test?id=1&name=uniapp'
});
// 在test.vue页面接受参数
export default {
onLoad: function (option) { //option为object类型,会序列化上个页面传递的参数
console.log(option.id); //打印出上个页面传递的参数。
console.log(option.name); //打印出上个页面传递的参数。
}
}
- 页面栈
框架以栈的形式管理当前所有页面,记住栈是先进后出的。
当用户按左上角返回按钮、安卓用户点击物理back按键时,触发uni.navigateBack
,关闭当前页面,也就是将当前页面出栈,返回上一个页面,也就是返回页面栈中最上层的页面。当然uni.navigateBack
中OBJECT.delta
可以控制返回页面栈中第几层的页面。可以用getCurrentPages()
可以得到所有页面栈的对象。
uni.navigateTo()
,保留当前页面,将当前页面入栈,再跳转新的页面。
uni.redirectTo()
,关闭当前页面,不将当前页面入栈,再跳转新的页面。
uni.navigateTo()
, uni.redirectTo()
只能打开非 tabBar 页面。
不能在 App.vue 里面进行页面跳转。如果用浏览器刷新整个页面,会清空当前页面栈。
8、关于消息弹窗的使用
跟服务端交互时肯定要用消息弹窗,比如报错时给出错误提示。uni-app框架封装几种消息弹窗的足迹。
uni.showToast()
显示消息提示弹窗。
uni.hideToast()
隐藏消息提示框。
uni.showModal()
显示模态弹窗,类似于标准 html 的消息框:alert、confirm。
10、关于加载动画的使用
uni.showLoading()
显示 loading 提示框, 需主动调用 uni.hideLoading()
才能关闭提示框。
uni.hideLoading()
隐藏 loading 提示框。
showToast 和 showLoading 是底层同一个,所以 showToast 和 showLoading 会相互覆盖,而 uni.hideLoading()
也会关闭 showToast,所以注意调用顺序。
11、关于使用nginx反向代理出现Invalid Host header问题
点开manifest.json文件,在h5中devServer选项中设置"disableHostCheck": true
。
12、关于使用formData格式请求接口
uni-app中是用uni.request()
和服务端通信的。其中data
参数是传给服务端的数据。
最终发送给服务器的数据是 String 类型,如果传入的 data 不是 String 类型,会被转换成 String。转换规则如下:
- 对于 GET 方法,会将数据转换为 query string。例如 { name: 'name', age: 18 } 转换后的结果是 name=name&age=18。
- 对于 POST 方法且 header['content-type'] 为 application/json 的数据,会进行 JSON 序列化。
- 对于 POST 方法且 header['content-type'] 为 application/x-www-form-urlencoded 的数据,会将数据转换为 query string。
会发现uni.request()
不能发送formData类型的数据给后端。如果要发送formData类型的数据,要用uni.uploadFile()
,其作用是将本地资源上传到开发者服务器,客户端发起一个 POST 请求,其中 content-type 为 multipart/form-data。
formData
参数是用uni.uploadFile()
上传本地资源中其他额外的 form data数据。
uni.uploadFile()
中几个必填的参数,如url
、filePath
、name
可以随便填写。所以可用uni.uploadFile()
发起数据格式为formData的接口请求了。
13、关于元素尺寸的获取
因为uni-app跨平台时不支持引入jQuery等操作 DOM 的插件,H5 平台可以通过条件编译的方式引入使用。所以在uni-app中最好不要引入jQuery,可以使用uni.createSelectorQuery()
创建一个SelectorQuery
对象实例来获取DOM信息。
例如要获取<view class="p-list"></view>
的高度。
const _this = this;
uni.createSelectorQuery().select(".p-list").boundingClientRect(data => {
_this.height = data.height;
}).exec();
14、关于区域上拉刷新功能实现
用uni-app中scroll-view内置组件和第三方uni-load-more组件实现。
注意scroll-view中要设定固定高度才可以生效。
<template>
<view class="p-list">
<scroll-view scroll-y="true" @scrolltolower="lower" :style="{height:height+'px'}">
<view v-for="item in list">{{item}}</view>
<uni-load-more :status="status" iconType="auto" v-show="isShow"></uni-load-more>
</scroll-view>
</view>
</template>
<script>
export default {
data(){
return {
current:1,//页码
list:[],
height: 0,//列表区域高度
isShow:false,
status: 'loading',//加载更多的状态
}
},
components: {
uniLoadMore: () => import('@/components/uni-load-more/uni-load-more.vue'),
},
mounted() {
this.getViewHeight();
},
methods: {
getViewHeight() {
const _this = this;
uni.createSelectorQuery().select(".p-list").boundingClientRect(data => {
_this.height = data.height;
}).exec();
},
lower() {
if (this.isShow) return;
this.isShow = true;
this.current++;
this.getList();
},
getList(){
let data = new Object;
data.current = this.current;
data.size = 15;//每次加载条数
API.getRenewRecord(data)
.then(res => {
this.isShow = false;
if (res.code == 200) {
this.list = [...this.list, ...res.data]
if (res.data.length == 0) {
this.status = 'noMore';
this.isShow = true;
}
}
})
.catch(err => {
this.isShow = false;
})
}
}
}
</script>
15、关于区域下拉刷新功能实现
如果页面头部有固定内容,下拉刷新只能用scroll-view内置组件实现。 注意scroll-view中要设定固定高度才可以生效。
<template>
<view class="p-list">
<scroll-view scroll-y refresher-enabled
:refresher-triggered="triggered"
:refresher-threshold="100"
@refresherrefresh="onRefresh" :style="{height:height+'px'}">
<view v-for="item in list">{{item}}</view>
</scroll-view>
</view>
</template>
<script>
export default {
data(){
return {
current:1,//页码
list:[],
height: 0,//列表区域高度
triggered:false,//下拉刷新状态,true开启下拉刷新,false结束下拉刷新
}
},
mounted() {
this.getViewHeight();
},
methods: {
getViewHeight() {
const _this = this;
uni.createSelectorQuery().select(".p-list").boundingClientRect(data => {
_this.height = data.height;
}).exec();
},
onRefresh() {
if (this.triggered) return;
this.triggered = true;
this.current++;
this.getList();
},
getList(){
let data = new Object;
data.current = this.current;
data.size = 15;//每次加载条数
API.getRenewRecord(data)
.then(res => {
this.triggered = false;
if (res.code == 200) {
this.list = [...res.data,...this.list]
}
})
.catch(err => {
this.triggered = false;
})
}
}
}
</script>
因为在微信浏览器中,下拉时整个页面会下滑,然后导致scroll-view的下拉刷新卡顿。用以下方法解决
先在pages.json中开启该页面的下拉刷新监听
{
"pages":[
{
"path": "testList",
"style": {
"enablePullDownRefresh": true
}
},
]
}
然后在页面中监听下拉刷新,一旦下拉刷新马上执行uni.stopPullDownRefresh
把下拉刷新马上关闭。
onPullDownRefresh() {
uni.stopPullDownRefresh()
},
同时在App.vue中,把下拉刷新的页面效果隐藏
<style>
uni-page[data-page=websms] uni-page-refresh {
display: none;
}
</style>
16、IOS收起软键盘后页面不下滑的解决
<template>
<view>
<input v-model.trim="value" @blur="handleblur"/>
</view>
</template>
<script>
export default{
methods: {
handleblur() {
uni.pageScrollTo({
scrollTop: 0,
duration: 0
});
},
}
}
</script>
17、IOS6不能撑开display:flex嵌套使用的父级高度
在父级元素加上min-height
属性。或者避免嵌套
18、关于自定义打包后的index.html页面
uni-app项目中默认是没有index.html,不像Vue项目默认带有index.html。那么要用CND引入第三方库,该怎么操作。
首先在根目录下,新建index.html文件
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
})
</script>
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
</head>
<body>
<noscript>
<strong>Please enable JavaScript to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
然后在manifest.json中添加如下配置,即可。
"h5":{
"template": "index.html",
}
19、关于首屏优化的操作
开启摇树优化和预加载 然后在manifest.json中添加如下配置,即可。
"h5":{
"optimization": {
"prefetch": true,
"preload": true,
"treeShaking": {
"enable": true
}
}
}
20、关于IOS首屏加载过慢的问题。
五、部署阶段
1、 H5发布
先配置
然后打包编译
最后打包生成的文件部署到服务器上。