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(失败)

  1. pending。它的意思是 “待定的,将发生的”,相当于是一个初始状态。创建Promise对象时,且没有调用resolve或者是reject方法,相当于是初始状态。这个初始状态会随着你调用resolve,或者是reject函数而切换到另一种状态。
  2. resolved。表示解决了,就是说这个承诺实现了。 要实现从pending到resolved的转变,需要在 创建Promise对象时,在函数体中调用了resolve方法
  3. rejected。拒绝,失败。表示这个承诺没有做到,失败了。要实现从pending到rejected的转换,只需要在创建Promise对象时,调用reject函数。

通过代码打印promise的方法:

console.dir(Promise)

image-20231107145707785


使用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语法

image-20240125183202252

还可以在这个插件中设置,在使用Ref数据使,自动添加**.value**

VScode的设置中,选择:扩展 -> Volar ,勾选==自动输入value==

image-20240125183851286

父子间组件传值

父 => 子

子组件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>

示例:

image-20231018155705477

Uni-App

编写Uni-App,推荐使用官方的IDE:HBuilderX,编写微信小程序,还需要下载微信开发者工具:微信开发者工具

引入插件

DCloud有活跃的插件市场,https://ext.dcloud.net.cn,并提供了变现、评价等机制。

DCloud插件市场将插件分为前端组件、JS SDK、uni-app前端模板、App原生插件、uniCloud等7大类、20多个子类。

比如安装uni-uiuni-ui,点击下载插件并导入HBuilderX

1699257514785

云存储

云存储的上传方式有3种:

  1. web界面:即在https://unicloud.dcloud.net.cn web控制台,点击云存储,通过web界面进行文件上传。该管理界面同时提供了资源浏览、删除等操作界面。

  2. 客户端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" />
  3. 云函数上传文件到云存储:即在云函数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>

下面是效果图:

image-20231107102531845

页面间传值

例如我们从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 插件

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.jsonpages.json 设置为 jsonc

设置 =》搜索文件关联 =》Files: Associations =》添加项

image-20240617025717041

安装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"
]

Vue与Uni-APP教程
https://junyyds.top/2023/10/18/Vue/
作者
Phils
发布于
2023年10月18日
更新于
2024年6月21日
许可协议