Vue3基础(二)
一+一加1 2024-10-04 11:03:09 阅读 50
一、搭建工程(vite)
<code>## 1.创建命令
npm create vue@latest
## 2.具体配置
## 配置项目名称
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript? Yes
## 是否添加JSX支持
√ Add JSX Support? No
## 是否添加路由环境
√ Add Vue Router for Single Page Application development? No
## 是否添加pinia环境
√ Add Pinia for state management? No
## 是否添加单元测试
√ Add Vitest for Unit Testing? No
## 是否添加端到端测试方案
√ Add an End-to-End Testing Solution? » No
## 是否添加ESLint语法检查
√ Add ESLint for code quality? Yes
## 是否添加Prettiert代码格式化
√ Add Prettier for code formatting? No
二、setup语法糖
<template>
<div class="person">code>
<h2>姓名:{ {name}}</h2>
<h2>年龄:{ {age}}</h2>
<button @click="changName">修改名字</button>code>
<button @click="changAge">年龄+1</button>code>
<button @click="showTel">点我查看联系方式</button>code>
</div>
</template>
<script lang="ts">code>
export default {
name:'Person',
}
</script>
<!-- 下面的写法是setup语法糖 -->
<script setup lang="ts">code>
console.log(this) //undefined
// 数据(注意:此时的name、age、tel都不是响应式数据)
let name = '张三'
let age = 18
let tel = '13888888888'
// 方法
function changName(){
name = '李四'//注意:此时这么修改name页面是不变化的
}
function changAge(){
console.log(age)
age += 1 //注意:此时这么修改age页面是不变化的
}
function showTel(){
alert(tel)
}
</script>
扩展:上述代码,还需要编写一个不写setup
的script
标签,去指定组件名字,比较麻烦,我们可以借助vite
中的插件简化
第一步:npm i vite-plugin-vue-setup-extend -D
第二步:vite.config.ts
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [ VueSetupExtend() ]
})
第三步:<script setup lang="ts" name="Person">code>
三、路由
1、基本使用
npm i vue-router
路由配置文件
import {createRouter,createWebHistory} from 'vue-router'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import About from '@/pages/About.vue'
const router = createRouter({
history:createWebHistory(),
routes:[
{
path:'/home',
component:Home
},
{
path:'/about',
component:About
}
]
})
export default router
main.ts 入口文件
import router from './router/index'
app.use(router)
app.mount('#app')
App.vue
<template>
<div class="app">code>
<h2 class="title">Vue路由测试</h2>code>
<!-- 导航区 -->
<div class="navigate">code>
<RouterLink to="/home" active-class="active">首页</RouterLink>code>
<RouterLink to="/news" active-class="active">新闻</RouterLink>code>
<RouterLink to="/about" active-class="active">关于</RouterLink>code>
</div>
<!-- 展示区 -->
<div class="main-content">code>
<RouterView></RouterView>
</div>
</div>
</template>
<script lang="ts" setup name="App">code>
import {RouterLink,RouterView} from 'vue-router'
</script>
2、两种路由工作模式
3、编程式导航
路由组件的两个重要的属性:<code>$route和$router
变成了两个hooks
import {useRoute,useRouter} from 'vue-router'
const route = useRoute()
const router = useRouter()
console.log(route.query)
console.log(route.parmas)
console.log(router.push)
console.log(router.replace)
4、重定向
{
path:'/',
redirect:'/about'
}
四、pinia
1、搭建环境
npm install pinia
src\main.ts
import { createApp } from 'vue'
import App from './App.vue'
/* 引入createPinia,用于创建pinia */
import { createPinia } from 'pinia'
/* 创建pinia */
const pinia = createPinia()
const app = createApp(App)
/* 使用插件 */{}
app.use(pinia)
app.mount('#app')
2、存储、读取数据
Store
是一个保存:状态、业务逻辑 的实体,每个组件都可以读取、写入它。
它有三个概念:state
、getter
、action
,相当于组件中的: data
、 computed
和 methods
。
具体编码:src/store/count.ts
// 引入defineStore用于创建store
import {defineStore} from 'pinia'
// 定义并暴露一个store
export const useCountStore = defineStore('count',{
// 动作
actions:{},
// 状态
state(){
return {
sum:6
}
},
// 计算
getters:{}
})
使用数据
<template>
<h2>当前求和为:{ { sumStore.sum }}</h2>
</template>
<script setup lang="ts" name="Count">code>
// 引入对应的useXxxxxStore
import {useSumStore} from '@/store/sum'
// 调用useXxxxxStore得到对应的store
const sumStore = useSumStore()
</script>
3、修改数据
第一种修改方式,直接修改
countStore.sum = 666
第二种修改方式:批量修改
countStore.$patch({
sum:999,
school:'atguigu'
})
第三种修改方式:借助action
修改(action
中可以编写一些业务逻辑)
import { defineStore } from 'pinia'
export const useCountStore = defineStore('count', {
/*************/
actions: {
//加
increment(value:number) {
if (this.sum < 10) {
//操作countStore中的sum
this.sum += value
}
},
//减
decrement(value:number){
if(this.sum > 1){
this.sum -= value
}
}
},
/*************/
})
组件中调用action
// 使用countStore
const countStore = useCountStore()
// 调用对应action
countStore.incrementOdd(n.value)
4、storeToRefs
借助storeToRefs
将store
中的数据转为ref
对象,方便在模板中使用。
注意:pinia
提供的storeToRefs
只会将数据做转换,而Vue
的toRefs
会转换store
中数据。
<template>
<div class="count">code>
<h2>当前求和为:{ {sum}}</h2>
</div>
</template>
<script setup lang="ts" name="Count">code>
import { useCountStore } from '@/store/count'
/* 引入storeToRefs */
import { storeToRefs } from 'pinia'
/* 得到countStore */
const countStore = useCountStore()
/* 使用storeToRefs转换countStore,随后解构 */
const {sum} = storeToRefs(countStore)
</script>
5、getters
概念:当state
中的数据,需要经过处理后再使用时,可以使用getters
配置。
追加getters
配置。
// 引入defineStore用于创建store
import {defineStore} from 'pinia'
// 定义并暴露一个store
export const useCountStore = defineStore('count',{
// 动作
actions:{
/************/
},
// 状态
state(){
return {
sum:1,
school:'atguigu'
}
},
// 计算
getters:{
bigSum:(state):number => state.sum *10,
upperSchool():string{
return this. school.toUpperCase()
}
}
})
组件中读取数据
const {increment,decrement} = countStore
let {sum,school,bigSum,upperSchool} = storeToRefs(countStore)
6、$subscribe
通过 store 的 $subscribe()
方法侦听 state
及其变化
talkStore.$subscribe((mutate,state)=>{
console.log('LoveTalk',mutate,state)
localStorage.setItem('talk',JSON.stringify(talkList.value))
})
7、store组合式写法
import {defineStore} from 'pinia'
import axios from 'axios'
import {nanoid} from 'nanoid'
import {reactive} from 'vue'
export const useTalkStore = defineStore('talk',()=>{
// talkList就是state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getATalk函数相当于action
async function getATalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
talkList.unshift(obj)
}
return {talkList,getATalk}
})
五、组件通信
1、props
若 父传子:属性值是非函数。
若 子传父:属性值是函数。
父组件
<template>
<div class="father">code>
<h3>父组件</h3>
<h4>我的车:{ { car }}</h4>
<h4>儿子给的玩具:{ { toy }}</h4>
<Child :car="car" :getToy="getToy"/>code>
</div>
</template>
<script setup lang="ts" name="Father">code>
import Child from './Child.vue'
import { ref } from "vue";
// 数据
const car = ref('奔驰')
const toy = ref()
// 方法
function getToy(value:string){
toy.value = value
}
</script>
子组件
<template>
<div class="child">code>
<h3>子组件</h3>
<h4>我的玩具:{ { toy }}</h4>
<h4>父给我的车:{ { car }}</h4>
<button @click="getToy(toy)">玩具给父亲</button>code>
</div>
</template>
<script setup lang="ts" name="Child">code>
import { ref } from "vue";
const toy = ref('奥特曼')
defineProps(['car','getToy'])
</script>
2、自定义事件
自定义事件常用于:子 => 父
事件对象$event
: 是调用emit
时所提供的数据,可以是任意类型!!!
3、mitt
与消息订阅与发布(<code>pubsub)功能类似,可以实现任意组件间通信
npm i mitt
src\utils\emitter.ts
// 引入mitt
import mitt from "mitt";
// 创建emitter
const emitter = mitt()
// 创建并暴露mitt
export default emitter
接收数据的组件中:绑定事件、同时在销毁前解绑事件
import emitter from "@/utils/emitter";
import { onUnmounted } from "vue";
// 绑定事件
emitter.on('send-toy',(value)=>{
console.log('send-toy事件被触发',value)
})
onUnmounted(()=>{
// 解绑事件
emitter.off('send-toy')
})
提供数据的组件,在合适的时候触发事件
import emitter from "@/utils/emitter";
function sendToy(){
// 触发事件
emitter.emit('send-toy',toy.value)
}
4、v-model
概述:实现 父↔子 之间相互通信。
前序知识 —— v-model
的本质
<!-- 使用v-model指令 -->
<input type="text" v-model="userName">code>
<!-- v-model的本质是下面这行代码 -->
<input
type="text" code>
:value="userName" code>
@input="userName =(<HTMLInputElement>$event.target).value"code>
>
组件标签上的v-model
的本质::moldeValue
+ update:modelValue
事件。
<!-- 组件标签上使用v-model指令 -->
<AtguiguInput v-model="userName"/>code>
<!-- 组件标签上v-model的本质 -->
<AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>code>
AtguiguInput
组件中:
<template>
<div class="box">code>
<!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
<!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
<input
type="text" code>
:value="modelValue" code>
@input="emit('update:model-value',$event.target.value)"code>
>
</div>
</template>
<script setup lang="ts" name="AtguiguInput">code>
// 接收props
defineProps(['modelValue'])
// 声明事件
const emit = defineEmits(['update:model-value'])
</script>
也可以更换value
,例如改成abc
<!-- 也可以更换value,例如改成abc-->
<AtguiguInput v-model:abc="userName"/>code>
<!-- 上面代码的本质如下 -->
<AtguiguInput :abc="userName" @update:abc="userName = $event"/>code>
AtguiguInput
组件中:
<template>
<div class="box">code>
<input
type="text" code>
:value="abc" code>
@input="emit('update:abc',$event.target.value)"code>
>
</div>
</template>
<script setup lang="ts" name="AtguiguInput">code>
// 接收props
defineProps(['abc'])
// 声明事件
const emit = defineEmits(['update:abc'])
</script>
如果value
可以更换,那么就可以在组件标签上多次使用v-model
<AtguiguInput v-model:abc="userName" v-model:xyz="password"/> code>
5、$attrs概述:$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。
具体说明:$attrs
是一个对象,包含所有父组件传入的标签属性。
注意:
$attrs
会自动排除props
中声明的属性(可以认为声明过的props
被子组件自己“消费”了)
父组件:
<template>
<div class="father">code>
<h3>父组件</h3>
<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>code>
</div>
</template>
<script setup lang="ts" name="Father">code>
import Child from './Child.vue'
import { ref } from "vue";
let a = ref(1)
let b = ref(2)
let c = ref(3)
let d = ref(4)
function updateA(value){
a.value = value
}
</script>
子组件:
<template>
<div class="child">code>
<h3>子组件</h3>
<GrandChild v-bind="$attrs"/>code>
</div>
</template>
<script setup lang="ts" name="Child">code>
import GrandChild from './GrandChild.vue'
</script>
孙组件:
<template>
<div class="grand-child">code>
<h3>孙组件</h3>
<h4>a:{ { a }}</h4>
<h4>b:{ { b }}</h4>
<h4>c:{ { c }}</h4>
<h4>d:{ { d }}</h4>
<h4>x:{ { x }}</h4>
<h4>y:{ { y }}</h4>
<button @click="updateA(666)">点我更新A</button>code>
</div>
</template>
<script setup lang="ts" name="GrandChild">code>
defineProps(['a','b','c','d','x','y','updateA'])
</script>
6、$refs、$parent
概述:
$refs
用于 :父→子。
$parent
用于:子→父。
原理如下:
属性 | 说明 |
---|---|
$refs | 值为对象,包含所有被ref 属性标识的DOM 元素或组件实例。 |
$parent | 值为对象,当前组件的父组件实例对象。 |
7、provide、inject
概述:实现祖孙组件直接通信
具体使用:
在祖先组件中通过provide
配置向后代组件提供数据
在后代组件中通过inject
配置来声明接收数据
具体编码:
【第一步】父组件中,使用provide
提供数据
<template>
<div class="father">code>
<h3>父组件</h3>
<h4>资产:{ { money }}</h4>
<h4>汽车:{ { car }}</h4>
<button @click="money += 1">资产+1</button>code>
<button @click="car.price += 1">汽车价格+1</button>code>
<Child/>
</div>
</template>
<script setup lang="ts" name="Father">code>
import Child from './Child.vue'
import { ref,reactive,provide } from "vue";
// 数据
let money = ref(100)
let car = reactive({
brand:'奔驰',
price:100
})
// 用于更新money的方法
function updateMoney(value:number){
money.value += value
}
// 提供数据
provide('moneyContext',{money,updateMoney})
provide('car',car)
</script>
注意:子组件中不用编写任何东西,是不受到任何打扰的
【第二步】孙组件中使用inject
配置项接受数据。
<template>
<div class="grand-child">code>
<h3>我是孙组件</h3>
<h4>资产:{ { money }}</h4>
<h4>汽车:{ { car }}</h4>
<button @click="updateMoney(6)">点我</button>code>
</div>
</template>
<script setup lang="ts" name="GrandChild">code>
import { inject } from 'vue';
// 注入数据
let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})
let car = inject('car')
</script>
8、pinia
六、其他知识点
Vue3基础使用-CSDN博客
https://blog.csdn.net/m0_71534259/article/details/139900842?spm=1001.2014.3001.5501
Vue2基础-CSDN博客
https://blog.csdn.net/m0_71534259/article/details/139250173?spm=1001.2014.3001.5501
上一篇: FastAPI的一站式全栈开发利器:Full Stack FastAPI 模板带你快速构建现代 Web 应用!
下一篇: 2024年最新前端LayUI框架快速上手详解(二),斗鱼c++面试
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。