笔记内容主要参考uni-app官方文档,仔细阅读uni-app官方文档 介绍教程等部分能获得更多内容。

uni-app面试题

条件编译

条件编译指的是:开发时,可以为不同平台编写不同的代码,分别用#ifdef#endif,但在编译后,只保留对应平台的代码,其他平台的代码被删除,从在实现多端兼容的同时减少打包后的代码体积

页面跳转和层级

在uni-app中,页面跳转就那五个api

uni.navigateTo不关闭当前页面,跳转到非tabbar页面,会让历史记录栈长度+1

uni.redirectTo:关闭当前页面,跳转到非tabbar页面,类似vue-router中的replace。uni.navigateTouni.redirectTo的唯一区别就在于前者保留当前页面进行跳转,后者关闭当前页面进行跳转

uni.reLaunch:关闭所有页面,跳转到任意页面

uni.switchTab:关闭所有非tabbar页面,跳转到tabbar页面

uni.navigateBack关闭当前页面,回退到历史记录栈中的一个页面,包括 tabBar 页面

层级 = 页面在栈中的深度,比如

  • 首页:层级 0
  • 商品页:层级 1
  • 详情页:层级 2
  • 评论页:层级 3

navigateBack({ delta: 2 }) 表示回退 2 层,即回到“商品页”

uni.navigateBack通常配合getCurrentPages()使用

1
2
3
4
5
6
7
8
const arr = getCurrentPages()
if (arr.length > 1) {
uni.navigateBack()
} else {
uni.switchTab({
url: '/pages/home/home'
})
}

用uni-app开发和使用vue开发web有什么区别

标签不同

功能uni-app 标签Vue Web 标签说明
块级容器<view><div>viewdiv 的跨端替代
行内文本<text><span>text 支持长按选中,是唯一可选中文本的标签
图片<image><img>image 支持懒加载、缩放模式等小程序特性
按钮<button><button><div>两者都有 button,但属性不同
列表<scroll-view><list><div> + CSS 滚动scroll-view 支持滚动事件、下拉刷新
输入框<input> / <textarea><input> / <textarea>属性和事件有差异
导航栏<navigator><router-link><a>navigator 控制页面跳转
条件渲染<block v-if><template v-if>block 不渲染为节点,仅逻辑分组
视频<video><video>属性和事件不同,uni-app 更简化

因此模板中的标签和样式中的选择器都要修改

请求的方式不同

在基于vue的web开发中,一般通过axios发送请求,在uni-app中,有自己的请求api,比如uni.request

package.json文件

在hbuilder上,uni-app项目没有package.json也可以运行,因为uni-app项目使用的是自己的本地依赖

组件库

内部有许多组件库,不能使用 Element Plus这样的组件库,因为Element Plus 是为 Vue 3 + Web(浏览器) 设计的组件库,而 uni-app 是一个多端框架,存在根本性不兼容。

pinia

在uni-app中使用vue的状态管理库比如pinia无需手动下载,直接使用即可。

vue-router

不推荐使用vue-router来进行路由跳转,而是使用自带的路由跳转api,路由传参几乎只能借助查询参数,接收路由参数也不能使用useRoute,而是在onLoad钩子中接收。而且路由中的hash值无法使用(在微信小程序和app中)

dom操作

dom操作只能在浏览器上生效,所以uni-app项目不能书写任何dom操作

事件

鼠标事件和键盘事件不能使用(除非只打包为web端)

index.html文件

uni-app项目中没有index.html文件,index.html是在编译打包阶段由构建工具自动生成的,但是可以在manifest.json文件中配置模板文件。uni-app是 多端统一 框架,需要适配小程序、App、H5等多个平台,每个平台的入口文件不同,不能依赖固定的index.html,构建时根据目标平台动态生成对应的入口文件

其他

  • IntersectionObserver只能在支持浏览器的环境中使用
  • 在web开发中和uni-app跨端开发中,storage api不同,web开发中的storage api 只能在浏览器上生效,uni-app中有跨端的storage api。
  • 不能使用布局属性比如clientWidth,因为这是属于DOM的属性。
  • window和document对象也不能使用
  • 阿里图标在app上不生效,因为App 端(iOS/Android)默认禁止加载外部网络资源(安全策略);小程序端不支持动态加载外部 CSS。解决方式是将阿里图标下载到本地然后再引入 (在App.vue中引入iconfont.css)

微信小程序的发布流程

  • 在微信开发者工具中,确保已经登录自己的开发账号
  • 然后点击上传按钮,填写项目版本号和项目备注,再点击上传,然后项目就会自动进行编译打包,最后再上传。在上传前还会对代码的质量进行检测,不过即便检测未完全通过,也会上传。
  • 此时上传的代码会成为开发版本,开发版本可以被设置为体验版本,会得到一个二维码,后续所有内部开发成员都能扫描这个二维码体验小程序。可以通过搜索微信号的方式,单方面的添加体验成员。
  • 想要小程序能被正式发布,还需提交审核。在提交审核前,还需设置小程序服务类目,对于个人开发者而言,无法发布哪些允许用户自定义内容的小程序(必须支持用户评论的小程序,支持用户上传图片,视频的小程序)。
  • 审核通过后,点击发布,就能将小程序发布到线上,之后在微信中就能搜索到小程序了。

原生微信小程序和uniapp开发小程序的对比

  • 使用原生开发可以紧随官方的版本,更新响应速度快,让项目达到最优状态
  • 微信小程序和uniapp都有官方库和第三方库,uni-ui配合uView的组合功能更多更全
  • uniapp插件市场更活跃

其他

  • uniapp用过哪些插件
  • uniapp做小程序登录
  • uniapp做小程序支付
  • uniapp做小程序分享
  • 小程序引入比如腾讯地图要注意哪些

微信开发者工具预览功能

如果预览微信小程序的时候遇到了网络错误的问题,需要选择开发调试,点击开启调试

什么是uni-app

是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程快应用等多个平台

uni-app的跨端原理

uni-app分为编译器和运行时(runtime)。uni-app能实现一套代码、多端运行,是通过这2部分配合完成的。

uni-app的编译器基于webpack或者vite,能把基于uni-app规范编写的代码,编译,打包成能在特定平台上运行的代码,并这个过程中集成相应的runtime;编译打包后的代码文件,还会被组织成,在结构上符合特定平台规范的项目

编译器

编译器运行在电脑开发环境。一般是内置在HBuilderX工具中,也可以使用独立的cli版。

使用HBuilderX可视化界面创建的项目,编译器在HBuilderX的安装目录下的plugin目录,随着HBuilderX的升级会自动升级编译器。

开发者按照uni-app规范编写代码,再由编译器将其编译成特定平台支持的代码

  • 在web平台,将.vue文件编译为js,css代码。与普通的vue cli项目类似
  • 在微信小程序平台,编译器将.vue文件拆分生成wxml、wxss、js等代码。
  • 在app平台,将.vue文件编译为js代码

编译器分vue2版和vue3版

  • vue2版:基于webpack实现
  • vue3版:基于Vite实现,性能更快

这些工具本身都是基于Node.js平台开发的,因此在你的本地开发环境中必须安装 Node.js 来运行这些构建脚本。

编译器支持条件编译。在同一份代码中,根据目标平台的不同,只编译并保留对应平台的代码,其他平台的代码被忽略,减小打包体积

