毕设课才讲的uniapp,不够详细且进度太慢。
为了照顾其他同学,赶出了这篇文章,细节后面再补充。如果有误,欢迎指正;觉得不错,欢迎点赞。
uni-app 介绍
- uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。你可以理解uniapp为一个vue的组件库,经过封装可以适用多个平台。
- 本文旨在让拥有vue基础的同学快速上手uniapp,如果没有uniapp基础,请移步我的vue笔记
- uniapp很多东西与vue一样,本文只介绍不同之处。
- 官网:https://uniapp.dcloud.net.cn
准备工具
- Hbuilderx (开发与编译工具)
- 微信小程序开发工具(微信小程序预览测试)
- vscode (如果使用命令行开发)
- nodejs
新建项目 / 认识界面
HBuilderX 创建 uni-app 项目
1.下载安装 HbuilderX 编辑器
2.通过 HbuilderX 创建 uni-app vue3 项目
3.安装 uni-app vue3 编译器插件
4.编译成微信小程序端代码
5.开启服务端口
小技巧分享:模拟器窗口分离和置顶
HBuildeX 和 微信开发者工具 关系
除了运行在微信开发者工具,实际开发中更多是运行在浏览器,只有完成一些模块的开发才在其他平台运行。因为uniapp编译到其他平台有延时,没有h5快。
命令行创建 uni-app 项目
通过命令行创建 uni-app 项目,不必依赖 HBuilderX,TypeScript 类型支持友好。
命令行创建 uni-app 项目:
vue3 + ts 版
# 通过 git 从 gitee 克隆下载
git clone -b vite-ts https://gitee.com/dcloud/uni-preset-vue.git
创建其他版本可查看:uni-app 官网
运行方法:
pnpm run dev:h5 # 以h5运行
pnpm run dev:mp-weixin #打包为微信小程序
如果打包为微信小程序,需要在微信开发者工具中导入dist目录下的mp-weixin
转换为命令行结构
官方插件市场不好用,还有广告,所以我拒绝
拉取命令行项目:
git clone -b vite-ts https://gitee.com/dcloud/uni-preset-vue.git
将使用HbuilderX创建的项目全部移动到src目录下面:
使用vscode或idea等打开项目(HubilderX buhaoyong),运行下面命令:
现在的运行方法:
将项目导入vscode或idea等工具,运行下面命令:
# 安装依赖
pnpm i
# 运行项目
pnpm dev:h5
然后,你可以在uniapp项目使用element plus(根据官方文档配置后):
效果:
vscode插件
如果使用vscode开发,还需要安装下面插件:
- uni-create-view :快速创建 uni-app 页面
- uni-helper :uni-app 代码提示
- uniapp 小程序扩展 :鼠标悬停查文档
内置组件
- 为了实现多端的兼容,uniapp将html原生标签封装为内置组件,可以在多端显示一致的效果
- 因此,不要在uniapp开发中使用html元素标签,例如h3,div,span等,而是使用uniapp内置组件
uniapp常用内置组件如下:
view
:类似于div,用于包裹各种元素内容。text
: 类似spanbutton
: 按钮form
:表单input
: 输入框navigator
:超连接,但是只能跳转到在page.json注册了的页面image
: 图片
更多内置组件查看官方文档
示例:
<template>
<view>
<template v-if="test">
<view>test 为 true 时显示</view>
</template>
<template v-else>
<view>test 为 false 时显示</view>
</template>
</view>
</template>
<script setup>
import {ref} from 'vue'
const test = ref(true)
</script>
目录结构
使用HbuilderX创建的工程的结构一般如下(没有package.json,不能通过npm安装依赖):
使用命令行创建的uniapp项目结构一般如下:
即使用vite创建vue工程,并将HBuilder创建的工程移动到src中
对uniapp文件的介绍:
├─pages 业务页面文件存放的目录
│ └─index
│ └─index.vue index页面
├─static 存放应用引用的本地静态资源的目录(注意:静态资源只能存放于此)
├─unpackage 非工程代码,一般存放运行或发行的编译结果
├─index.html H5端页面
├─main.js Vue初始化入口文件
├─App.vue 配置App全局样式、监听应用生命周期
├─pages.json **配置页面路由、导航栏、tabBar等页面类信息**
├─manifest.json **配置appid**、应用名称、logo、版本等打包信息
└─uni.scss uni-app内置的常用样式变量
page.json
页面和路由
- 在uniapp中,我们不再需要使用Vue Router进行路由管理,而是直接在page.json中进行路由配置。
- 创建页面:在page页面下,创建
页面名/页面名
,然后在page.json中注册 (HbuilderX自动完成) - toptar为官方实现的路由跳转组件,点击对应图标就会自动跳转到对应页面
{
// 页面路由
"pages": [
{
"path": "pages/index/index",
// 页面样式配置
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的"
}
}
],
// 全局样式配置
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#27BA9B",
"backgroundColor": "#F8F8F8"
},
// tabBar 配置
"tabBar": {
"selectedColor": "#27BA9B",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabs/home_default.png",
"selectedIconPath": "static/tabs/home_selected.png"
},
{
"pagePath": "pages/my/my",
"text": "我的",
"iconPath": "static/tabs/user_default.png",
"selectedIconPath": "static/tabs/user_selected.png"
}
]
}
}
效果:
组件自动导入
通过配置page.json的easycom属性,可以自动导入组件(包括自定义组件和第三方组件库)
安装 uni-ui 组件库 (HbuilderX创建不用安装,而是在官方插件市场下载)
pnpm i @dcloudio/uni-ui
配置自动导入组件
// pages.json
{
// 组件自动导入
"easycom": {
"autoscan": true,
"custom": {
// 导入官方组件库:uni-ui 规则如下配置
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" ,
// 导入自定义全局组件:以 XF 开头的组件,在 components 目录中查找
"^XF(.*)": "@/components/XF$1.vue"
}
},
"pages": [
// …省略
]
}
安全区域
不同手机的安全区域不同,适配安全区域能防止页面重要内容被遮挡。
可通过 uni.getSystemInfoSync() 获取屏幕边界到安全区的距离。
自定义导航配置
// src/pages.json
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom", // 隐藏默认导航
"navigationBarTextStyle": "white",
"navigationBarTitleText": "首页"
}
}
组件安全区适配
<!-- src/pages/index/componets/CustomNavbar.vue -->
<script>
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>
<template>
<!-- 顶部占位 -->
<view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<!-- ...省略 -->
</view>
</template>
页面传值
<navigator
:url="'/pages/classlist/classlist?id='+item._id+'&name='+item.name"
class="box" >
</navigator>
上面代码中的navigator可以通过Query 参数传值
使用uniapp的onload函数即可获取
import {onLoad} from "@dcloudio/uni-app"
onLoad((e )=>{
let {id,name} = e
console.log(id,name);
})
状态管理
使用hbuilder创建的项目自带pinia,无需安装
使用命令行创建,需要自己安装
pinia-plugin-persistedstate会报错,我们使用pinia-plugin-unistorage替代
# 安装pinia
pnpm i pinia
# 安装pinia持久化存储插件
pnpm i pinia-plugin-unistorage
创建pinia实例(stores/index.ts)
import { createPinia } from 'pinia'
import { createUnistorage } from 'pinia-plugin-unistorage'
// 创建 pinia 实例
const pinia = createPinia()
// 持久化
pinia.use(createUnistorage())
// 默认导出,给 main.ts 使用
export default pinia
在main.js/ts中引入pinia
import App from './App'
import { createSSRApp } from 'vue'
import pinia from './stores'
export function createApp() {
const app = createSSRApp(App)
const pinia = createPinia()
app.use(pinia)
return {
app,
}
}
基本示例:
// stores/modules/counter.ts
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 };
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++;
},
},
});
<template>
<view class="content">
<text>{{ counterStore.count }}</text>
<button @click="counterStore.increment">+1</button>
</view>
</template>
<script setup lang="ts">
import { useCounterStore } from '@/stores/modules/counter';
const counterStore = useCounterStore()
</script>
pinia持久化实例
组合式写法:
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
unistorage: true, // 开启持久化
state: () => {
return { count: 0 };
},
actions: {
increment() {
this.count++;
},
},
});
组合式写法:
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useCounterStore = defineStore('counter',
() => {
const count = ref(0);
function increment() {
count.value++;
}
return {
count,
increment,
};
},{
unistorage: true,
}
);
stores/index.ts统一导出
//...
// 模块统一导出
export * from './modules/couter'
封装http请求
使用hbuilder创建的项目自带axios,无需安装
使用命令行创建,需要自己安装
pnpm i axios
封装本地存储:
//utils/auth.js
const TokenKey = 'xxx'
export function getToken() {
return uni.getStorageSync(TokenKey)
}
export function setToken(token) {
return uni.setStorageSync(TokenKey, token)
}
export function removeToken() {
return uni.removeStorageSync(TokenKey)
}
封装实例:
//utils/request.js
import {
getToken
} from '@/utils/auth.js'
// 全局请求封装
const base_url = 'http://localhost:8080';
// 请求超出时间
const timeout = 5000;
// 需要修改token,和根据实际修改请求头
export const request = (params) => {
let url = params.url;
let method = params.method || "get";
let data = params.data || {};
let header = {
'Authorization': getToken() || '',
'Content-Type': 'application/json;charset=UTF-8',
...params.header
};
return new Promise((resolve, reject) => {
// 显示加载提示
uni.showLoading({
title: '加载中...'
});
uni.request({
url: base_url + url,
method: method,
header: header,
data: data,
timeout: timeout,
success(response) {
const res = response;
// 根据返回的状态码做出对应的操作
// console.log()
if (res.data.code === 200) {
resolve(res.data);
} else {
uni.clearStorageSync();
switch (res.data.code) {
case 401:
uni.showModal({
title: "提示",
content: "请登录",
showCancel: false,
success() {
setTimeout(() => {
uni.redirectTo({
url: "/pages/login/login",
});
}, 1000);
},
});
break;
case 404:
uni.showToast({
title: '请求地址不存在...',
duration: 2000,
});
break;
default:
uni.showToast({
title: '请重试...',
duration: 2000,
});
break;
}
}
},
fail(err) {
console.log(err);
if (err.errMsg.indexOf('request:fail') !== -1) {
uni.showToast({
title: '网络异常',
icon: "error",
duration: 2000,
});
} else {
uni.showToast({
title: '未知异常',
duration: 2000,
});
}
reject(err);
},
complete() {
// 不管成功还是失败都会执行
uni.hideLoading();
uni.hideToast();
}
});
}).catch(() => {});
};
封装请求:
//api/api.js
import {
request
} from "@/utils/request.js"
const baseUrl = "/wallpaper"
export function apiGetBanner(query) {
return request({
url: baseUrl + '/banner/list',
method: 'get',
params: query
})
}
使用:
import {
apiGetBanner,
} from "@/api/apis.js"
const getBanner = async () => {
let res = await apiGetBanner();
bannerList.value = res.rows;
console.log(res);
}
条件编译
- 通过特殊注释,以
#ifdef
或#ifndef
加%PLATFORM%
开头,以#endif
结尾。%PLATFORM%
常用取值:MP(大部分小程序),WEB(网页),APP - 多平台编译:
#ifdef H5 || MP-WEIXIN
表示在 H5 端 或 微信小程序端 代码。 - 条件编译支持: 支持 .vue, .ts, .js, .scss, .css, pages.json 等文件。
<script setup lang="ts">
// 微信平台特有API,需要条件编译
// #ifdef MP-WEIXIN
wx.login()
wx.requestPayment()
// #endif
</script>
<template>
<!-- 微信开发能力按钮,需要条件编译 -->
<!-- #ifdef MP-WEIXIN -->
<button open-type="openSetting">授权管理</button>
<button open-type="feedback">问题反馈</button>
<button open-type="contact">联系我们</button>
<!-- #endif -->
</template>
<style>
/* 如果出现样式兼容,也可添加条件编译 */
page {
/* #ifdef H5 */
background-color: pink;
/* #endif */
}
</style>
插件/依赖安装
官方插件
DCloud 插件市场
进入官方插件市场,搜索需要引入的插件(包括官方组件库uni-ui)
例如:
点击链接,跳转到uni-forms 表单 - DCloud 插件市场
点击下载并导入
重新运行项目即可
命令行
使用npm安装即可
组件库
uni-ui
引入 uni-ui 组件库:
pnpm i @dcloudio/uni-ui
配置自动导入组件:
// pages.json
{
// 组件自动导入
"easycom": {
"autoscan": true,
"custom": {
// uni-ui 规则如下配置
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [
// …省略
]
}
安装类型声明文件
pnpm i -D @uni-helper/uni-ui-types
在tsconfig.json中配置:
// tsconfig.json
{
"compilerOptions": {
// ...
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types", // uni-app 组件类型
"@uni-helper/uni-ui-types" // uni-ui 组件类型
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
"nativeTags": ["block", "component", "template", "slot"]
}
}
uView-Plus
uni-ui是官方提供的组件库,而uView-Plus是uniapp的第三方组件库
官方组件库提供的组件有限,且大部分没有示例
pnpm add uview-plus
pnpm add dayjs
pnpm add clipboard
因为uview-plus依赖SCSS,所以必须要安装此插件,否则无法正常运行。
在项目src
目录中的main.js
中,引入并使用uview-plus的JS库
// main.js
import uviewPlus from 'uview-plus'
import App from "./App.vue";
import { createSSRApp } from "vue";
import pinia from './stores'
export function createApp() {
const app = createSSRApp(App);
app.use(pinia)
app.use(uviewPlus)
return {
app,
};
}
在项目根目录的uni.scss
中,引入uview-plus的全局SCSS主题文件
/* uni.scss */
@import 'uview-plus/theme.scss';
在App.vue
中首行的位置引入uview-plus基础样式,注意给style标签加入lang="scss"属性
// App.vue
<style lang="scss">
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
@import "uview-plus/index.scss";
</style>
在项目src
目录的pages.json
中配置easycom组件模式
// pages.json
{
"easycom": {
// 注意一定要放在custom里,否则无效,https://ask.dcloud.net.cn/question/131175
"custom": {
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
// 此为本身已有的内容
"pages": [
// ......
]
}
在types文件中新建uview.d.ts文件写入下方声明代码:
declare module "uview-plus"
使用
<u-button text="提交"></u-button>
或
<up-button text="提交"></up-button>
避免与uni-ui冲突等问题参考文档
element plus
实际上,如果你使用了命令行开发,你甚至可以使用element plus
首先,安装依赖:
npm install element-plus --save
安装自动导入插件
npm install -D unplugin-vue-components unplugin-auto-import
配置自动导入(vite.config.js):
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
build: {
// 开发阶段启用源码映射:https://uniapp.dcloud.net.cn/tutorial/migration-to-vue3.html#需主动开启-sourcemap
sourcemap: process.env.NODE_ENV === 'development',
},
plugins: [
uni(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
})
],
})
现在uniapp项目可以使用element plus了:
效果: