Vue与Uni-APP教程
本文最后更新于:2025年4月29日 下午
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==
vue-router
引入vue-router
安装:
npm install vue-router
使用:
在src
目录下,新建文件夹router
,在router
文件夹中新建文件index.ts/index.js
,写入一下内容
// 创建一个路由器,暴露出去
import { createRouter, createWebHistory } from 'vue-router'
// 测试路由
import Home from '@/pages/Home.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), // 路由器的工作模式
routes: [
{
path: '/home',
component: Home
}
]
})
export default router
在main.ts/main.js
中引入:
import router from './router'
app.use(router)
在组件中可以使用这种方式:RouterLink为路由跳转,RouterView为不同路由的显示
<!-- 导航区 -->
<div class="navigate">
<RouterLink to="/home" active-class="active">首页</RouterLink>
<RouterLink to="/news" active-class="active">新闻</RouterLink>
<RouterLink to="/about" active-class="active">关于</RouterLink>
</div>
<!-- 展示区 -->
<div class="main-content">
<RouterView />
</div>
路由器工作模式
history模式
优点:
URL
更加美观,不带有#
,更接近传统的网站URL
。缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有404模式。
const router = createRouter({ history: createWebHistory(), // history模式 /******/ })
hash模式
优点:兼容性更好,因为不需要服务端处理路径。
缺点:
URL
带有#
不太美观,且在SEO
优化方面相对较差const router = createRouter({ history: createWebHashHistory(), // hash模式 /******/ })
路由_to的两种写法
路径字符串方式
<RouterLink to="/home" active-class="active">首页</RouterLink>
路径对象方式
<RouterLink :to="{path: '/homne'}" active-class="active">首页</RouterLink>
路由传参
路由传参可通过 query
、param
query参数:
直接通过路由传参:
<router-link :to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`">
{{ news.title }}
</router-link>
在跳转到的路由组件中,使用route
获取
<template>
<ul class="news-list">
<li>编号:{{ route.query.id }}</li>
<li>标题:{{ route.query.title }}</li>
<li>内容:{{ route.query.content }}</li>
</ul>
</template>
<script setup lang="ts" name="Detail">
import {useRoute} from "vue-router";
const route = useRoute();
</script>
优化写法:
路由传参:将to使用对象写法,query中写参数
<router-link
:to="{
path: `/news/detail`,
query: {
id: news.id,
title: news.title,
content: news.content
}
}"
>
{{ news.title }}
</router-link>
在跳转到的路由组件中,使用route
获取
<template>
<ul class="news-list">
<li>编号:{{ query.id }}</li>
<li>标题:{{ query.title }}</li>
<li>内容:{{ query.content }}</li>
</ul>
</template>
<script setup lang="ts" name="Detail">
import { toRefs } from "vue";
import {useRoute} from "vue-router";
const route = useRoute();
const {query} = toRefs(route);
</script>
param参数:
首先在路由配置页中,预留参数位:在子路由的path
中写入detail/:id/:title/:content
routes: [
{
path: '/news',
component: News,
children: [
{
path: 'detail/:id/:title/:content',
component: Detail
}
]
},
]
路由跳转:
<router-link :to="`/news/detail/${news.id}/${news.title}/${news.content}`">
{{ news.title }}
</router-link>
路由接收:
<template>
<ul class="news-list">
<li>编号:{{ route.params.id }}</li>
<li>标题:{{ route.params.title }}</li>
<li>内容:{{ route.params.content }}</li>
</ul>
</template>
<script setup lang="ts" name="Detail">
import {useRoute} from "vue-router";
const route = useRoute();
</script>
优化写法
在路由配置页中,为路由设置名称
children: [
{
name: 'detail',
path: 'detail/:id/:title/:content',
component: Detail
}
]
路由跳转: 且params中不可以传对象
或数组
<RouterLink :to="{
name: 'detail',
params: {
id: news.id,
title: news.title,
content: news.content
}
}">
{{ news.title}}
</RouterLink>
如果有些参数不是必要的,可以在路由配置页中,在该参数后面加一个
?
children: [
{
name: 'detail',
path: 'detail/:id/:title/:content?',
component: Detail
}
]
props配置
为了简化在路由接收时,从route
中取值的繁琐步骤,可在路由配置页,添加:props: true
children: [
{
name: 'detail',
path: 'detail/:id/:title/:content',
component: Detail,
props: true
}
]
此时,路由组件<Detail />
相当于变为了 <Detail id=? title=? content=? />
,将接收到的参数转化为props
在路由接收页面就可以直接通过 defineProps
拿到参数:
<template>
<ul class="news-list">
<li>编号:{{ id }}</li>
<li>标题:{{ title }}</li>
<li>内容:{{ content }}</li>
</ul>
</template>
<script setup lang="ts" name="Detail">
defineProps(['id', 'title', 'content'])
</script>
Props有三种写法,选择合适的写法
{
// 第一种写法:将路由收到的所有params参数作为props传给路由组件
props: true,
// 第二种写法:第二种写法:函数写法,可以自己决定将什么作为props传给路由组件
props(route){
return route.query
}
// 第三种写法:对象写法,可以自己决定将什么作为props传给路由组件
props: {
id: 1,
title: '新闻标题',
content: '新闻内容'
}
}
replace属性
作用:控制路由跳转时操作浏览器历史记录的模式。
浏览器的历史记录有两种写入方式:分别为
push
和replace
:push
是追加历史记录(默认值)。可在浏览器点击退后,返回之前路由replace
是替换当前记录。无法回退之前路由,可用于登录页跳转
开启
replace
模式:加入replace
<Router-link replace to="/home">首页</Router-link>
编程式路由导航
编程式路由导航,为了脱离<RouterLink>
实现路由跳转,我们只需要在组件种引入useRouter
import { useRouter } from "vue-router";
const router = useRouter()
// 此处 push 用法和路由 to 的用法一样,有两种,字符串和对象写法
router.push({
name: 'detail',
params: {
id: news.id,
title: news.title,
content: news.content
}
})
编程式路由可写在函数中,当满足条件进行跳转,例如登录跳转。
重定向
使用redirect
可重定向到指定路由
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), // 路由器的工作模式
routes: [
{
path: '/home',
component: Home
},
{
path: '/',
redirect: '/home'
}
]
})
Pinia
Vue的状态管理库
搭建pinia环境
安装pinia
npm i pinia
在main.ts/main.js
中引入pinia:
import { createApp } from 'vue'
import App from './App.vue'
// 第一步:引入pinia
import { createPinia } from 'pinia'
const app = createApp(App)
// 第二步:创建pinia
const pinia = createPinia()
// 第三步:挂载pinia
app.use(pinia)
app.mount('#app')
存储+读取数据
假如我们有一个组件叫Count.vue
,我们需要存储一个数据为:sum
,我们可以在项目路径中@/src/store
中创建count.ts
,写入
defineStore
需要一个id和对象,id我们使用组件名
import {defineStore} from "pinia";
export const useCountStore = defineStore('count', {
// 真正存储数据的地方
state() {
return {
sum: 0
}
}
})
在组件Count.vue
中使用时,我们先引入count.ts
,在通过count.ts
中暴露出的useCountStore.sum
获取数据
import {useCountStore} from "@/store/count"
const countStore = useCountStore()
// 取数据
countStore.sum
更方便的读取数据 storeToRefs,使用storeToRefs,只会关注store中的数据,不会对方法进行ref包裹
import {useCountStore} from "@/store/count"
import {storeToRefs} from "pinia";
const countStore = useCountStore()
const {sum, school, address} = storeToRefs(countStore)
修改数据(3种方式)
第一种修改方式,直接操作数据
function add() {
countStore.sum += 1
}
第二种修改方式,批量变更,使用$patch
可以进行批量数据修改,当批量数据变更时使用这种
function add() {
countStore.$patch({
sum: countStore.sum += n.value,
school: 'bjdx'
})
}
第三种修改方式,在count.ts
中编写修改数据的行为,在count.ts
中加入actions,在actions中编写修改行为,可复用
import {defineStore} from "pinia";
export const useCountStore = defineStore('count', {
// actions里面放置的是一个一个方法,用于响应组件的“动作”
actions: {
increment(value: number) {
this.sum += value // 此处无法直接获取sum,需使用this.sum获取
}
},
// 真正存储数据的地方
state() {
return {
sum: 0,
school: '北京大学',
address: '北京市朝阳区芍药居'
}
}
})
在count.ts
组件中调用: 直接使用 countStore.方法
function add() {
countStore.increment(n.value)
}
getters使用
在count.ts
中,可以加入getters:
import {defineStore} from "pinia";
export const useCountStore = defineStore('count', {
/******/,
getters: {
bigSum: state => state.sum * 10,
upperSchool(): string {
return this.school.toUpperCase()
}
}
})
使用时和state中的数据一样,推荐使用方法:
import {useCountStore} from "@/store/count"
import {storeToRefs} from "pinia";
const countStore = useCountStore()
const {sum, school, address, bigSum, upperSchool} = storeToRefs(countStore)
$subscribe使用
$subscribe可以监视store中的数据变化,例如下面代码
import {useTalkStore} from "@/store/loveTalk";
const talkStore = useTalkStore()
talkStore.$subscribe((mutation, state) => {
console.log(mutation)
console.log(state)
})
mutation: 提供关于状态更改操作的详细信息
state: 取到存储的当前最新的完整状态
Store的组合式
store还可以用组合式去书写,例如loveTalk.ts
import {reactive} from "vue";
export const useTalkStore = defineStore('talk', () => {
// talkList 就是 state
const talkList = reactive(JSON.parse(localStorage.getItem('talkList') as string) || [])
// 函数 相当于action
async function getAllTalk() {
let {data: {content: title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
let obj = {id: nanoid(), title: title}
talkList.unshift(obj)
}
return {talkList,getAllTalk}
})
组件通信
【props】
概述:props
是使用频率最高的一种通信方式,常用于:父 <-> 子
- 若父传子:属性值是非函数
- 若子传父:属性值是函数
首先我们创建一个父组件:Father.vue
<template>
<div class="father">
<h3>父组件</h3>
<h4>汽车:{{ car }}</h4>
<h4 v-show="toy">子给的玩具:{{ toy }}</h4>
<Child :car="car" :sendToy="getToy"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import {ref} from 'vue'
// 数据
let car = ref('梅赛德斯奔驰')
let toy = ref('')
// 方法
function getToy(value:string){
toy.value = value
}
</script>
一个子组件:Child.vue
<template>
<div class="child">
<h3>子组件</h3>
<h4>玩具:{{ toy }}</h4>
<h4>父给的车:{{ car }}</h4>
<button @click="sendToy(toy)">把玩具给父亲</button>
</div>
</template>
<script setup lang="ts" name="Child">
import {ref} from 'vue'
// 数据
let toy = ref('奥特曼')
// 声明接收props
defineProps(['car','sendToy'])
</script>
父组件传给子组件,通过在子组件:car='car'
,子组件通过defineProps
拿到'car'
子组件传给父组件,在子组件的按钮上写上父组件传的sendToy
函数,父组件使用getToy
方法获取的值
注意:props适合父子组件,如果组件嵌套过深不适合使用
【自定义事件】
自定义事件专门用于:子->父
在父组件中,给子组件添加自定义事件:@自定义事件
,并且在子组件使用defineEmits()
,使用emit
去接收
父组件:Father.vue
<template>
<div class="father">
<h3>父组件</h3>
<h4 v-show="toy">子给的玩具:{{ toy }}</h4>
<!-- 给子组件Child绑定事件 -->
<Child @send-toy="saveToy"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import { ref } from "vue";
// 数据
let toy = ref('')
// 用于保存传递过来的玩具
function saveToy(value:string){
console.log('saveToy',value)
toy.value = value
}
</script>
子组件:Child.vue
<template>
<div class="child">
<h3>子组件</h3>
<h4>玩具:{{ toy }}</h4>
<button @click="emit('send-toy',toy)">测试</button>
</div>
</template>
<script setup lang="ts" name="Child">
import { ref } from "vue";
// 数据
let toy = ref('奥特曼')
// 声明事件
const emit = defineEmits(['send-toy'])
</script>
【mitt】
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"
]
微信小程序登录
微信小程序扫码登录流程-官方时序图
流程为:
- 前端调用wx.login()获取code值,发给后端
- 后端将code,appId,appSecret作为参数调用微信接口 code2Session
- 后端通过调用微信接口返回的openId,session_key(用于解析用户信息)
- 后端根据微信接口返回的openId查询是否存在用户信息
- 存在则返回用户信息+token,不存在则注册用户返回openId用于前端请求用户保存用户信息
第一步:
引入依赖:
<!-- 微信小程序 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.1.0</version>
</dependency>
在application.yml
文件中,配置:
wx:
miniapp:
appId: xxxxxxxxxxxxxxxxxx
secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
msgDataFormat: JSON
创建配置文件类WxConfigProperties.java
,读取配置内容:
@Component
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxConfigProperties {
private String appId;
private String secret;
}
创建微信工具包对象WxConfigOperator.java
@Component
public class WxConfigOperator {
@Resource
private WxConfigProperties wxConfigProperties;
@Bean
public WxMaService wxMaService() {
// 小程序 id和密钥
WxMaDefaultConfigImpl wxMaConfig = new WxMaDefaultConfigImpl();
wxMaConfig.setAppid(wxConfigProperties.getAppId());
wxMaConfig.setSecret(wxConfigProperties.getSecret());
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(wxMaConfig);
return service;
}
}
通过wx.login(微信前端–小程序)接口获取code,将code传到后端,后端调用https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code