这解决了多端兼容问题,避免写大量 if-else 判断

1
2
3
// #ifdef  App
console.log("这段代码只有在App平台才会被编译进去。非App平台编译后没有这段代码")
// #endif

运行时环境

uni-app在每个平台都准备了相应的runtime,会在编译的过程中集成到项目中。

  • uni-app给小程序端提供的runtime,主要是一个小程序版的vue runtime,页面路由、组件、api等方面基本都是转义。
  • uni-app给web端提供的runtime,相比普通的vue项目,多了一套ui库、页面路由框架、和uni对象(即常见API封装)
  • uni-app给App端提供的runtime更复杂,可以先简单理解为DCloud也有一套小程序引擎,打包app时将开发者的代码和DCloud的小程序打包成了apk或ipa。

uni-app提供的runtime包括3部分:基础框架、组件、API

基础框架

在web和小程序上,不需要uni-app提供js引擎和排版引擎。因为浏览器有浏览器内核,可以执行渲染和运行js代码的工作;各个小程序平台也有自己的webviewjs引擎;但安卓上,需要uni-app提供谷歌v8引擎,webview则使用系统自带的;ios上也不需要uni-app提供js引擎和webview,都使用ios系统自带的。

组件

在小程序端,uni-app基础组件会直接转义为小程序自己的内置组件。提供给小程序的runtime中,基础组件不占体积。在web和android、iOS端,这几十个组件都在uni-app的runtime中,会占用一定体积,相当于内置了一套ui库。

api

uni-app runtime内置了大量常见的、跨端的API,比如联网(uni.request)、读取存储(uni.getStorage)。使用uni-app的标准API,可以跨端使用。但对于不跨端的部分(就是某个平台的特色api),仍可以调用该端的专有API。由于常见的API都已经被封装内置,所以日常开发时,开发者只需关注uni标准API,当需要调用特色端能力时在条件编译里编写特色API调用代码。

在小程序平台,uni对象会转为小程序的自有对象,比如在微信小程序平台,编写uni.request等同于wx.request。

在web平台,window、dom等浏览器专用API仍可以使用

HBuilderX

HBuilderX是通用的前端开发工具,但为uni-app做了特别强化,和uni-app属于同一家公司的产品。

基本配置

  • 插件下载:工具 -> 插件下载
  • 快捷方式定制:工具 -> 预定快捷方案
  • tab补全代码:工具->设置->语言服务配置
  • 保存代码格式化:工具->设置->编辑器配置
  • 合并代码时显示最后一行代码:工具->设置->编辑器配置

创建一个uni-app项目

在点击工具栏里的文件 -> 新建 -> 项目

选择uni-app类型,输入工程名,选择模板,点击创建,即可成功创建。

uni-app自带的模板有默认的空项目模板、Hello uni-app 官方组件和API示例,还有一个重要模板是 uni ui项目模板,日常开发推荐使用该模板,已内置大量常用组件。

也可以使用vue-cli命令行开发,不过个人还是喜欢使用HBuilder。

运行uni-app

点击左上角的运行选项,即可选择项目的运行方式

如果要运行到手机,需要连接usb,在设置中开启USB调试。如果要运行到手机模拟器,则需要先下载,比如逍遥,雷电模拟器,然后打开这些应用,才能被hbuilder检测到

将项目运行到微信开发者工具:

  • AppID:在manifest->微信小程序配置中填写自己的微信小程序的 AppID,这个是需要我们去申请的

  • 安装路径:在设置中配置“微信开发者工具”的安装路径,只有配置了微信开发者工具路径,hx才能打开微信开发者工具

  • 开启服务端口:在微信开发者工具中,通过 设置 -> 安全设置面板,开启微信开发者工具的服务端口

发布uni-app

打包为原生App

在打包之前也需要在manifest.json 的可视化界面做相关配置。

  • 在基础配置面板中,获取uni-app 应用标识,并填写应用名称
  • 切换到 App 图标配置面板,点击浏览按钮,选择合适的图片之后,再点击自动生成所有图标并替换

在HBuilderX工具栏,点击发行,选择原生app-云端打包(在线打包),可以直接将代码打包成APK文件,缺点是可能需要排队。本地打包后得到的是用于手动打包的文件,需要开发原生app的知识。

云打包完成后,在内置控制台点击链接下载 apk 的安装包,并安装到 Android 手机中查看打包的效果。

App打包时,注意如果涉及三方sdk,需进行申请并在manifest.json里配置,否则相关功能无法使用。

iOS App打包需要向Apple申请证书

发布为Web网站

manifest.json 的可视化界面,进行如下配置

如果使用history路由,就需要后端服务器做对应的配置

当我们令运行时的基础路径为/api/,打包后index.html引入文件的路径就会变成如下:

1
2
 <script type="module" crossorigin src="/api/assets/index-yJj5sN9l.js"></script>
<link rel="stylesheet" crossorigin href="/api/assets/index-Dl1z_jWj.css">

在HBuilderX工具栏,点击发行,选择网站-H5,点击会弹出这个窗口:

无需在意,直接点击发行,即可生成 H5 的相关资源文件,保存于unpackage/dist/build/web 目录。

至于部署打包后的网页,其实和部署博客一样,可以借助github-pages+vercel,或者使用dcloud免费的网页托管服务。

发布为微信小程序

  1. 申请微信小程序AppID

  2. 在HBuilderX中顶部菜单,依次点击 “发行” => “小程序-微信”,输入小程序名称和appid点击发行即可

如果手动发行(未勾选自动上传微信平台),则点击发行按钮后,会在项目的目录 unpackage/dist/build/mp-weixin 生成微信小程序项目代码。打开微信小程序开发者工具,导入生成的微信小程序项目,测试项目代码运行正常后,点击上传按钮,之后按照提交审核=> 发布小程序标准流程,逐步操作即可。

如果在发行界面勾选了自动上传微信平台,则无需再打开微信工具手动操作,将直接上传到微信服务器提交审核。

项目结构

下面只展示基础的项目结构,更详细的结构可以参考官方文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
│─components            符合vue组件规范的uni-app组件目录
│ └─comp-a.vue 可复用的a组件
├─pages 业务页面文件存放的目录
│ ├─index
│ │ └─index.vue index页面
│ └─list
│ └─list.vue list页面
├─static 存放应用引用的本地静态资源(如图片、视频等)的目录,注意:静态资源都应存放于此目录
├─uni_modules 存放uni_module 详见
├─unpackage 非工程代码,一般存放运行或发行的编译结果
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期
├─pages.json 配置页面路由、导航条、选项卡等页面类信息
├─manifest.json 配置应用名称、appid、logo、版本等打包信息
└─uni.scss 内置的常用样式变量

微信小程序中的一个页面就是一个文件夹,然后里面有四个文件,分别对应html,css,js,json文件,而在uni-app中,一个vue文件就包含了前三者,然后页面json文件被page.json中的pages.style属性给替代了。

page.json

项目页面的配置文件 ,类似微信小程序中的app.json,这种命名方式貌似着重强调了pages属性。

主要组成如下

globalStyles

全局定义页面的样式,会被pages选项中页面单独定义的样式覆盖

1
2
3
4
5
6
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#fff",
"backgroundColor": "#F8F8F8"
// "navigationStyle": "custom"
},

