vue3(下)
生命周期
概念:Vue
组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue
会在合适的时机,调用特定的函数
Vue2
的生命周期
生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁
创建阶段:
beforeCreate
、created
挂载阶段:
beforeMount
、mounted
更新阶段:
beforeUpdate
、updated
销毁阶段:
beforeDestroy
、destroyed
Vue3
的生命周期
生命周期整体分为四个阶段,分别是:创建、挂载、更新、卸载
创建阶段:
setup
挂载阶段:
onBeforeMount
、onMounted
更新阶段:
onBeforeUpdate
、onUpdated
卸载阶段:
onBeforeUnmount
、onUnmounted
停止语句-> debugger
销毁在父里用 v-show=变量名 (false)隐藏 || v-if(false)连同结构一起删
常用的钩子:
onMounted
(挂载完毕)、onUpdated
(更新完毕)、onBeforeUnmount
(卸载之前)
自定义hook
本质是一个函数,把setup
函数中使用的Composition API
进行了封装。
自定义hook
的优势:复用代码, 让setup
中的逻辑更清楚易懂。
在.ts另外写一个 引入、用函数封装的{对象,方法},必须暴露出去export default
默认暴露,后面直接跟一个值
如果只是export
后面跟着的是一个函数必须起名export default function(){}
调用:先引入,后调用(主文件中......vue)
<script setup lang="ts">
import useSum from './hooks/useSum'
import useDog from './hooks/useDog'
let {sum,increment,decrement} = useSum()
let {dogList,getDog} = useDog()
</script>
请求方法(重点单词async await axios try catch)
axios 只会请求正确的接口,if接口错误会什么都没有。必须得先引入
import {reactive,onMounted} from 'vue'
import axios,{AxiosError} from 'axios'
async try await catch捕捉错误并提示
async function getDog(){
try {
// 发请求
let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// 维护数据
dogList.push(data.message)
} catch (error) {
// 处理错误
const err = <AxiosError>error
console.log(err.message)
}
}
路由
基本切换效果
//创建一个路由器并暴露出去(引入+创建)
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(),//路由器的工作模式
//路由:对象们[{path:'路径',component:'组件'}...{}]
routes:[
{
path:'/home',
component:Home
},
{
path:'/about',
component:About
}
]
})
//暴露
export default router
main.ts
代码(操作,路由参与代码)
import router from './router/index'
//App根组件的使用
//创建一个应用
//使用路由器
app.use(router)
//挂载整个应用到app容器中
app.mount('#app')
使用App.vue
引入路由器后在html里 <RouterView></RouterView> import {RouterLink,RouterView} from 'vue-router'
路由器工作模式
history
模式
优点:URL
更加美观,不带有#
,更接近传统的网站URL
。
缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有404
错误。
const router = createRouter({ history:createWebHistory(), //history模式 /******/ })
hash
模式
优点:兼容性更好,因为不需要服务器端处理路径。
缺点:URL
带有#
不太美观,且在SEO
优化方面相对较差。
const router = createRouter({ history:createWebHashHistory(), //hash模式 /******/ })
4.5. 【to的两种写法】
<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>
<!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>
ink>
嵌套路由
编写
News
的子路由:Detail.vue
配置路由规则,使用
children
配置项:
跳转路由:
<!--简化前:需要写完整的路径(to的字符串写法) -->
<router-link to="/news/detail">跳转</router-link>
<!--简化后:直接通过名字跳转(to的对象写法配合name属性) -->
<router-link :to="{name:'guanyu'}">跳转</router-l
路由的props配置
作用:让路由组件更方便的收到参数(可以将路由参数作为props
传给组件)
props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
props:{a:1,b:2,c:3},
// props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
props:true
//在vue里直接调用 defineProps(['id','title','content'])
// props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
props(route){
return route.query
replace属性
作用:控制路由跳转时操作浏览器历史记录的模式。
浏览器的历史记录有两种写入方式:分别为
push
和replace
:push
是追加历史记录(默认值)。replace
是替换当前记录。
开启
replace
模式:在<template> 里的Routerlink 后
<RouterLink replace .......>News</RouterLink>
编程式导航
路由组件的两个重要的属性:$route
和$router
变成了两个hooks
to
重定向
作用:将特定的路径,重新定向到已有路由。
具体编码:
{ path:'/', redirect:'/about' }
pinia
准备一个效果
存储+读取数据
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:{} })
修改数据
第一种修改方式,直接修改
const countStore=useCountStore()
countStore.sum = 666
第二种修改方式:批量修改($patch)
countStore.$patch({ sum:999, school:'atguigu' })
第三种修改方式:借助
action
修改(action
中可以编写一些业务逻辑) actions里面放置的是一个一个的犯法,用于响应组件中的“动作”
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)
getters
概念:当
state
中的数据,需要经过处理后再使用时,可以使用getters
配置。追加
getters
配置。
getters:{
bigSum:(state):number => state.sum *10,
upperSchool():string{
return this. school.toUpperCase()
}
}
组件中读取数据:
const {increment,decrement} = countStore let {sum,school,bigSum,upperSchool} = storeToRefs(countStore)
$subscribe
通过 store 的 $subscribe()
方法侦听 state
及其变化
talkStore.$subscribe((mutate,state)=>{
console.log('LoveTalk',mutate,state)
localStorage.setItem('talk',JSON.stringify(talkList.value))
})
store组合式写法
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}
组件通信
Vue3
组件通信和Vue2
的区别:
移出事件总线,使用
mitt
代替。
vuex
换成了pinia
。把
.sync
优化到了v-model
里面了。把
$listeners
所有的东西,合并到$attrs
中了。$children
被砍掉了。
props
概述:props
是使用频率最高的一种通信方式,常用与 :父 ↔ 子。
若 父传子:属性值是非函数。
若 子传父:属性值是函数。
父组件:
<template>
<div class="father">
<h3>父组件,</h3>
<h4>我的车:{{ car }}</h4>
<h4>儿子给的玩具:{{ toy }}</h4>
<Child :car="car" :getToy="getToy"/>
</div>
</template>
<script setup lang="ts" name="Father">
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">
<h3>子组件</h3>
<h4>我的玩具:{{ toy }}</h4>
<h4>父给我的车:{{ car }}</h4>
<button @click="getToy(toy)">玩具给父亲</button>
</div>
</template>
<script setup lang="ts" name="Child">
import { ref } from "vue";
const toy = ref('奥特曼')
defineProps(['car','getToy'])
</script>
概述:自定义事件常用于:子 => 父。
注意区分好:原生事件、自定义事件。
原生事件:
事件名是特定的(
click
、mosueenter
等等)事件对象
$event
: 是包含事件相关信息的对象(pageX
、pageY
、target
、keyCode
)
自定义事件:
事件名是任意名称
<strong style="color:red">事件对象
$event
: 是调用emit
时所提供的数据,可以是任意类型!!!</strong >
示例:
<!--在父组件中,给子组件绑定自定义事件:--> <Child @send-toy="toy = $event"/> <!--注意区分原生事件与自定义事件中的$event--> <button @click="toy = $event">测试</button>
//子组件中,触发事件: this.$emit('send-toy', 具体数据)
概述:与消息订阅与发布(pubsub
)功能类似,可以实现任意组件间通信。
v-model
概述:实现 父↔子 之间相互通信。
前序知识 ——
v-model
的本质<!-- 使用v-model指令 --> <input type="text" v-model="userName"> <!-- v-model的本质是下面这行代码 --> <input type="text" :value="userName" @input="userName =(<HTMLInputElement>$event.target).value" >
组件标签上的
v-model
的本质::moldeValue
+update:modelValue
事件。<!-- 组件标签上使用v-model指令 --> <AtguiguInput v-model="userName"/> <!-- 组件标签上v-model的本质 --> <AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>
AtguiguInput
组件中:<template> <div class="box"> <!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 --> <!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件--> <input type="text" :value="modelValue" @input="emit('update:model-value',$event.target.value)" > </div> </template> <script setup lang="ts" name="AtguiguInput"> // 接收props defineProps(['modelValue']) // 声明事件 const emit = defineEmits(['update:model-value']) </script>
$attrs
概述:
$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。具体说明:
$attrs
是一个对象,包含所有父组件传入的标签属性。注意:
$attrs
会自动排除props
中声明的属性(可以认为声明过的props
被子组件自己“消费”了)