vue3学习记录四

路由配置的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>

Suspense

全局API转移到应用对象上

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值