可以看出,page.json文件中的globalStyles属性,替换了微信小程序中的app.json文件中的window属性

pages

注册页面的地方;值是一个页面对象数组,第一个页面对象就是首屏,在pages中注册的页面(路由组件)都会被打包进主包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/home/home",
"style": {
"navigationBarTitleText": "推荐",
"navigationStyle": "custom", //自定义导航栏(隐藏原生)
"enablePullDownRefresh": true
}
},
{
"path": "pages/classify/classify",
"style": {
"navigationBarTitleText": "分类",
"navigationStyle": "custom", //自定义导航栏(隐藏原生)
"enablePullDownRefresh": true
}
}
]
}

页面对象属性:

  • path:页面组件路径
  • style:定义页面的一些样式,比如配置,"navigationStyle": "custom";配置后这个页面的导航栏直接消失了

tabbar

值是一个对象,用来配置标签栏。

要注意的是,H5 端有时会缓存旧的 pages.json 配置,导致配置了tabBar但是不显示的问题,此时就需要重新编译,重启项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"tabBar": {
"selectedColor": "#28b389",
"list": [{
"pagePath": "pages/home/home",
"iconPath": "static/home.png",
"selectedIconPath": "static/home-h.png",
"text": "推荐"
},
{
"pagePath": "pages/classify/classify",
"iconPath": "static/classify.png",
"selectedIconPath": "static/classify-h.png",
"text": "分类"
},
{
"pagePath": "pages/user/user",
"iconPath": "static/user.png",
"selectedIconPath": "static/user-h.png",
"text": "我的"
}
]
}
}

subPackages

小程序分包的核心配置属性,用来注册分包,属性的值是分包对象数组,有几个分包对象最终就有几个分包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"subPackages": [
{
"root": "subpkg", //指定分包的根目录
"name":'pkgA',//分包别名
//当前分包下所有页面的相对路径
"pages": [
"detail/detail",
"goods_list/goods_list",
"search/search"
]
}
]
}

root: "subpkg",表示这个分包的根目录,是项目下的 subpkg 文件夹,所有该分包的文件都放在 subpkg/ 目录下,而pages中的页面路径相对的都是root

分包中的页面不需要在pages.jsonpages属性中注册,pages属性中注册的页面都会被打包进主包中。

如果想要创建多个分包,需要在根目录下创建多个分包文件夹类似subpkg,然后在subPackages数组中,添加更多分包对象。

分包中不仅可以放页面组件(.vue 页面),还可以放普通 Vue 组件(.vue),静态资源(图片、字体、JSON 等),JS 工具函数等

更多内容参考:pages.json 页面路由 | uni-app官网

static

uni-app编译器根据pages.json扫描需要编译的页面,并根据页面引入的js、css合并打包文件。

对于本地的图片、字体、视频、文件等资源,如果可以直接识别,那么也会把这些资源文件打包进去,但如果这些资源以变量的方式引用, 比如:<image :src="url"></image>,甚至可能有更复杂的函数计算,此时编译器无法分析。

那么有了static目录,编译器就会把这个目录整体复制到最终编译包内,只要运行时确实能获取到这个图片,就可以显示。

当然这也带来一个注意事项,如果static里有一些没有使用的废文件,也会被打包到编译包里,造成体积变大。

static 目录下的文件(vue组件、js、css 等)只有被引用时,才会被编译打包。

简单的来说就是,static目录下的文件无论是否被引用,都会被打包进最终文件;而非static目录下的文件,只有被引用了,才会被打包进最终文件。

uni.scss

uni.scss文件的用途是为了方便整体控制应用的风格。比如按钮颜色、边框风格,uni.scss文件里预置了一批scss变量

1
2
3
4
5
6
7
....
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
.....

uni.scss是一个特殊文件,在代码中无需手动import 这个文件,即可在scss代码中使用这里的样式变量。uni-app的编译器在webpack配置中特殊处理了这个uni.scss,使得每个scss文件都被注入这个uni.scss,达到全局可用的效果。

简单来说,这就是一个只包含scss变量的文件,会被自动注入到所有scss文件中。

注意:如要使用这些常用变量,需要在 HBuilderX 里面安装 scss 插件;使用时需要在 style 节点上加上 lang="scss"

pages.json不支持scss,原生导航栏和tabbar的样式修改只能使用js api。

1
2
3
4
5
{
"navigationBarTitleText": "首页",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "$primary-color" // ❌ 不支持 SCSS 变量!
}

uni-app 提供了 JS API 在运行时修改导航栏和 tabbar。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 修改导航栏标题,有时候导航栏标题就是不能写死
uni.setNavigationBarTitle({
title: '新标题'
});

// 修改导航栏颜色
uni.setNavigationBarColor({
frontColor: '#ffffff', // 标题颜色(仅支持 #ffffff 或 #000000)
backgroundColor: '#FF0000', // 背景颜色
animation: {
duration: 300,
timingFunc: 'easeIn'
}
});

//修改 tabbar 某项
uni.setTabBarItem({
index: 0,
text: '新标签',
iconPath: '/static/new-icon.png',
selectedIconPath: '/static/new-icon-active.png'
});

main.js

是 uni-app 的入口文件,类似微信小程序中的app.js文件,命名为main.js更符合vue的风格。

main.js主要作用是:

  • 初始化vue实例
  • 定义全局组件
  • 使用需要的插件如 i18n、vuex。

说白了不就是和vue的入口文件作用一样吗,只不过在语法上略有区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
//vue2
import Vue from 'vue'
import App from './App'
import PageHead from './components/page-head.vue' //全局引用 page-head 组件

Vue.config.productionTip = false
Vue.component('page-head', PageHead) //全局注册 page-head 组件,每个页面将可以直接使用该组件
App.mpType = 'app'

const app = new Vue({
...App
})
app.$mount() //挂载 Vue 实例

App.vue

所有页面都是在App.vue下进行切换的,是应用入口文件。但App.vue本身不是页面,这里不能编写视图元素,也就是

没有<template>。而在标准的 Vue.js 项目中,App.vue 通常作为应用的根组件,并且包含 <template><script><style> 标签。

而在 uni-app 中,App.vue 主要扮演的是一个全局配置文件(沦落至此)的角色,而不是具体的页面组件。

在uni-app中的作用包括:监听应用生命周期、配置全局样式。我们可能有疑问,全局样式配置不是在uni.scss文件中进行的吗?其实不是的,我们之前也介绍过,这个文件内部只是一些scss变量,很遗憾,这个文件虽然也在项目根目录,但并不像微信小程序中的app.wxss文件一样,用来书写全局样式。

应用生命周期仅可在App.vue中监听,在页面监听无效。至此,uni-app中的生命周期就包括了:组件的生命周期,页面的生命周期和应用的生命周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>

<style lang="scss">
/*每个页面公共css */
@import "@/common/styles/common.scss";
@import "@/uni.scss";
</style>

至于应用的生命周期有哪些,下面只列举了常见的部分。

函数名说明
onLaunchuni-app 初始化完成时触发(全局只触发一次)
onShowuni-app 启动,或从后台进入前台显示,参数为应用启动参数
onHideuni-app 从前台进入后台
onErroruni-app 报错时触发
onUnhandledRejection对未处理的 Promise 拒绝事件监听函数(2.8.1+ app-uvue 暂不支持)
onPageNotFound页面不存在监听函数
onLastPageBackPress最后一个页面按下Android back键,常用于自定义退出
onExit监听应用退出

