序言
这篇文章主要目的是分享我个人在探索uniapp期间的一些坑和整体感受,难免会有一些偏个人的主观意见,希望各位再看这篇文章之前先去看官方文档,以免先入为主。
官方文档并非同步更新的,需要同步着当前的uni-app v3版本升级 这篇文章看,这样才是比较完整的内容。
关于苹果下架H5 app 的相关新闻,请看这个官方说明 。想看更多新闻请看 公告 。
开发工具介绍及使用
因为app的开发依赖HbuilderX的app编译模块 , 所以HbuilderX这个开发工具的版本相当重要。
我们从HbuilderX更新日志可以看出,v3版本的编译器是从2.5.1版本加入正式版的,那时候是2020-01-03,直到现在过去2个月,v3版本还不算稳定,推荐是官方有推出更新就更新 (虽然每次更新填坑的时候也在挖坑)
目前的app开发(整个流程)需要官方的开发工具HbuilderX的支持。因为app的项目热开发、debug、真机运行、打包都需要这个HbuilderX 去做一个 “启动器”的角色。开发上面可以使用其他的编辑器,比如我们的vscode,保存代码HbuilderX会自动编译。
目前我使用的是最新的HbuilderX 2.6.1 ,这里有这个版本的 相关更新
开发插件相关
在菜单栏 => 工具栏下有插件安装,在这里基本能找到你想要的插件
工程构建
开发app必须要用HbuilderX ,所以项目的初始化我直接使用了官方提供的基础模板,从而走上了从零构建项目工程的道路...
第二种方式是通过vue-cli,这里官方文档 快速入手 有更加具体的说明,这里我不再赘述。
APP打包
这里介绍了发布app 的大概资料。app的打包需要HbuilderX完成,这里面涉及到开发者证书相关的一块 ,正式打包需要原生这边的一些东西。可以需询问相关的开发人员,也可以问我。
我们自己开发测试可以使用官方提供的测试证书 (打包时候的默认选项)。
插件市场
以往的从npm的地方的下载的插件通常只支持H5端, 插件市场则是uniapp相关插件主要获取来源,插件特性是能兼容多端。
插件安装方式
- 使用npm进行安装使用,这是我们比较熟悉的方式 (但目前使用的组件一般很少支持这种方式)
- 手动下载插件的zip压缩包, 解压后直接放入项目文件夹中使用
生态
uniapp已经出来这么多年了,而目前生态的状况在我看来,我是不满意的。或许是因为uniapp的稳定性不够或者其他原因,插件市场插件数量少同时质量也不够高(比起我们之前的要差上一个档次),没有一套其他第三方团队提供的比较完整地UI框架,大对数都是个人开发者在做。
文档
插件能否有效利用,文档是一个主要的因素。我个人在使用插件的时候发现大部分插件的文档并不友好(甚至连官方也不是很完善)
v3编译器带来的影响
其实自从V3编译器上线正式版之后,经历了一个月的疫情的影响和官方HbuilderX本身的各种小bug不断,目前插件市场的绝大多数组件都没有兼容v3 ,所以可以试着想想对于v3这个版本来说生态是很初始的一个状态的,生态的跟进还需要一定的时间(UI相关的插件会好很多,多半只是UI的布局受到影响)
项目顶层设计
JS API发生了一个巨大的改变,开发方式相同,但是各项api名字都不一样,但也就只是改了个名字。
出现了一系列对原生的html5+ api (加强版api)。这里会出现很多原生相关的知识点和小技巧,无疑是一大波知识点的补充。
也有部分js和vue的api无法使用,主要原因的一个功能在某个端不存在想对应的概念,或者说不支持这个特性。
比方说微信小程序的请求是不支持发送cookie凭证,而我们常用的H5是可以做到的,cookie凭证在我们的文件上传一类的接口中需要使用到
就是这样,可能是因为官方为了让多端的表现统一,所以在接口设计上就会统一砍掉,保持开发上的一致性。 这样的情况从我来看不在少数
但实际上如果 配置 发送cookie凭证 , 实际上是可以使用的(前提是那个端支持), 同时也不会报错 (注意使用条件编译), 但是官方文档并不会提到这一点
页面适配方案及布局
适配方案
尺寸单位。按照文档,直接使用rpx,简单易用,使用750px宽的的设计稿即可 (什么?给的是1242?自己用ps调)。
我目前99%都是使用rpx, 只有在做少部分比如一些边框的那种细线的时候才会使用到px,但并不是说px使用的场景就很少,这需要各位的挖掘。
并不是现在的所有第三方插件都使用了rpx, 因为有些组件有点年代了, 它们大量使用着upx和px。选择第三方插件的时候请多多注意。
布局和样式
官方首先推荐flex布局, 当然flex本身就是个很不错的选择,我个人也推荐这个,当然同时我们也能使用原本就有的布局方法,这些在vue页面都是支持的(nvue的布局是例外)。
每个页面的css自带作用域, 不用在style中额外声明scoped
样式重置相关
在 uni-app 中不能使用 * 选择器。样式重置是个问题, 比如我们常见的一些
*{
box-sizing:border-box
}
这个盒子模型贯穿我们整个项目的一个布局思路, 但目前我自己使用只能每个单独去处理, 比较不方便, 同时没有遇到较好的解决方法
UI框架
多端UI库的基本认识 ,下面是介绍我所知道的大多数开发者会使用的UI库。
基础组件
基础组件会是我们业务开发中很重要的组成部分。官方放在基础组件这个位置就说明了态度。
性能来说最好(官方),但UI上来说特别简单(这里input默认情况连边框都没有),需要自己的额外修饰
uni-ui
优点:官方团队维护的组件库, 稳定性和支持性有保证。
缺点:难看, 且业务型组件不够丰富, 有的组件甚至没有样式 (也可以侧面说可开发的部分很多)
这里附上 uniapp相关组件的在线预览
这里有个很奇妙的问题 , 下面这个是组件的引入方式, 在组件中单独引入没有问题, 但当在main.js中想做全局组件引入就会报错 , 目前没有探明原因
import { uniLoadMore } from '@dcloudio/uni-ui/'
ColorUI
- css库,为数不多的使用人数多的,比较好看的UI之一,没有使用文档! 使用纯靠复制粘贴
- 并没有对组件进行功能上的封装 , 样式的命名上面也不够谨慎
- 不支持nvue
我个人是及其不推荐使用这个库的,样式的写法可以作为参考
ThroUI
- 我个人没有使用,但是觉得还可以的一个很完善的有功能封装的UI框架,推荐尝试使用(踩坑)
- 不支持nvue
graceUI
- 各方面看起来支持性最好的一个第三方UI框架,但是要钱! 200一个月,没钱
- 各方面支持性很好,支持nvue
vant-weapp
- 你没有看错, 这是vant对微信小程序组件推出的框架, 但uniapp本身是支持微信小程序组件的, 当然不好的地方是只支持编译 app、微信小程序、H5,其他小程序不支持。
- 同时官方说明使用微信小程序从性能上来说比常规的vue组件、页面性能要差, 这点希望考虑进去。
- 不支持nvue
- 至少组件很全,还有一个专门的团队在维护呀
个人意见
开发上面我个人推荐是基础组件+ uni-ui + 任意一款第三方UI框架, 首选稳定的官方提供的框架,然后在部分需求去选择业务组件。
v3版本刚出的情况下,难免会出现比较多的问题。特别是那种个人开发者做的独立插件。从UI看来,nvue页面的业务开发速度上明显会落后于常规vue页面
webview
https://uniapp.dcloud.io/component/web-view
我个人总感觉这是个很关健的组件,使用这个组件能完全模仿出以前传统的webiview+ h5的方式。同时通讯各种的api都很齐全。
ps : 目前我使用方面主要是用来app内部打开外链使用。当时我遇到这个需求的时候,期望能找到api直接调用打开webview的方法,我个人认为uniapp的接口设计上是会有这么个接口的,但我寻找了很久都没有找到类似的接口,于是我只能单独做了个路由页面去做webview以处理app内部打开外链。
nvue
nvue,native vue,即原生的vue,这是这个文件后缀名的来源 。
相关资料
坏处
- 正如前面所提到的UI框架对nvue的支持普遍不好,意味着nvue页面业务开发速度会受到不少的影响
- nvue是基于weex ,意味着使用上你需要接受weex的各种限制,这是个全新的知识点,你需要花想当的时间去消化吸收 (从样式限制 来看,布局上就能受到不小阻碍)
- 公用文件相关。在nvue中,我们可能会使用一些公用的组件,但如果想在nvue页面中使用, 组件也得按照weex的规则去写,否则就会报错无法使用,这意味着所有的公用的组件也许你都得考虑做nvue的兼容,这是个不小的工作量。
- 特别有意思的是官方的uni-ui 组件都普遍是在 2019-11-12 才支持nvue(可以点几个组件看看,里面有更新日志),这里面有值得挖掘的部分。虽然目前不清楚之前的nvue发生了什么,连官方都是最近才做完的兼容,我们有理由怀疑nvue整体的可靠性
- nvue 专用组件使用方面比较诡异
- 调试困难,即便是官方有debug工具, 但能看到的信息不多,调试起来的感觉比较像猜
好处
- 高性能。官方一直推荐的部分需要高性能的场景就推荐使用nvue。个人感受:如丝般顺滑
个人意见
我个人使用过nvue, 综合考虑下来是不推荐使用,原因如上面所述。个人是否承担nvue页面给整个项目带来的负担及后续的维护成本(还有面对各种无法难以形容的开发情况保持良好的心态)
http请求
我试用了插件市场中差不多三款http的插件,最终选择了 luch-request ,因为其余的插件封装不完整,还有细微的bug和不支持的功能。
使用方法与 axios基本相似,少部分不同 , 需要自己对请求进行再封装 。
存在的问题
目前这个插件还不支持abort
个人意见
插件市场有好几个模仿axios的的http请求库, 在我的认识里面相似及其功能的插件对于大多数人来说只需要一款,绝对不会出现第二款。而目前在插件市场就出现了3款,且使用人数都相差不大。所以目前插件市场出现这种现象是比较令我感到奇怪,侧面证明uniapp的生态还不算稳定。
反馈
文件上传, 出现问题 自定义出现难度
vuex相关
使用方式基本没有差别
路由
pages.json, 文档明确说明了所有的页面路径都是需要在这里配的, 同时还可以在这里设置对应页面的导航栏的各种自定义按钮呀, 搜索栏, 透明等等。(注意每次配置完后你的热开发都是要重启的,这个不像我们的webpack这块能自动编译)
参数的接收
//钩子
onLoad(query){
//query 就是对象参数, 中文的参数需要使用 encodeURIComponent 和 decodeURIComponent转换处理
}
个人意见
我认为上述的这种做法并不是很好,因为我认为的这种中文转码应该内置处理了,就像vue-router本身不存在这样的问题。
插件市场的 vue-router 是个不错的选择,这个是插件是在uniapp中模拟vue-router,使用之后大致上能延续以往习惯的开发方式,包含了vue-router原本就比较丰富的api。但是目前还不能在v3版本使用,因为v3版本的部分问题没有修复,如果修复了可以尝试去跟进
我目前在这个作者群, 这是作者的相关项目 uni-simple-router ,如果方便的可以给作者点个star, 秋梨膏 ~
反馈
原生api用着还可以
资源路径
资源路径说明 ,如果背景图片引用上出现问题,参考背景图片- 绝对路径 引用图片
背景图片相对路径引用失败可能是v3版本的一个暂时性的bug
反馈
少数有bug
字体图标 iconfont
跟原本常规使用方式一致,使用icomoon即可。部分不适合/不能使用字体图标的场景使用图片代替即可
引入方式
不能像传统项目直接在main.js中直接引用,HbuilderX会报出App平台 v3 模式暂不支持在 js 文件中引用***.css
关于这个暂不支持可能后续会有解决方案,这里将会持续关注
这里提供一个可用的引入方式,就是在App.vue中引入,代码如下:
/* #ifndef APP-PLUS-NVUE*/
/*字图标片icomoon */
@import "static/icomoon/style.css";
/* #endif */
以我们常用的icomoon为例, 我们将icomoon放在static文件加下面并通过上述方式引用, 但是这时候会报错, 提示字体文件 ttf/eot找不到 , 我们需要对其中引用tff文件的引用路径做一下小小修改,下面是例子
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?8v7q4m');
src: url('fonts/icomoon.eot?8v7q4m#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?8v7q4m') format('truetype'),
url('fonts/icomoon.woff?8v7q4m') format('woff'),
url('fonts/icomoon.svg?8v7q4m#icomoon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}
我们需要所有的路径改为绝对路径 , 也就是图中的fonts/icomoon 改成 ~@/static/icomoon/fonts/icomoon (这里的路径请根据自己的文件结构自行调整), 修改完后就能够正确使用 。
当然这么做也有其缺点
- 每次从字体图标库打包了新的文件下来后都要修改.
- 还有icomoon预览用的html就失效了 🙂 , 因为~@是uniapp特有的一个规则,在uniapp中才能正常编译使用,而index.html的路径是常规的引用规则。
这里实际上还有另外一个解决方式, 那就是字体文件转换成base64的方式,直接内联进去,这样就不会有字体文件引用的问题
此时或许有人注意到我在上面提供的例子中, 是通过条件编译来引入字体图标的css文件的 , 在nvue的页面中不引人这个css文件, 这么做的原因是主要因为下面这段
[class^='icon-'],
[class*=' icon-'] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
这只是一个例子, 因为nvue的 css使用规则是基于weex的, 在这套规则中不能识别属性选择器, 会提示报错。这意味着我们放弃了在nvue文件中使用我们自定义的字体图标 ,目前没有找到比较方便的解决方法 ,在nvue页面推荐使用官方提供的icons 组件做字体图标。
调试
调试相关文档 , 正常情况只需要在电脑浏览器上对H5进行调试即可, 基本上H5跟app的表现是一致的, 当遇到比较复杂的问题, 就可以考虑debug调试
普通H5调试
菜单栏 =>运行 => 运行到浏览器 (选择自己想要打开的浏览器)
真机调试vue!页面
菜单栏 => 视图 => 显示webview控制台 (其实很像 google 的 inspect)
真机调试Nvue页面
关于-app-的调试debug , 连接失败可以尝试关闭电脑防火墙,同时电脑和手机要处于同一wifi下
推送和统计
https://uniapp.dcloud.io/cloud
uniapp插件开发指南 (包含原生sdk相关)
业务组件
目前这里讲的组件都是uni-ui, 看之前推荐去快速体验 下载app,边实时预览边看。
Toast
uni.showToast({
title: error_message,
duration: 2000,
icon: 'none', //不想要icon需要手动取消
position: 'bottom'//这个位置的显示会特殊点
})
- 使用上颇为麻烦, 因为默认是有icon的, 所以每次都需要手动icon: 'none' (我很希望能找到一个全局设置的方法)
- position: 'bottom' 这里面比较特殊,显示的效果是特殊的轻提示 (用app能看出明显区别)
Drawer
这个侧边栏使用的场景不用我说, 一般首页普遍都会有。这里我想提醒的是这个组件的问题。
- 先将这个组件开启遮罩,这个组件会有滚动穿透的现象,说明这个遮罩功能是不完善的,但是目前官方仍然没去处理, 官方的想法我暂时看不懂
- 这个组件的遮罩有个范围,表现为不覆盖 tabbar(底部栏),同时也不覆盖导航栏,这种遮罩在部分场景下是不符合需求的
解决方法
- 放弃使用官方提供的组件,去插件市场换个组件
- 手动有subNvue实现
List
我在项目中使用完这个组件之后,我意识到了为什么文档不友好体现在哪里。
首先我们进去一看 示例,这不就是我们移动端常见的cell吗?
如果认真看虽然很像cell,但是却没有提供插槽, 我不认为加入个简单的插槽都这么困难,这里面发生了什么,但原因我目前仍然没有查明
我们继续看文档, 看向使用方式会发现使用的时候需要同时引入uniList,uniListItem这两个组件 ,很明显uniListItem 这个组件基本上就是cell, 同时你会发现当然文档并没有uni-list的一个文档或者是相关说明。
我当时就裂开了, 官方文档竟然能这么写
为了解决这个疑问,我们首先来看源码
uni-list
<template>
<!-- #ifndef APP-NVUE -->
<view class="uni-list">
<slot />
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<list class="uni-list" :enableBackToTop="enableBackToTop" loadmoreoffset="15" :scroll- y="scrollY" @loadmore="loadMore">
<slot />
</list>
<!-- #endif -->
</template>
uni-list-item
<template>
<!-- #ifdef APP-NVUE -->
<cell>
<!-- #endif -->
<view :class="disabled ? 'uni-list-item--disabled' : ''" :hover-class="disabled || showSwitch ? '' : 'uni-list-item--hover'"
class="uni-list-item" @click="onClick">
<!-- ...其余代码 -->
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
可能这里放代码不合适,可以去npm地址 下载下来看源码会更适合。
这里我们应该把目光着重放在 其中的条件编译部分的代码,会发现这两个组件会在nvue页面中, 使用 list、cell 这两个nvue专用的原生组件 , 文档明确说明了这两个组件之间的关系, 即
能作为list组件的一级子组件的组件只有cell、header、refresh、loading,而cell就是其中1个; 同样的能作为cell组件的父组件的只有list、recycler、waterfall,而list是其中一个
让我们尝试一下错误示范, 在nvue页面中,如果我们如下方这么写,那么就会提示报错,因为这是不符合规范的
<list>
<view></view>
<!--或者是其他规范外的组件-->
</list>
<!--or-->
<view>
<cell></cell>
<!--或者是其他规范外的组件-->
</view>
至此我们能理解为什么uni-list和uni-list-item会在文档中成对出现了, 多半是不希望有人在nvue页面使用这个组件的时候出错。但文档并没有能对这个情况做一个很好的说明,能想象得出来,会出现这种情况并不会只是在这个组件中。
官方已经为所有的组件进行了nvue的适配,无论是否使用nvue,至少我们知道使用nvue的时候会遇到更多的障碍,各位在使用的时候要谨慎应对。
知识点
小于1px的细线条
/*其实跟vant的方案是一样的, 通常的1px看起来是会有点粗,只需要设置1px再通过transform 之后就可以达到想要的细线条,这个方法在hbuilderx2.6.1.202002261 后证实有效*/
&::after {
content: " ";
position: absolute;
height: 1px;
top: 50%;
right: 0;
left: 0;
background-color: #d2d2d2;
transform: scaleY(0.5);
}
富文本解决方案
这里是指类似v-html的过程
大部分情况下我们使用v-html, 替代方案 插件市场富文本
报错日志
这个我认为也是比较重要的东西, 因为有些错误只会在特定情况下发生(app的确很容易这样),事后的问题重现难度大。应该要统一做个错误日志,好方面问题排查
比如用onError的钩子去处理
https://uniapp.dcloud.io/use?id=常见问题
目前并未探索出比较好的错误预警方式!
顶部栏和底部栏相关高度
https://uniapp.dcloud.io/frame?id=css%e5%8f%98%e9%87%8f
自定义导航栏
实际上官方提供的原生导航栏本身就很强大!包括了各种你想的到的常见需求,例如 常见搜索栏, 各种按钮 uni-app导航栏开发指南, 官网文档对应部分
但同时应该也要注意相关问题,避免开发上面出现比较大的问题。
当需求特别诡异的时候,可以考虑 前端模拟导航栏
下拉刷新
原生自带的下拉刷新我全局关掉了,目前我的做法是在需要地方单独做下拉刷新 (nvue页面),但并没有对下拉刷新进行抽象 (官方的示例中也不抽象)。
常用列表的下拉刷新方案 (vue)
应用生命周期 ,每个页面提供了对应的下拉刷新 onPullDownRefresh和上拉刷新 onReachBottom的 生命周期.
下拉刷新的更多妙用 , 这里你甚至能手动调用下拉刷新
登录模块
实现方面可以参考HBuilderX新建uni-app项目时的登陆模板。 登录页不一定要设为首页
页面呢..及相关微信登录等其他第三方登录 , 可以参考登录模板或者插件市场的相关代码。
总体来说做起来不会太难
扫码 (二维码相关)
返回逻辑自定义
https://ask.dcloud.net.cn/article/35120
返回上个页面并刷新(回调)
使用场景
常用是提交表单之后返回上个页面并刷新列表
let page = getCurrentPages();//获取页面栈
let prevPage = pages[pages.length - 2];//获取你想操作的对应页面
// #ifdef APP-PLUS
prevPage.$vm.test();//app端写法
// #endif
// #ifndef APP-PLUS
prevPage.test();//调用列表页面方法
// #endif
uni.navigateBack();//跳转页面的操作,这个一定要放在后面不然page 这些就会失效
//test 是对应页面的对应方法
没有, 因为其他平台式并不一定有dom这个概念的
其他推荐
感谢 李坤泉 前端大佬的投稿...