Vue与Uni-APP教程
本文最后更新于:2024年6月21日 凌晨
ES6语法
Promise语法
回调函数
举例就以uniapp里面的网络请求uni.request()为例了,如果是微信小程序wx.request()也是一样的,还有jQuery的ajax(),这些都是异步请求,通过success回调函数获取数据的,而axios网络请求已经封装了promise了。
例如我们先要获取文章分类列表的id,再得到数据后,通过id获取该分类下的所有文章,再通过文章的id获取文章下的评论,最终获取该文章的所有评论:
getData() {
//获取分类列表id
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/navlist.php",
success: res => {
let id = res.data[0].id
// 根据分类id获取该分类下的所有文章
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/newslist.php",
data: {
cid: id
},
success: res2 => {
//获取到一篇文章的id,根据文章id找到该文章下的评论
let id = res2.data[0].id;
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/comment.php",
data: {
aid: id
},
success: res3 => {
//找到该文章下所有的评论
console.log(res3)
}
})
}
})
}
})
上面的代码,有多层嵌套,出现多个success回调,按照这样写的话,可维护、可读性很差,下面开始改造:
onLoad() {
this.getNav(res => {
console.log(res);
});
},
methods: {
getNav(callback) {
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/navlist.php",
success: res => {
callback(res)
}
})
},
}
我们在getNav()
中可以传一个回调函数callback
,将请求的结果调用回调callback(res)
,下面全部改写回调函数:
onLoad() {
// 调用获取导航列表后,将结果调用其他函数时,额外传入一个回调函数
this.getNav(res => {
let id = res.data[0].id;
this.getList(id, res => {
let id = res.data[0].id;
this.getComment(id, res => {
console.log(res);
})
});
});
},
methods: {
// 获取导航列表
getNav(callback) {
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/navlist.php",
success: res => {
callback(res)
}
})
},
// 获取新闻列表
getList(id, callback) {
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/newslist.php",
data: {
cid: 51
},
success: res => {
callback(res);
}
})
},
// 获取当前新闻的评论
getComment(id, callback) {
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/comment.php",
data: {
aid: id
},
success: res => {
callback(res);
}
})
},
}
这样的话仔细看来,并没有解决回调地狱的问题,还是回调里面嵌套回调,只是把函数独立出来了,看着清晰条理了一些而已,但是维护难度还是有的,所以随着ES6的普及,这种方案逐渐边缘化,取而代之的就是promise方案了。
什么是promise
promise是解决异步的方法,本质上是一个构造函数,可以用它实例化一个对象。对象身上有resolve、reject、all,原型上有then、catch方法。promise对象有三种状态:pending(初识状态/进行中)、resolved或fulfilled(成功)、rejected(失败)
- pending。它的意思是 “待定的,将发生的”,相当于是一个初始状态。创建Promise对象时,且没有调用resolve或者是reject方法,相当于是初始状态。这个初始状态会随着你调用resolve,或者是reject函数而切换到另一种状态。
- resolved。表示解决了,就是说这个承诺实现了。 要实现从pending到resolved的转变,需要在 创建Promise对象时,在函数体中调用了resolve方法
- rejected。拒绝,失败。表示这个承诺没有做到,失败了。要实现从pending到rejected的转换,只需要在创建Promise对象时,调用reject函数。
通过代码打印promise
的方法:
console.dir(Promise)
使用Promise进行改造:
onLoad() {
this.getNav().then(res => {
console.log(res);
})
},
methods: {
// 获取导航列表
getNav() {
return new Promise((resolve, reject) => { // 返回promise对象
uni.request({
url: "https://ku.qingnian8.com/dataApi/news/navlist.php",
success: res => {
resolve(res);
},
fail: (err) => {
reject(err);
}
})
})
},
}
最后的调用变为了.then的链式调用:
//promise链式调用
this.getNav().then(res=>{
let id=res.data[0].id;
return this.getArticle(id);
}).then(res=>{
let id=res.data[0].id;
return this.getComment(id);
}).then(res=>{
console.log(res);
}).catch(err => {
console.log(err);
})
如果想要等待所有请求全部加载完成再响应,可以使用:
onLoad() {
let p1 = this.getNav();
let p2 = this.getArticle();
let p3 = this.getComment();
Promise.all([p1, p2, p3]).then(res => {
console.log(res);
})
}
await/async异步处理同步化
这两个命令是成对出现的,如果使用await没有在函数中使用async命令,那就会报错,如果直接使用async没有使用await不会报错,只是返回的函数是个promise,可以,但是没有意义,所以这两个一起使用才会发挥出它们本身重要的作用。
这两个命令怎么用那,还是通过上面的案例,来该着一下then的链式调用代码。
async onLoad() {
let id, res;
res = await this.getNav();
id = res.data[0].id;
res = await this.getArticle(id);
id = res.data[0].id;
res = await this.getComment(id);
console.log(res)
},
以上代码就是最终的改造版了,可以看到onload是函数,这个函数必须有async命令,在调用函数的部分,前面都加了一个await,这个命令的意思就是等这一行的异步方法执行成功后,将返回的值赋值给res变量,然后才能再走下一行代码,这就是将原来的异步编程改为了同步编程,这就是标题提到的“异步处理,同步化”。
总结:如果涉及到网络请求没有依赖关系的话,异步请求是效率最高的,但是下一个的方法依赖于上一个网络请求的结果,那么久必须使用await命令,将异步结果等待返回之后再执行后面的代码。
Vue3
搭建脚手架
npm create vue@latest
然后根据自己需要进行脚手架的设置:
D:\我的文件\Desktop>npm create vue@latest
Vue.js - The Progressive JavaScript Framework
√ 请输入项目名称: ... vue3
√ 是否使用 TypeScript 语法? ... 否 / 是
√ 是否启用 JSX 支持? ... 否 / 是
√ 是否引入 Vue Router 进行单页面应用开发? ... 否 / 是
√ 是否引入 Pinia 用于状态管理? ... 否 / 是
√ 是否引入 Vitest 用于单元测试? ... 否 / 是
√ 是否要引入一款端到端(End to End)测试工具? » 不需要
√ 是否引入 ESLint 用于代码质量检测? ... 否 / 是
正在构建项目 D:\我的文件\Desktop\vue3...
项目构建完成,可执行以下命令:
cd vue3
npm install
npm run dev
项目目录
.vscode --- VScode工具的配置文件
node_modules --- Vue项目的运行依赖文件夹
public --- 资源文件夹(浏览器图标)
src --- 源码文件夹
.gitignore --- git忽略文件
index.html --- rHTML文件
package.json --- 信息描述文件
README.md --- 注释文件
vite.config.js --- Vue配置文件
插件
在官方推荐的编译器VSode
中,安装Volar
插件,用来支持Vue语法
还可以在这个插件中设置,在使用Ref数据使,自动添加**.value**
在VScode
的设置中,选择:扩展 -> Volar ,勾选==自动输入value==
父子间组件传值
父 => 子
子组件son.vue
<template>
<view>{{title}}</view>
</template>
<script setup>
const props = defineProps({
title: {
type: String
}
});
</script>
在父组件中:
<template>
<view>
<son title="这是父传子的内容"></son>
</view>
</template>
子 => 父
子组件son.vue
:在emit中**send
**是自定义事件,在父组件接收时,需名字相同
<template>
<input type="text" v-model:value="text">
<button @click="onClick">发给父组件</button>
</template>
<script setup>
import { ref } from "vue";
const text = ref("");
const emit = defineEmits(['send'])
const onClick = () => {
emit('send', text.value);
}
</script>
父组件:在父组件中,接收son
子组件的值,使用**send
**去接收
<template>
<view>
<son @send="receive"></son>
</view>
</template>
<script setup>
const receive = (e) => {
console.log(e);
}
</script>
示例:
Uni-App
编写Uni-App,推荐使用官方的IDE:HBuilderX,编写微信小程序,还需要下载微信开发者工具:微信开发者工具
引入插件
DCloud有活跃的插件市场,https://ext.dcloud.net.cn,并提供了变现、评价等机制。
DCloud插件市场将插件分为前端组件、JS SDK、uni-app前端模板、App原生插件、uniCloud等7大类、20多个子类。
比如安装uni-ui
:uni-ui,点击下载插件并导入HBuilderX
云存储
云存储的上传方式有3种:
web界面:即在https://unicloud.dcloud.net.cn web控制台,点击云存储,通过web界面进行文件上传。该管理界面同时提供了资源浏览、删除等操作界面。
客户端API或组件上传:在前端js中编写
uniCloud.uploadFile
,或者使用uni ui的FilePicker组件,文件选择+上传均封装完毕。<uni-file-picker v-model="imageValue" fileMediatype="image" mode="grid" @select="select" @progress="progress" @success="success" @fail="fail" ref="files" />
云函数上传文件到云存储:即在云函数js中编写
uniCloud.uploadFile
使用云函数js编写:
自定义上传页面
<template>
<view class="file">
<view class="uploadGroup">
<view class="box" v-for="(item, index) in temFiles" :key="index">
<image :src="item" mode="aspectFill"></image>
<view class="close" @click="onClose(index)">×</view>
</view>
<view class="box add" @click="addFile" v-show="temFiles.length<maxSize">+</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
temFiles: [],
maxSize: 9
}
},
onLoad() {},
methods: {
addFile() {
uni.chooseImage({
success: res => {
this.temFiles = [...this.temFiles, ...res.tempFilePaths].slice(0, this.maxSize);
}
})
},
onClose(e) {
this.temFiles.splice(e, 1);
}
},
}
</script>
<style lang="scss" scoped>
.uploadGroup {
padding: 30rpx;
display: flex;
flex-wrap: wrap;
.box {
margin-left: 15rpx;
margin-bottom: 15rpx;
width: 200rpx;
height: 200rpx;
background: #eee;
position: relative;
image {
width: 100%;
height: 100%;
}
.close {
position: absolute;
right: 0;
top: 0;
width: 50rpx;
height: 50rpx;
background: rgba(0, 0, 0, 0.7);
color: #fff;
border-radius: 0 0 0 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
.add {
font-size: 80rpx;
display: flex;
align-items: center;
justify-content: center;
color: #999;
}
}
</style>
下面是效果图:
页面间传值
例如我们从A页=>B页:
在A页中的跳转时,将参数拼接上:
clickItem(id) {
uni.navigateTo({
url: "/pages/B/B?id=" + id,
})
}
此时点击跳转的链接就变为了(http://localhost:5173/#/pages/B/B?id=1)
当我们在B页面要接收时,直接使用onLOad
去接收
onLoad(id) {
console.log(id);
},
此时,A页面就将id转给了B页面。
各种事件
下拉刷新
当用户下拉刷新时触发,示例:
onPullDownRefresh() {
this.listArr = [];
this.getData();
},
下拉刷新时,重新获取数据,但是刷新的动画不会消失,所以可以在数据完后后使用uni.stopPullDownRefresh()
结束刷新的动画。
触底事件
当用户滑倒到页面底部时触发,示例:
// 触底
onReachBottom() {
this.getData();
},
methods: {
getData() {
uniCloud.callFunction({
name: "art_get_all",
data: {
skip: this.listArr.length
}
}).then(res => {
let oldList = this.listArr;
this.listArr = [...oldList, ...res.result.data]
uni.stopPullDownRefresh()
})
},
},
此处在下拉时,请求数据,并且根据已经加载的数据,获取新的数据,所以采用:this.listArr.length
,去分页查询新的数据,下面是与函数的查询:
const db = uniCloud.database();
exports.main = async (event, context) => {
let {
skip = 0
} = event;
return await db.collection("article").limit(8).skip(skip).orderBy("time", "desc").get(); // 每次传递8条,时间倒序
};
Uni-App-TS
创建项目
使用命令行创建uni-app-vue3-ts版:
npx degit dcloudio/uni-preset-vue#vite-ts 项目名
进入项目后,进行安装依赖:npm i
,启动可按package.json
文件中的scripts
启动命令
用VS Code开发
安装uni-app 插件
- uni-create-view :快速创建 uni-app 页面
- uni-helper :uni-app 代码提示
- uniapp 小程序扩展 :鼠标悬停查文档
TS类型校验
pnpm i -D miniprogram-api-typings @uni-helper/uni-app-types
配置tsconfig.json
,可参考:
// tsconfig.json
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"lib": ["esnext", "dom"],
// 类型声明文件
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types" // uni-app 组件类型
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
// 原配置 `experimentalRuntimeMode` 现调整为 `nativeTags`
"nativeTags": ["block", "component", "template", "slot"],
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
JSON 注释问题
在VScode中,严格要求json格式,在json中会爆红,只需要我们设置文件关联,把 manifest.json
和 pages.json
设置为 jsonc
设置 =》搜索文件关联 =》Files: Associations =》添加项
安装uni-ui组件库
采用npm方式进行安装,在 vue-cli
项目中可以使用 npm
安装 uni-ui
库
npm i @dcloudio/uni-ui
配置easycom
使用 npm
安装好 uni-ui
之后,需要配置 easycom
规则,让 npm
安装的组件支持 easycom
打开项目根目录下的 pages.json
并添加 easycom
节点:
// pages.json
{
"easycom": {
"autoscan": true,
"custom": {
// uni-ui 规则如下配置
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
// 其他内容
pages:[
// ...
]
}
当我们使用ts时,需要对uni-ui声明组件类型,安装@uni-helper/uni-ui-types
npm i -D @uni-helper/uni-ui-types
并且在项目根目录的tsconfig.json
添加类型, compilerOptions -> types:
"types": [
"@dcloudio/types",
"miniprogram-api-typings",
"@uni-helper/uni-app-types",
"@uni-helper/uni-ui-types"
]