页面

uni-app项目中,一个页面就是一个 vue 文件。

在 uni-app js 引擎版中,后缀名是.vue文件或.nvue文件。 这些页面均全平台支持,差异在于当 uni-app 发行到App平台时,.vue文件会使用webview进行渲染,.nvue会使用原生进行渲染

一个页面可以同时存在vue和nvue,在pages.json的路由注册中不包含页面文件名后缀,同一个页面可以对应2个文件名。重名时优先级如下:

  • 在非app平台,先使用vue,忽略nvue
  • 在app平台,使用nvue,忽略vue

新建页面

uni-app中的页面,默认保存在工程根目录下的pages目录下。

每次新建页面,均需在pages.json中配置pages列表;未在pages.json -> pages 中注册的页面,uni-app会在编译阶段进行忽略。

通过HBuilderX开发 uni-app 项目时,在 uni-app 项目上右键新建页面,HBuilderX会自动pages.json中完成页面注册,非常更方便。同时,HBuilderX 还内置了常用的页面模板(如图文列表、商品列表等,区别于之前提到的项目模板),选择这些模板,可以大幅提升开发效率。

新建页面时,可以选择是否创建同名目录。创建目录的意义在于:

  • 如果你的页面较复杂,需要拆分多个附属的js、css、组件等文件,则使用目录归纳比较合适。

  • 如果只有一个页面文件,大可不必多放一层目录。

页面生命周期

无论组件还是页面,都是vue文件,但区别在于,注册在pages.json -> pages中的组件才能算作页面。**uni-app 页面,由于既是组件又页面,所以除支持 Vue 组件生命周期外,还支持页面生命周期函数。**

当以组合式 API 使用时,在 Vue2 和 Vue3 中存在一定区别。如果使用的是vue3语法,这些uni-app页面独有的生命周期函数,使用前也要按需导入。

函数名说明
onLoad监听页面加载,该钩子被调用时,响应式数据、计算属性、方法、侦听器、props、slots 已设置完成,其参数为上个页面传递的数据,参数类型为 Object(用于页面传参)
onShow监听页面显示,页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面
onReady监听页面初次渲染完成,此时组件已挂载完成,DOM 树($el)已可用,注意如果渲染速度快,会在页面进入动画完成前触发
onHide监听页面隐藏
onUnload监听页面卸载
onResize监听窗口尺寸变化
onPullDownRefresh监听用户下拉动作,一般用于下拉刷新
onReachBottom页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据。

页面生命周期,说白了就是页面会自动监听某些事件,并执行对应的回调函数。

官方文档还给出了生命周期流程图,下面只给出使用vue2开发的页面声明周期流程图,与vue3对应的流程图区别只在于部分生命周期函数的名称不同(beforeDestory->beforeUnmount,Destoryed->Unmounted)

tabbar页面和非tabbar页面

tabBar 页面一旦被加载,就会保留在内存中,不会被销毁,无论:

  • 通过 uni.switchTab 切换到其他 tabBar 页面;
  • 通过 uni.navigateTo 跳转到非 tabBar 页面;
  • 通过uni.navigateBack跳转到上一个页面

它们始终处于 “隐藏但未销毁” 的状态。

逻辑层和渲染层分离

在web平台,逻辑层和渲染层,都运行在统一的webview(浏览器内核)里,并没有分离,但在小程序和混合开发的app,逻辑层和渲染层被分离了。逻辑层独立成了单独的js引擎,负责执行业务逻辑;渲染层仍然是webview,负责页面渲染。简单来说,就是由单线程转换成了双线程。

要注意的是这里的app端,指的是混合开发的app,即混合了前端开发技术和app原生开发技术,而不是原生app,原生app中没有webview。

分离的核心原因是性能。过去很多开发者吐槽基于webview的app性能不佳,很大原因是js运算和界面渲染抢资源导致的卡顿,这一也说明,webview其实是能执行js代码的,只不过为了提高性能,现在主要被用来渲染。

逻辑层和渲染层分离,好处是 js 运算不阻塞渲染,缺点是,逻辑层和渲染层属于2个不同的线程,不同线程之间的通信是有一定延时的。

逻辑层详解

逻辑层是运行在一个独立的js引擎里的,它不依赖于本机的 webview,所以一方面它没有浏览器兼容问题,可以在 Android4.4 上跑 es6 代码;另一方面,它无法运行 windowdocumentnavigatorlocalstorage浏览器专用的 js API。

