路由配置的props配置
第一种写法:将路由收到的所有params参数作为props传给路由组件
只需在路由配置项中加个 props: true 就可以了,注意第一种写法只能传params 参数
第二种写法函数写法:函数里面的参数是一个路由对象,根据需要可以将 params 参数 或者 query 参数传递出去
const router = createRouter({
history:createWebHistory(),
routes:[
{
name: 'xinwen',
path: '/news',
component: News,
children :
[
{
name: 'xiang',
path: 'detail',
component: Detail
// 第一种写法
props: true;
// 第二种写法,函数写法,注意这个参数就是一整个路由对象,包含params参数和query参
//数,自己决定要传什么参数
props(route)
{
return route.query
}
}
]
]
})
最后在要接收数据的路由文件中引入下面的代码,就接收到了传递的id , title ,content 等数据信息defineProps(['id', 'title', 'content'])
路由的replace 属性 和 push 属性
默认是push 属性,就是点击到这个页面路由后,可以返回上一个界面, 如果指定的是replace 属性的话,就不能回退到上一个界面的路由
编程式路由导航
一般我们跳转路由都是用的RouterLink 标签,现在要实现一个功能,在一个页面停止3秒后就跳转另一个标签,就不能在脚本文件中使用 RouterLink
import { useRouter } from 'vue-router'
const router = useRouter()
获取了路由器,你只要再使用 router.push('指定路由路径') 就可以跳转指定的路由路径 如 router.push('/new')
注意:push的写法和to的写法是一样的,既可以写如上的字符串的地址,也能写成对象形式
路由的重定向
redirect: '重定向地址'
先找到路由的规则指定处,在路由一级规则制定完后加上下面一段代码,就表示打开app时,最开始的呈现界面就换成了 home 路由的界面
{
path: '/'
redirect: 'home'
}
vue状态管理工具pinia
配置 pinia 环境的步骤
// 第一步,引入pinia
import { createPinia } from 'pinia'
// 第二步 创建pinia
const pinia = createPinia()
// 第三步:安装pinia
app.use(pinia)
pinia 的存储数据方式:
例如在存储count 数据的文件 count.ts中
import { defineStore } from 'pinia'
// 第一个参数为id, 第二个参数为 一个配置项,里面的state 要写成函数形式,返回一个对象,里面是要存储的数据
export let useCountStore = defineStore('count', {
state() {
return {
sum: 6
}
}
})
利用存储的数据
先从存储count数据的文件引入
import { useCountStore } from '@/store/count'
const countStore = useCountStore()
在调用其中的数据,有下列两种调用state中的方法,再将数据放到模板中去呈现即可
// 以下两种方式都可以拿到state中的数据
console.log('@@@',countStore.sum)
console.log('@@@',countStore.$state.sum)
修改pinia里面的数据的三种方式
第一种修改方式-直接修改
function add(){
countstore.sum += 1;
}
第二种修改方式,利用 $patch 方法一次性直接修改
function add(){
countStore.$patch({
sum : 888,
school: '北京大学',
address: '北京'
})
}
第三种修改方式:在pinia 定义的存储文件配置对象中加入 actions, 他是专门放置方法 ,用于相应组件中的动作的
import { defineStore } from 'pinia'
// 第一个参数为id, 第二个参数为 一个配置项,里面的state 要写成函数形式,返回一个对象,里面是要存储的数据
export let useCountStore = defineStore('count', {
actions:{
add2(value)
{
this..sum += value
}
},
state() {
return {
sum: 6
}
}
})
然后再在组件里面调用该函数就可以
function add(){
countStore.add2(n.value);
}
storeToRefs:
就是只将store中的数据转化为ref的数据响应式,如果用toRefs的话,不仅即将数据转化为ref响应式,连里面的方法也转化为了ref响应式数据
getters:当state 中的数据,需要经过处理后在使用的时候,可以使用getters配置
getters配置的追加
import { defineStore } from 'pinia'
// 第一个参数为id, 第二个参数为 一个配置项,里面的state 要写成函数形式,返回一个对象,里面是要存储的数据
export const useCountStore = defineStore('count', {
actions: {
add2(value) {
this.sum += value
}
},
state() {
return {
sum: 6,
school: '上高',
address: '湖北'
}
},
getters: {
bigsum(state) {
return state.sum * 10
},
upperSchool(state) {
return this.school.toUpperCase()
}
}
}
)
pinia 的组合式写法
import { defineStore } from 'pinia'
import { reactive } from 'vue'
// 组合式写法第二个参数就会一个函数,不是一个内置对象了
export const useCountStore = defineStore('count', () => {
// 直接将state里面的数据在外面定义好
const Count = reactive({ sum: 6, school: '上高', address: '湖北' })
// 这里直接将action里面的方法全部写成函数形式
function add2(value) {
Count.sum += value
}
// 最后在将里面的数据和方法全部return 出去
return { Count, add2 }
})
组件通信
组件通信方式1:props方式,可以实现父传子,子传父的功能
若是父传子:属性值是非函数
若是子传父:属性值就是函数
下面是父组件的代码:car就是父传子的数据,子传父的是数据就在getToy函数里
样式代码自行补充,下面写了结构代码和脚本代码
<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>
下面是子组件的代码:样式代码自行补充,下面写了结构代码和脚本代码
<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>
组件通信二——自定义事件,解决将子组件的数据传给父组件
@send-toy就是一个父组件中的自定义事件,saveToy函数中的参数就是从子组件中传过来的信息
<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>
defineEmits 是在子组件中定义事件发生器的函数举例,下面是较复杂的事件定义
整个字符串 'update:modelValue'
是事件名。这个事件名遵循了Vue中v-model的事件命名约定,用于实现自定义组件的双向数据绑定。
对象 { name: 'change', props: ['value'] }
表示你定义了一个名为 change
的自定义事件,并且这个事件有一个名为 value
的参数
const emit = defineEmits(
[ // 声明事件名,可以是字符串或包含事件名和参数类型的元组
'update:modelValue',
// 也可以使用对象语法声明带有参数的事件
{ name: 'change', props: ['value'] }
])
下面是子组件中的代码
emit('send-toy',toy) 是用来触发事件的
子组件触发事件send-toy后,触发事件中的toy参数就会传送到父组件中的 saveToy的函数的参数中
<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>
点击测试后,就会调用emit事件,@click="emit('send-toy',toy),toy就是传给父组件的数据,emit事件调用后就会执行父组件中的函数
组件通信方式三——mitt 可以实现任意组件的通信
和总线相类似。
引入mitt,及绑定事件例子
emit 的方法: on 绑定事件,off解除事件,emit是触发事件的方法
// 引入mitt
import mitt from 'mitt'
// 调用mitt 得到 emitter ,emitter 能 绑定事件,触发事件,
const emitter = mitt()
// 绑定事件,test1,触发事件test后,就会执行回调参数中的内容
emitter.on('test1', () => {
console.log('test1被触发')
})
//on 绑定事件,off解除事件,emit是触发事件的方法
// 暴露emitter
export default emitter
触发事件:
<template>
<div class="child1">
<h3>子组件1</h3>
<h4>玩具:{{ toy }}</h4>
<button @click="emitter.emit('send-toy',toy)">玩具给弟弟</button>
</div>
</template>
<script setup lang="ts" name="Child1">
import {ref} from 'vue'
import emitter from '@/utils/emitter';
// 数据
let toy = ref('奥特曼')
</script>
<template>
<div class="child2">
<h3>子组件2</h3>
<h4>电脑:{{ computer }}</h4>
<h4>哥哥给的玩具:{{ toy }}</h4>
</div>
</template>
<script setup lang="ts" name="Child2">
import {ref,onUnmounted} from 'vue'
import emitter from '@/utils/emitter';
// 数据
let computer = ref('联想')
let toy = ref('')
// 给emitter绑定send-toy事件
emitter.on('send-toy',(value:any)=>{
toy.value = value
})
// 在组件卸载时解绑send-toy事件
onUnmounted(()=>{
emitter.off('send-toy')
})
</script>
组件通信方式四—v-model 实现组件通信
即可以实现父传子,也能实现子传父的操作
v-model 用在html标签上时,这样就实现了代码与页面的通信交互
<!-- <input type="text" v-model="username"> -->
底层原理如下
<input type="text"
:value="username" // 从底层获取数据到页面
// 用input事件将页面数据传给底层
@input="username = (<HTMLInputElement>$event.target).value">
v-model用在路由组件上时,这样就实现了当前组件(父组件)与 AtguiguInput 组件的通信,数据可以相互传递
<AtguiguInput v-model="username"/>
底层原理如下:modelValue - 给子组件传的数据名, update:modelValue - 给子组件传的事件名
@input 是一个在html标签上的input事件监听器
<AtguiguInput
:modelValue="username"
@update:modelValue="username = $event"
/>
value 可以更换,在组件上我们就可以多次使用 v-model,如下
<template>
<AtguiguInput v-model:ming="username" v-model:mima="password"/>
<\template>
<script setup lang="ts" name="Father">
import { ref } from "vue";
import AtguiguInput from './AtguiguInput.vue'
// 数据
let username = ref('zhansgan')
let password = ref('123456')
</script>
子组件接收时
<script setup lang="ts" name="AtguiguInput">
defineProps(['ming','mima'])
const emit = defineEmits(['update:ming','update:mima'])
</script>
注意:$event 是什么?
对于原生事件,$event 就是事件对象,就能 .target
对于自定义事件,$event 就是触发事件时,所传递的数据不能 .target
组件通信方式五-—$attrs :一般是祖组件给孙组件传递数据
$attrs 里有可以从父传到子中没有通过defineprops接受的数据
父组件:
<template>
<div class="father">
<h3>父组件</h3>
<h4>a:{{a}}</h4>
<h4>b:{{b}}</h4>
<h4>c:{{c}}</h4>
<h4>d:{{d}}</h4>
<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>
</div>
</template>
<script setup lang="ts" name="Father">
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:number){
a.value += value
}
</script>
子组件:
<template>
<div class="child">
<h3>子组件</h3>
<GrandChild v-bind="$attrs"/>
</div>
</template>
<script setup lang="ts" name="Child">
import GrandChild from './GrandChild.vue'
</script>
孙子组件
<template>
<div class="grand-child">
<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(6)">点我将爷爷那的a更新</button>
</div>
</template>
<script setup lang="ts" name="GrandChild">
defineProps(['a','b','c','d','x','y','updateA'])
</script>
组件通信方式六— $refs 父传子 和 $parent 子传父
getallchile() 函数中的参数就会是该组件所有子组件的一个实例,如果想要访问某个特定的子组件,就用ref 为该组件做好标记
<button @click="getAllChild($refs)">让所有孩子的书变多</button>
function getAllChild(refs:{[key:string]:any}){
console.log(refs)
for (let key in refs){
refs[key].book += 3
}
<!-- 其他子组件... -->
<ChildComponent ref="myChild" /> <!-- 给特定的子组件设置 ref -->
this.$refs.myChild.addBook();
下面是子组件通过$parent 来访问父组件中的元素属性
<button @click="minusHouse($parent)">干掉父亲的一套房产</button>
// 方法
function minusHouse(parent:any){
parent.house -= 1
}
组件通信方式七— provide 和 inject 直接传给一个组件的所有后代,不管是子组件还是孙组件或者更深的组件
向后代组件传输数据
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import {ref,reactive,provide} from 'vue'
let money = ref(100)
let car = reactive({
brand:'奔驰',
price:100
})
function updateMoney(value:number){
money.value -= value
}
// 向后代提供数据
provide('moneyContext',{money,updateMoney})
provide('car',car)
</script>
后代接收组件传来的数据
<script setup lang="ts" name="GrandChild">
import { inject } from "vue";
let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})
let car = inject('car',{brand:'未知',price:0})
</script>
插槽
默认插槽:slot标签
具名插槽:有名字的插槽
作用域插槽:
数据结构都是由子组件来维护的(数据都在子组件中),但数据生成的结构是由父组件来决定的,就需要用到作用域插槽
父组件代码:v-slot="params" v-slot中的参数收到的是从插槽传过来的全部params数据形成的一个对象,名字自取,亦可v-slot="p"
<template>
<div class="father">
<h3>父组件</h3>
<div class="content">
<Game>
<template v-slot="params">
<ul>
<li v-for="y in params.youxi" :key="y.id">
{{ y.name }}
</li>
</ul>
</template>
</Game>
</div>
</div>
</template>
<script setup lang="ts" name="Father">
import Game from './Game.vue'
</script>
子组件代码:<slot :youxi="games" x="哈哈" y="你好"></slot> 这是一个默认插槽
<template>
<div class="game">
<h2>游戏列表</h2>
<slot :youxi="games" x="哈哈" y="你好"></slot>
</div>
</template>
<script setup lang="ts" name="Game">
import {reactive} from 'vue'
let games = reactive([
{id:'asgytdfats01',name:'英雄联盟'},
{id:'asgytdfats02',name:'王者农药'},
{id:'asgytdfats03',name:'红色警戒'},
{id:'asgytdfats04',name:'斗罗大陆'}
])
</script>
注意:我们也可以为插槽附上名字,
<slot name="qwe" :youxi="games" x="哈哈" y="你好"></slot>
父组件中获取参数时也要写对应的插槽名
<template v-slot: qwe = "params">
你要获得的是youxi 里面的内容:所以父组件获取数据信息时就可以直接解构出来
<template v-slot: qwe = "{youxi}">
各种常见,实用api
shallowRef 和 shallowReactive
shallowRef 和 shallowReactive定义的数据只能修改最浅层的数据,只能第一层的数据是响应式的
绕开了深层次响应的定义
readonly 和 shallowReadonly
readonly 创建一个对像的只读副本
let sum1 = ref(0)
let sum2 = readonly(sum1)
let car1 = reactive({
brand:'奔驰',
options:{
color:'红色',
price:100
}
})
let car2 = readonly(car1)
如上述代码:sum1是可以都且修改的变量,sum2是sum1的一个只读副本,他的值不能修改,会随着sum1的变化而变化,car2中的任何一个属性也不能被修改,只能读,也是会随着car1的变化而变化。
shallowReadonly是一个浅层次的只读,只处理浅层次的只读,下面代码中,car2的第一层是只读的,深层次的属性就可以修改
let car1 = reactive({
brand:'奔驰',
options:{
color:'红色',
price:100
}
})
let car2 = shallowReadonly(car1)
toRaw 和 markRaw
toRaw 用来获取一个响应对象的原始对象,toRaw返回的对象就不是响应式对象了
/* toRaw */
let person = reactive({
name:'tony',
age:18
})
// 用于获取一个响应式对象的原始对象
let rawPerson = toRaw(person)
// console.log('响应式对象',person)
// console.log('原始对象',rawPerson)
markRow :标记一个数据对象,使其永远不会成为响应式数据对像
自定义ref—customref
作用:创建一个自定义的ref, 对齐依赖项跟踪和更新出发进行逻辑控制,比如实现防抖效果
根据实际情况将其打包成一个hooks组件
customref 包含一个回调参数,回调参数中包含两个参数,track 和 trigger ,track 是一个跟踪函数,在参数被读取时调用,而trigger 是触发函数,在数据被修改时调用
customref 函数要返回get函数和set函数,前者在数据被读取时调用,后者在数据被修改时调用
import { customRef } from "vue";
export default function(initValue:string,delay:number){
// 使用Vue提供的customRef定义响应式数据
let timer:number
// track(跟踪)、trigger(触发)
let msg = customRef((track,trigger)=>{
return {
// get何时调用?—— msg被读取时
get(){
track() //告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新
return initValue
},
// set何时调用?—— msg被修改时
set(value){
clearTimeout(timer)
timer = setTimeout(() => {
initValue = value
trigger() //通知Vue一下数据msg变化了
}, delay);
}
}
})
return {msg}
}
Teleport:作用:将组件的html 结构放在指定的位置
下述代码就将modal 这个div的结构直接 放在body里
<template>
<button @click="isShow = true">展示弹窗</button>
<teleport to='body'>
<div class="modal" v-show="isShow">
<h2>我是弹窗的标题</h2>
<p>我是弹窗的内容</p>
<button @click="isShow = false">关闭弹窗</button>
</div>
</teleport>
</template>