比如,jscore就是一个标准 js 引擎,可以正常运行标准 js(ECMAscript) `,比如 if、for、各种字符串、日期处理等。

所谓浏览器 js 引擎,就是 jscorev8 的基础上新增了一批浏览器专用 API,比如 dom;

node.js 引擎,则是 v8 基础上补充一些电脑专用 API,比如本地 io;

小程序端的 js 引擎,其实是在 jscore 上补充了一批手机端常用的 JS API,比如扫码;

安卓端的js引擎,则是在谷歌v8引擎的基础上添加了一些手机端常用的 JS API;

ios端的js引擎则是基于iOS操作系统提供的jscore

视图层详解

视图层交由webview渲染,所以什么是webview?webview是移动端原生应用中的嵌入式浏览器控件,用于在原生应用中加载网页内容,实现混合开发。

小程序平台有自己的webview组件,本质类似浏览器内核。

css语法

处理器支持

uni-app 支持less、sass、scss等预处理器,但是需要安装相应的插件

尺寸单位

uni-app 支持的通用 css 单位包括 px、rpx。

px 即屏幕像素;rpx 即响应式 px,一种根据屏幕宽度自适应的动态单位。以 750 宽的屏幕为基准,750rpx 恰好为屏幕宽度。屏幕变宽,rpx 实际显示效果会等比放大。

vue 页面还支持rem,vw,vh

样式导入

使用@import语句,可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。

在模块化开发环境中,还可以使用import "../../common/uni.css"的方式导入css文件,不过是在js文件中才行。

1
2
3
4
5
6
<style>
@import "../../common/uni.css";
.uni-card {
box-shadow: none;
}
</style>

选择器

支持的选择器包括类选择器,id选择器,标签选择器,并集选择器,伪元素选择器。

注意:

  • uni-app 中不能使用 * 选择器,可以用标签选择器来代替。

  • 微信小程序自定义组件中仅支持 class 选择器

  • page 相当于 body 节点:

    1
    2
    3
    4
    <!-- 设置页面背景颜色,使用 scoped 会导致失效 -- >
    page {
    background-color: #ccc;
    }

全局样式与局部样式

定义在 App.vue 中的样式为全局样式,作用于每一个页面。在 pages 目录下 的 vue 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 App.vue 中相同的选择器。

注意:App.vue 中通过 @import 语句导入的外联样式,一样作用于每一个页面。

css变量

uni-app 提供内置 CSS 变量

CSS 变量描述App小程序H5
--status-bar-height系统状态栏高度系统状态栏高度25px0
--window-top内容区域距离顶部的距离00NavigationBar 的高度
--window-bottom内容区域距离底部的距离00TabBar 的高度

var(--status-bar-height) 此变量在微信小程序环境为固定 25px,在 App 里为手机实际状态栏高度,好处是不需要通过JS来获取。

当设置 "navigationStyle":"custom" 取消原生导航栏后,由于窗体为沉浸式,占据了状态栏位置。此时可以使用一个高度为 var(--status-bar-height) 的 view 放在页面顶部,使用sticky布局,避免页面内容压住状态栏

背景图片

uni-app 支持在 css 里设置背景图片,使用方式与普通 web 项目大体相同,但需要注意以下几点:

  • 支持 base64 格式图片。
  • 支持网络路径图片。
  • 对于小程序,在css中使用本地图片(也就是把本地图片当作背景)是不被支持的,如果非要使用,当图片大小小于40kb,uni-app则会自动帮你把它转化成base64格式,否则需要手动转化成base64格式或者网络图片,否则就会出错。字体文件同理。

组件

自定义组件

通过uni-app的easycom, 将组件引入精简为一步。只要自定义组件,安装在项目的 components 目录下,并符合 components/组件名称/组件名称.vue 目录结构。就可以不用引用、不注册,直接在页面中使用,非常方便。

而在微信小程序中,组件的注册分为局部注册和全局注册,分别在app.json和页面的json文件中usingComponents属性下注册。

说明

easycom自动开启的,不需要手动开启,有需求时可以在pages.jsoneasycom节点进行个性化设置,如关闭自动扫描,或自定义扫描匹配组件的策略。

easycom方式引入的组件,无需在页面内import,也不需要在components内声明,即无需引入无需注册,可在任意页面使用

easycom方式引入组件不是全局引入,而是局部引入。例如在H5端只有加载相应页面,才会加载使用的组件。

在组件名完全一致的情况下,easycom引入的优先级低于手动引入(区分连字符形式与驼峰形式)。

easycom只处理vue组件,不处理小程序专用组件(如微信的wxml格式组件)

下载组件

uni-app有许多内置的组件,比如view,scroll-view。还有许多官方提供的扩展组件。

如果使用的是uni-ui模板的项目,则这些扩展组件都在创建项目的时候下载好了,都下载到了根目录下的uni_modules文件中,这样的好处就是不需要手动下载组件,直接使用即可;而且这些组件并不是会无条件地,全部被打包进最终文件。具体来说,打包工具(如 Webpack 或 Vite)会根据你实际使用的组件,来决定哪些内容会被包含在最终的应用程序中(tree-shaking)。

如果你没有创建uni-ui项目模板,也可以在你的工程里,通过 uni_modules ,单独安装需要的某个组件。导入指定项目后直接使用即可,这些组件也无需import和注册,就可以直接使用,也是得益于eazycom机制。

简单的来说,下载的第三方组件和在components目录中自定义的组件一样,不许要注册和导入,就可以直接使用

image

<image> 组件具有默认的宽高:320*240,通常令其宽度为100%,完全贴合父元素,然后设置modewidthFix,即高度随宽度等比例变化,这样配置就很像web中的img

<image> 组件支持多种缩放模式,用于控制图片在其容器中的显示方式。这些缩放模式通过 mode 属性来设置:

scaleToFill:不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素。

aspectFit:保持纵横比缩放图片,保证图片的全部内容可见,且尽可能大地显示图片。图片可能会被包含在一个矩形框内,同时保持其比例不变。

aspectFill:保持纵横比缩放图片,并且只保证图片的短边能完整显示出来,而长边部分区域可能会被裁剪掉。

widthFix:宽度固定不变,高度随宽度自适应变化,以保持图片的比例。这种模式通常用于需要确保图片宽度一致的情况下,是用的最多的。

heightFix:与 widthFix 类似,但这里高度是固定的,宽度随高度调整。

video

uni-app中video组件不同于h5中的video标签,其内置了许多功能,比如弹幕系统(弹幕系统不完善,只能设置滚动弹幕,而且在app端添加弹幕系统会导致应用闪退),滑动页面会自动调节播放进度。

由于在uni-app中只能让video进入全屏状态,且全屏状态自定义的controls无法展示,所以全屏状态只能使用默认的controls。

video组件在app端存在高度溢出问题,需要使用max-height:100%来限制。而且video的层级非常高,即便不进入全屏,也会压住所有元素(如果有位置重合的话),需要通过cover-viewcover-image来解决

用来替换web开发中的a标签。使用url属性来表示跳转的地址,但只能跳转本地页面。目标页面必须在pages.json中注册,其中的open-type属性用来指定跳转的方式,模拟那几个路由跳转api。

swiper

swiper的子元素是一个个swiper-item组件,swiper-item组件**宽高自动设置为100%**。注意:宽高100%是相对于其父组件swiper,不是相对于子组件,不能被子组件自动撑开。

然后在swiper-item中放入image组件,设置mode=widthFix再设置image宽度为100%,即可实现一个简单美观的轮播图。

uni-popup

使用频率非常高的弹窗组件,在uni-popup未被展示出来前,其内容不的结构都不会被渲染,隐藏uni-popup后,其内部的结构(包括组件)又会被销毁

常用api

概述

uni-app的 js API ,由标准JS 和 uni 扩展 API 这两部分组成。

标准 ECMAScript 的 js 仅是最基础的 js。浏览器基于它扩展了 window、document、navigator 等对象。uni-app 基于标准js扩展了 uni 对象,uni-app的几乎所有API都包含在这个对象上,并且 API 命名与小程序保持兼容。

除了 uni-app 框架内置的跨端 API(会被自动编译成特定平台对应的api),各端自己的特色 API 也可通过条件编译自由使用。

API Promise 化

异步的方法,如果不传入 success、fail、complete 等 callback 参数,将以 Promise 返回数据。例如:uni.getImageInfo()

1
2
3
4
5
6
7
8
9
10
11
12
// 正常使用
const task = uni.connectSocket({
success(res){
console.log(res)
}
})
// Promise 化
uni.connectSocket().then(res => {
// 此处即为正常使用时 success 回调的 res
// uni.connectSocket() 正常使用时是会返回 task 对象的,如果想获取 task ,则不要使用 Promise 化
console.log(res)
})

原来不用自己new Promise()啊😂

uni.request

uni.request(obj)uni-app 跨端网络请求的核心 API,封装了各端(H5、小程序、App)的原生请求能力,提供统一的调用方式。-

1
2
3
4
5
6
7
8
9
10
11
12
uni.request({
url: 'https://api.example.com/data',
method: 'GET',
data: { id: 1 },
header: { 'Content-Type': 'application/json' },
success(res) {
console.log(res.data);
},
fail(err) {
console.error(err);
}
});

object(传入api的对象)的常用属性:

url (String) 必填,请求的目标地址

  • 必须是 HTTPS(小程序强制要求)
  • 域名必须在各平台白名单中配置(如微信小程序需在后台配置 request 域名)
  • 支持相对路径(需配合 baseURL 手动处理)

data (Object/String/ArrayBuffer),发送给服务器的参数(请求体),会自动序列化:

  • Object → 自动转为 application/x-www-form-urlencodedapplication/json(取决于 header中的Content-Type)
  • String → 原样发送
  • ArrayBuffer → 用于上传二进制数据(如图片)

header (Object),设置请求头,比如

1
2
3
4
5
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
'X-Client': 'uni-app'
}
  • 不能设置 Referer(小程序禁止)
  • 小程序对 header 键名大小写不敏感

method (String) ,默认 GET,有效值:GET、POST、PUT、DELETE、HEAD、OPTIONS、TRACE、CONNECT

  • 小程序支持所有方法,某些服务器可能限制方法
  • method有效值必须大写

responseType ,默认 text,指定响应数据类型,非mime类。响应头中的Content-type和前端指定的responseType都能指导浏览器解析响应体中的数据,但是前端指定的responseType优先级更高

合法值:

  • 'text':字符串(默认)
  • 'arraybuffer':二进制数据,告诉浏览器:”别按照响应头中的Content-Type解析响应体数据,给我原始二进制”,用于下载文件、图片处理
1
2
3
4
5
6
7
8
9
// 下载图片并转为 base64
uni.request({
url: '/api/image',
responseType: 'arraybuffer',
success(res) {
const base64 = uni.arrayBufferToBase64(res.data);
this.imageSrc = 'data:image/png;base64,' + base64;
}
});

withCredentials (Boolean) ,默认 false,表示跨域请求时是否携带 Cookie,类似于 fetchcredentials: 'include'

用于需要登录态的接口,同时服务器需设置 Access-Control-Allow-Credentials: true。这个属性竟然不是请求头的属性。

defer (Boolean) , 默认 false(uni-app 3.0+)表示是否延迟请求,直到首屏内容渲染完成后再发送,用于优化首屏加载性能

适合非关键接口(如埋点、推荐列表)

建议:首屏关键接口(如用户信息)不要用 defer: true

success (Function),请求响应成功的回调函数,参数 res 对象包含:

1
2
3
4
5
6
{
data: {}, // 服务器返回数据(已解析)
statusCode: 200, // HTTP 状态码
header: {}, // 响应头
cookies: [] // 服务器 set-cookie 的 cookies(App 端)
}

fail (Function),失败回调参数err对象包含:

1
2
3
4
5
6
7
{
errCode: 1, // 错误码(uni-app 定义)
errSubject: 'Request', // 错误主题
data: '', // 可能的响应数据(部分情况)
cause: 'timeout', // 原因(如 timeout, abort)
errMsg: 'request:fail timeout' // 错误信息
}

常见 errCode

  • 1:参数错误
  • 2:网络连接超时
  • 3:网络请求失败

complete (Function),无论成功失败都会执行,适合做隐藏 loading结束 loading 动画

1
2
3
4
5
6
7
uni.showLoading();
uni.request({
// ... 配置
complete() {
uni.hideLoading(); // 无论成功失败都隐藏
}
});

uni.request的返回值

如果在调用uni.request时,传入 success / fail / complete 了参数中的一个,则返回的是一个普通的requestTask对象,可以用来中断请求

1
2
3
4
5
var requestTask = uni.request({
url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
complete: ()=> {}
});
requestTask.abort();//中断请求

如果没有传入 success / fail / complete 参数,则会返回封装后的 Promise 对象,此时无法中断请求

1
2
3
4
5
6
7
8
9
10
11
var p = uni.request({
url: 'https://api.example.com/data'
});
// 使用 Promise 风格处理
p.then((err, res) => {
if (err) {
console.error('请求失败', err);
} else {
console.log('请求成功', res.data);
}
});

媒体

uni.chooseImage(object)

从本地相册选择图片,或使用相机拍照。

object参数说明:

参数名类型必填说明
countNumber最多可以选择的图片张数,默认9
sizeTypeArray<String>original 原图,compressed 压缩图,默认二者都有
extensionArray<String>根据文件拓展名过滤,每一项都不能是空字符串。默认不过滤。
sourceTypeArray<String>album 从相册选图,camera 使用相机,默认二者都有。如需直接开相机或直接选相册,请只使用一个选项
cropObject图像裁剪参数,设置后 sizeType 失效
successFunction成功则返回图片的本地文件路径列表tempFilePaths
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明

参数类型说明
tempFilePathsArray<String>图片的本地文件路径列表
tempFilesArray<Object>、Array<File>图片的本地文件列表,每一项是一个 File 对象

示例:

1
2
3
4
5
6
7
8
uni.chooseImage({
count: 6, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: function (res) {
console.log(JSON.stringify(res.tempFilePaths));
}
});

File 对象结构如下

参数类型说明
pathString本地文件路径
sizeNumber本地文件大小,单位:B
nameString包含扩展名的文件名称,仅H5支持
typeString文件类型,仅H5支持

uni.previewImage(object)

预览图片。OBJECT 参数说明:

参数名类型必填说明
currentString/Number详见下方说明详见下方说明
showmenuBoolean是否显示长按菜单,默认值为 true
urlsArray<String>需要预览的图片链接列表
indicatorString图片指示器样。可取值:”default” - 底部圆点指示器; “number” - 顶部数字指示器; “none” - 不显示指示器。
loopBoolean是否可循环预览,默认值为 false
longPressActionsObject长按图片显示操作菜单,如不填默认为保存相册
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

关于current详细介绍参考官方文档。

uni.closePreviewImage(object)

object 参数说明:

参数名类型必填说明
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

uni.getImageInfo(object)

获取图片信息。

小程序下获取网络图片信息需先配置download域名白名单才能生效。

object参数说明:

参数名类型必填说明
srcString图片的路径,可以是相对路径,临时文件路径,存储文件路径,网络图片路径
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明

参数名类型说明
widthNumber图片宽度,单位px
heightNumber图片高度,单位px
pathString返回图片的本地路径
typeString返回图片的格式

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
uni.chooseImage({
count: 1,
sourceType: ['album'],
success: function (res) {
uni.getImageInfo({
src: res.tempFilePaths[0],//传入第一个图片临时路径
success: function (image) {
console.log(image.width);
console.log(image.height);
}
});
}
});

uni.saveImageToPhotosAlbum(object)

保存图片到系统相册。object参数说明:

参数名类型必填说明
filePathString图片文件路径,可以是临时文件路径也可以是永久文件路径,不支持网络图片路径
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明

参数名类型说明
pathString保存到相册的图片路径,仅 App 3.0.5+ 支持
errMsgString调用结果

注意

  • 可以通过用户授权API来判断用户是否给应用授予相册的访问权限
  • H5没有API可触发保存到相册行为,下载图片时浏览器会询问图片存放地址。
  • 微信小程序在2023年10月17日之后,使用API需要配置隐私协议

示例

1
2
3
4
5
6
7
8
9
10
11
12
uni.chooseImage({
count: 1,
sourceType: ['camera'],
success: function (res) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePaths[0],
success: function (res) {
console.log('save success',res.path);
}
});
}
});

文件

uni.saveFile(object)

OBJECT 参数说明:

参数名类型必填说明
tempFilePathString需要保存的文件的临时路径,只能是临时文件路径。
successFunction返回文件的保存路径,res = {savedFilePath: ‘文件的保存路径’}
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明:

参数说明
savedFilePath文件的保存路径

示例:

1
2
3
4
5
6
7
8
9
10
11
uni.chooseImage({
success: function (res) {
var tempFilePaths = res.tempFilePaths;
uni.saveFile({
tempFilePath: tempFilePaths[0],//选择第一个临时文件路径
success: function (res) {
var savedFilePath = res.savedFilePath;//保存成功后返回的文件路径
}
});
}
});

界面

uni.showToast(object)

显示消息提示框。

object参数说明:

参数类型必填说明
titleString提示的内容,长度与 icon 取值有关。
iconString图标,有效值详见下方说明,默认:success。
imageString自定义图标的本地路径(app端暂不支持gif)
maskBoolean是否显示透明蒙层,防止触摸穿透,默认:false
durationNumber提示的延迟时间,单位毫秒,默认:1500
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

uni.hideToast()

隐藏消息提示框。

uni.showLoading(object)

显示 loading 提示框, 需主动调用uni.hideLoading才能关闭提示框。

object参数说明:

参数类型必填说明
titleString提示的文字内容,显示在loading的下方
maskBoolean是否显示透明蒙层,防止触摸穿透,默认:false
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

uni.hideLoading()

隐藏 loading 提示框。

示例

1
2
3
4
5
6
7
uni.showLoading({
title: '加载中'
});

setTimeout(function () {
uni.hideLoading();
}, 2000);

uni.showModal(object)

显示模态弹窗,可以只有一个确定按钮,也可以同时有确定和取消按钮

object参数说明

参数类型必填说明
titleString提示的标题
contentString提示的内容
showCancelBoolean是否显示取消按钮,默认为 true
cancelTextString取消按钮的文字,默认为”取消”
cancelColorHexColor取消按钮的文字颜色,默认为”#000000”
confirmTextString确定按钮的文字,默认为”确定”
confirmColorHexColor确定按钮的文字颜色,H5平台默认为”#007aff”,微信小程序平台默认为”#576B95”,百度小程序平台默认为”#3c76ff”
editableBoolean是否显示输入框
placeholderTextString显示输入框时的提示文本
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

success返回参数说明

参数类型说明
confirmBoolean为 true 时,表示用户点击了确定按钮
cancelBoolean为 true 时,表示用户点击了取消(用于 Android 系统区分点击蒙层关闭还是点击取消按钮关闭)
contentStringeditable 为 true 时,值为用户输入的文本

示例

1
2
3
4
5
6
7
8
9
10
11
uni.showModal({
title: '提示',
content: '这是一个模态弹窗',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});

编译到微信小程序的注意事项

  • uni-app 中不能使用 * 选择器。只能通过元素选择器来替代
  • 微信小程序不支持a标签和i标签选择器,i标签会被编译成块级元素,如果i标签写在text标签中,则无法看见字体图标
  • 小程序的tabbar不支持midButton
  • 在微信小程序的普通组件中,只能通过getCurrentPages来获取当前页面的查询参数,不能使用onLoad,因为普通组件中没有这个钩子。
  • 在微信小程序中组件标签不区分大小写,Video会被识别为video
  • 微信小程序不支持ulli标签,更不支持这2个标签的选择器
  • 在小程序中checkbox-group中不能嵌套组件,即使组件中包含checkbox
  • 字体图标:uni-app 在编译时,会对 static 目录下的某些资源进行处理,小于 8KB 的 .ttf, .woff 等字体文件自动转为 base64 内联,这个机制是为了减少 HTTP 请求(H5 端优化),最主要的是,小程序不支持在 css 中使用本地文件,包括本地的背景图和字体文件,需以 base64 方式方可使用。
  • onPageScroll这个钩子在微信小程序中的非页面组件,无法生效

App打包需要注意些什么

  • 静态资源文件名不能包含中文

  • 打包前可以自定义应用图标和开屏图片,不能是webp格式的,uni-app不支持

  • 云打包简单快捷,只需排队。本地打包虽然速度快,但是只能生成打包所需资源,最终还要使用Android Studio 进行最终的打包,需要原生开发知识,较为复杂。

  • 如果app中包含video组件,打包前需要勾选VideoPlayer模块

  • 在App端可以给video组件添加vslide-gesture-in-fullscreen="true"来开启亮度和音量调节的手势

  • 在app端中,由于视频文件上传不能分片,所以请求体的体积有时候会很大,所以需要修改nginx的配置,以支持更大的请求体:

    1
    2
    3
    4
    5
    6
    7
    server {
    listen 80;
    server_name www.sanye.space;

    # ✅ 允许最大 500MB 的请求体
    client_max_body_size 500M;
    }
  • 在app端中,当上传文件的时候,发现上传的速率很慢,后来才发现因为上传使用的是4G流量,使用WIFI上传就没问题了,因为:移动网络的“上传”能力天生就比“下载”弱很多

    网络类型典型下载速度典型上传速度上传/下载比
    📶 Wi-Fi(家用宽带)100~1000 Mbps20~100 Mbps较均衡
    📱 4G LTE20~100 Mbps5~15 Mbps上传只有下载的 1/5~1/10
    📱 5G100~1000 Mbps10~50 Mbps上传仍远低于下载
  • 自定义开屏动画有图片跳动问题,目前这个问题官方还未解决,可以观察到B站软件自己也是有这个问题的,但是是用2张不同的图片来解决突兀的跳动效果的。

  • 在uni-app中某个元素被长按,通常会被选中且有激活样式,比如navigator标签,此时我们就可以通过添加hover-class="none"属性来解决这个问题

  • 编译到app端,输入框不会被移动键盘压住,而是会被挤压上去

  • 在非H5端,无法通过直接修改scrollTop属性来控制滚动条,因为scrollTop属于DOM属性,而是可以通过使用scroll-view组件,并动态修改scrollTop属性来实现通过JS控制滚动条

    1
    <scroll-view class="content" scroll-y :scroll-top="scrollTop">
    1
    2
    3
    nextTick(() => {
    scrollTop.value = 99999
    })
  • 在非H5端无法通过keydown事件,来实现按下enter键发送消息或者评论,在uni-app中可以使用 @confirm 事件,用于监听“软键盘上的确认/回车键”。@confirm 在 App、H5、小程序全平台都支持!

用uni-app做图片下载和图片上传的时候,如何做到多端兼容

图片下载

H5端

使用a标签加download属性,如果是跨域资源,此时需要后端设置 Content-Disposition: attachment

app端

先调用uni.downloadFile下载文件,在成功的回调success中拿到文件临时路径res.tempFilePath,如果下载的是图片,再调用uni.saveImageToPhotosAlbum方法,将临时文件下载到图库中。如果调用的是saveFile方法,即便文件下载成功也不知道去哪儿找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 封装下载和保存逻辑
function downloadAndSave() {
uni.downloadFile({
url: userStore.userinfo.avatar_url,
success(res) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success() {
uni.showToast({
title: '已保存到相册',
icon: 'success'
})
}
})
},
fail() {
uni.showToast({
title: '网络错误',
icon: 'error'
})
}
})
}

小程序端

下载图片时,最好先调用 uni.getSetting方法,检查授权情况,判断用户是否授权小程序访问图库,如果已授权,则直接下载并保存图片;如果未授权,则调用uni.authorize申请用户授权,授权成功后下载并保存图片,如果用户拒绝授权,提示用户"需要您授权保存图片到相册",如果用户同意授权,则调用uni.openSetting() 打开小程序设置页。

要注意的是 uni.getSettinguni.authorizeuni.openSetting() 这三个方法都只能在小程序端使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const onSaveAvatar = () => {
// #ifndef H5
uni.getSetting({
success(res) {
if (res.authSetting['scope.writePhotosAlbum']) {
// 已授权,直接下载并保存
downloadAndSave()
} else {
// 未授权,申请权限
uni.authorize({
scope: 'scope.writePhotosAlbum',
success() {
downloadAndSave()
},
fail() {
// 用户拒绝授权
uni.showModal({
title: '提示',
content: '需要您授权保存图片到相册',
showCancel: true,
confirmText: '去设置',
success(modalRes) {
if (modalRes.confirm) {
uni.openSetting() // 打开小程序设置页
}
}
})
}
})
}
}
})
// #endif
}

图片上传

图片上传前,需要先选择图片,通过调用uni.chooseImage 实现,这个方法在各端都支持。调用uni.chooseImage,在成功的回调函数中就能拿到选择的图片的临时路径,这个临时路径可以用来展示。

在uni-app中怎么做文件分片和分片上传

H5端

确定分片的起始字节编号,调用File对象的slice方法对文件进行分片即可。分片上传代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
uploadTask = uni.uploadFile({
url: 'https://www.sanye.space/api/uploadChunk',
formData,
file: chunk,
name: 'chunk', //文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header: {
Authorization: `Bearer ${userStore.token}`
},
success: successCb
});
const successCb = (response) => {
//这个箭头函数内部的this指向file对象
this.index++; //当前分片上传完毕后,分片号++
//如果整个文件还未上传完毕,则继续上传分片,当end == this.size的时候,恰好上传完毕
//当前分片的右区间小于文件的大小,说明还有分片需要上传
if (end < this.size) {
this.start = end; //更新左区间
this.uploadChunk(); //递归调用
} else {
this.status = "completed"; //如果上传完毕,则文件对象的修改状态
this.video_url = JSON.parse(response.data).path; //记录后端返回的视频文件路径
this.progress = 100
}
}

APP端

在uni-app中,App端不支持文件的分片,可以将整个文件视为一个分片,上传代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
uploadTask = uni.uploadFile({
url: 'https://www.sanye.space/api/uploadChunk',
formData: {
index: 0,
total: 1,
name: this.name
},
filePath: this.tempFilePath, //临时文件路径
name: 'chunk', //文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header: {
Authorization: `Bearer ${userStore.token}`
},
success: (response) => {
this.status = "completed"; //如果上传完毕,则文件对象的修改状态
this.video_url = JSON.parse(response.data).path; //记录后端返回的视频文件路径
this.progress = 100
}
});

小程序端

调用 uni.getFileSystemManager得到fs对象,然后调用fs对象的readFile按范围读取文件,进行文件分片,最终得到的也是二进制文件,但是想要上传,必须确定每个分片的临时路径,所以还需要结合writeFile将文件分片写进内存得到临时文件路径,后续用户分片上传。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const fs = uni.getFileSystemManager()
const getChunkNative = (file, start, length, chunkIndex) => {
try {
const tempChunkPath = `${wx.env.USER_DATA_PATH || uni.env.USER_DATA_PATH}/chunk_${chunkIndex}`
return new Promise((resolve, reject) => {
fs.readFile({
filePath: file.tempFilePath,
encoding: 'binary',
position: start,
length: start + length > file.size ? file.size - start : length,
success: (res) => {
fs.writeFile({
filePath: tempChunkPath,
data: res.data,
encoding: 'binary',
success: () => {
resolve(tempChunkPath) //实际上返回的是临时路径
}
})
},
fail: (err) => {
console.log(err)
reject()
}
})
})
} catch (err) {
console.error(err)
}
}
1
2
3
4
5
6
7
8
9
10
uploadTask = uni.uploadFile({
url: 'https://www.sanye.space/api/uploadChunk',
formData,
filePath: chunkPath,
name: 'chunk', //文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header: {
Authorization: `Bearer ${userStore.token}`
},
success: successCb
});

在uni-app中如何做图片裁剪

在uni-app中进行图片裁剪有多端通用的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 获取图片裁剪框在图片中的位置和大小
const { left, top, width, height } = getCropInfo();
const zoom = naturalWidth / containerWidth
const ctx = uni.createCanvasContext('myCanvas', instance.value)
// 其中props.img_url是图片临时路径
// width, height 不乘以zoom防止裁剪后的图片尺寸过大(宽高等于像素数)
// 进行绘制,将裁剪框内的全部像素,绘制到canvas中裁剪框大小的区域
ctx.drawImage(props.img_url, left * zoom, top * zoom, width * zoom, height * zoom, 0, 0, width, height);
//canvas中绘制的像素导出为图片,得到图片的临时路径,如果不是H5端,还需将其转化成base64格式
const res = await new Promise((resolve, reject) => {
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
x: 0,
y: 0,
width: width,
height: height,
success: async function(res) {
// console.log('裁剪后的图片路径:', res.tempFilePath);
// #ifdef H5
resolve(res.tempFilePath)
// #endif
// #ifndef H5
// 如果需要base64格式,可以在App端通过额外步骤转换
const base64 = await convertTempFilePathToBase64(res.tempFilePath)
resolve(base64)
// #endif
}
}, instance.value);
});
})
return res;

扩展:App

在app上做如何做微信登录

在app上如何做微信支付

在app上如何做分享功能

拿分享到微信举例,微信要允许这个“外部 App”调用微信分享功能,就必须:验证这个 App 是合法的、受信任的,所以你需要在微信开放平台上注册一个“移动应用”,提交你的 App 名称、包名、签名等信息,微信审核通过后,给你一个 AppID(wx1234567890abcdef)
你在 manifest.json 中填写这个 AppID,云打包时集成进去。

第一步,进入微信开放平台https://open.weixin.qq.com,注意,不是公众平台(mp.weixin.qq.com),是 开放平台。

第二步,登录并完善开发者资质,使用你的微信扫码登录,需要 企业认证(个人账号无法创建移动应用
需要营业执照、对公账户等,个人开发者暂时无法使用微信分享(这是微信的限制)

第三步,创建“移动应用”,进入【管理中心】,点击【创建应用】→【移动应用】,填写:应用名称(如:我的视频App),应用简介,应用图标(512x512),Android 包名(如:com.yourcompany.app),Android 签名(SHA1,从你的 keystore 获取,iOS 类似,填 Bundle ID 和证书)

第四步,提交审核,审核通过后,你会获得:AppID(如:wx1234567890abcdef),AppSecret

第五步:在 HBuilderX 中配置,打开 manifest.json,在“模块权限配置”中勾选 Share(分享),点击“配置”,填入微信开放平台的:AppID,AppSecret(可选),最后进行云打包。

扩展:nVue

nVue(Native Vue)是由 DCloud 公司开发的一个用于构建原生移动应用的框架。它允许开发者使用 Vue.js 语法来编写代码,但最终生成的是真正的原生 UI 组件,而不是 WebView 中的网页。

nvue如何实现原生渲染?

uni-app 的App 端,内置了一个基于 weex 改进的原生渲染引擎,提供了原生渲染能力。

一个 App 中可以同时使用两种页面,比如首页使用 nvue,二级页使用 vue 页面,hello uni-app 示例就是如此。

应用场景

虽然 nvue 也可以多端编译,输出 H5 和小程序,但 nvue 的 css 写法受限所以如果你不开发 App,那么不需要使用 nvue。

如果熟悉 weex 或 react native 开发,那么开发app时, nvue 是更优选择,能切实提升开发效率,降低成本。

如果不熟悉原生排版,那么建议仍然以使用 vue 页面为主,在 App 端某些 vue 页面表现不佳的场景下,使用 nvue 作为强化补充。