目录
非父子组件通讯
全局事件总线mitt库
组件的生命周期
$refs
动态组件
keep-alive
异步打包
v-model绑定组件
Composition API
定义响应式数据
readonly
toRefs与toRef
computed
$ref
生命周期钩子
provide和inject
watch侦听
watchEffect
script setup语法
defineProps和defineEmits
非父子组件通讯
即在上层组件中使用 provide ,在下层组件中使用 inject
上层组件:
<script>
import infos from './components/infos.vue'
export default{
components: { infos },
provide:{
name:'hhh',
age:18
}
}
</script>
如果传递的是自身的变量,应该将provide函数处理为函数,原因是使得this能指向组件
<script>
import infos from './components/infos.vue'
export default{
components: { infos },
data(){
return{
name:'hhh'
}
},
provide(){
return{
name:this.name
}
}
}
</script>
如果我们要传递的值可能会发生改变,那么需要 computed 方法进行响应式跟踪
使用这种方法,下层组件在使用时 应该加 .value
<template>
<div>
<infos></infos>
<button @click="name='qwee'"></button>
</div>
</template>
<script>
import infos from './components/infos.vue'
import { computed} from 'vue'
export default{
components: { infos },
data(){
return{
name:'hhh'
}
},
provide(){
return{
name:computed(()=>{return this.name})
}
}
}
</script>
<style scoped>
</style>
下层组件
<template>
<h1>我是第二个子组件{{name}}</h1>
</template>
<script>
export default{
inject:['name']
}
</script>
<style scoped>
</style>
全局事件总线mitt库
安装管理事件总线管理工具
npm i hy-event-store
一般来说会创建一个js文件创建一个新的事件总线对象
import{HYEventBus}from 'hy-event-store'
const eventbus = new HYEventBus()
export default eventbus
对于需要发送数据的组件,采用eventbus.emit方法,里面的参数,第一个是传递的名字,后面是内容
<template>
<div>
<h1>我是第二个子组件</h1>
<button @click='transfer'>111</button>
</div>
</template>
<script>
import eventbus from '@/utils/hy_bus.js'
export default{
methods:{
transfer(){
console.log(111);
eventbus.emit("event1",'boke',18)
}
}
}
</script>
<style scoped>
</style>
需要监听的组件,采用 event.on接受,并在里面采用回调函数得到参数并做出响应
<script>
import infos from "./components/infos.vue";
import eventbus from "./utils/hy_bus.js";
export default {
components: { infos },
data() {
return {
name: "hhh1",
};
},
created() {
eventbus.on("event1", (name,age) => {
console.log(name);
this.name = name;
});
},
};
</script>
但是一般需要监听的组件的写法是:
export default {
// components: { infos },
methods:{
eventFn(name,age){
console.log(name);
console.log(age);
}
},
created() {
eventbus.on("event1", this.eventFn);
},
unmounted(){
eventbus.off('event1',this.eventFn)
}
};
</script>
给回调函数定义函数名,并在此组件销毁时移除这个事件监听
组件的生命周期
也就是组件从创建到销毁会经历一系列的周期,在每个生命阶段都可以指定调用回调函数,生命周期与回调函数关系如下图
<script>
import infos from "./components/infos.vue";
import eventbus from "./utils/hy_bus.js";
export default {
components: { infos },
methods:{
eventFn(name,age){
console.log(name);
console.log(age);
}
},
beforeCreate(){
console.log('创建组件实例之前');
},
created(){
console.log('已经创建组件实例');
console.log('1.发送网络请求,请求数据');
console.log('2.监听eventbus事件');
console.log('3.监听watch数据');
},
beforeMount(){
console.log('组件挂载之前');
},
mounted(){
console.log('已经挂载,虚拟dom >> 真实dom');
console.log('获取DOM,使用dom');
},
beforeUpdate(){
console.log('数据改变了,但是页面还没改变');
},
updated(){
console.log('页面发生了改变');
},
beforeUnmount(){
console.log('准备卸载VNode');
},
unmout(){
console.log('组件已经卸载完成');
}
}
</script>
$refs
在vue中一般不会直接使用document.querySelector得到DOM并操作它
但是如果非要和DOM产生关联,VUE提供了一种方法:给元素绑定一个ref属性
使用方法
在元素中绑定 ref='xxx'
js调用中使用 this.$ref.xxx
<template>
<div>
<button @click='transfer'>111</button>
<span ref='span'></span>
</div>
</template>
<script>
import eventbus from '@/utils/hy_bus.js'
export default{
methods:{
transfer(){
console.log(this.$refs.span);
}
}
}
</script>
<style scoped>
</style>
当然这个方法也可以获取子组件实例,在获取之后甚至可以操作子组件内部的方法
this.$refs.item.fn()
如果想获取子组件中的根元素
this.$refs.item.$el
动态组件
当面对这个需求:点击按钮切换界面
对于切换界面,我们会引入n个vue文件,然后在APP.vue中使用v-if来进行选择
但是这样是很繁琐的,这里可以采用动态组件
<component is="XXXX"></component>
在引入组件之后,使用is="XXX"来选择显示XXX组件
keep-alive
对于上述动态组件的例子,当我切换界面时,原有界面会被销毁
对于需要频繁切换的界面,这样的效率是及其低下的,且如果在原有界面中有需要保持的数据,例如有一个计数器,那么在切换界面后我们还是希望保存这个数据,既将原有界面放置在缓存中
可以使用keep-alive
只需要使用 <keep-alive> 装载想要缓存的界面
<keep-alive>
<component :is="products[currentIndex]"></component>
</keep-alive>
以include举例,我只想要部分组件是keep-alive的,那么:
<keep-alive include="page">
<component :is="products[currentIndex]"></component>
</keep-alive>
但是这个地方的 page 也就是组件名称,需要在组件内部自己定义
<script>
export default {
name:'page',
data(){
return{
value:0
}
},
异步打包
在我们运行 npm run build 进行打包时,会生成两个js文件
第一个js文件包含自己编写的所有JS文件的总和,第二个js文件包含的是第三方库的js代码
很显然,如果我们自己编写了很多组件,且某些组件很大时,将其全部放在一个js文件的方式是不妥的,因为当我需要向服务器申请页面时,需要将全部代码都进行下载
那么在引入组件时,可以采用异步申请的方式,也就是在打包时,会给这个组件单独创造一个js文件
用到了defineAsyncComponent函数
import{ defineAsyncComponent } from 'vue'
const AsyncNavitem = defineAsyncComponent(()=>import('./components/navitem.vue'))
其他组件方法和普通的一样,这时打包会创建三个js文件
v-model绑定组件
v-model的普通用法参照:
【VUE3】保姆级基础讲解(一):初体验与指令_独憩的博客-CSDN博客
也可以将变量绑定到组件中
<template>
<page v-model="value"></page>
</template>
<script>
import Page from './components/page.vue'
export default {
data(){
return{
value:0
}
},
components:{Page},
}
</script>
<style scoped>
</style>
这个地方v-model其实相当于:
<page :modelValue="value" @updata:modelValue="value=$event"></page>
既将父组件的value传给子组件的modelValue(固定名字)中,并绑定updata:modelValue方法,将value值改变为子组件传过来的数值
在子组件中,应该接受参数 modelValue ,并传递数值方法名称updata:modelValue
<script>
export default {
props:{
modelValue:{
type:Number,
default:0
}
},
emits:['update:modelValue'],
methods:{
add(){
this.$emit('update:modelValue',100)
}
}
}
</script>
当然如果不想要 modelValue这个名字,也可以自定义,下面将其改为了myname,那么在子组件中也需要修改接收变量名称,以及传递数值方法名称:update:myname
<page v-model:myname="value"></page>
Composition API
举一个按钮计数器的例子来感受基础用法
<template>
<h1>{{Value}}</h1>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup(){
let Value = ref(0)//响应式数据
let increment = ()=>{
Value.value++
}
let decrement = ()=>{
Value.value--
}
return{
Value,increment,decrement
}
}
}
</script>
<style scoped>
</style>
将所有逻辑都写在setup函数中,并返回需要调用的变量和方法
定义响应式数据
在setup函数中定义响应式数据,需要借助别的工具
对于简单数据类型,使用ref函数
对于复杂数据类型,使用reactive函数
setup(){
let Value = ref(0)
let diff = reactive({
name:'kobe',
age : 18
})
return{
Value,diff
}
}
对于复杂数据类型,在使用时需要:diff.name diff.age
对于ref函数,在内部逻辑中若想改变或获取其值,应该加.value,例如 Value.value,但是在模板中使用则不需要
当然,ref函数也是可以定义复杂数据类型的,在逻辑中操作时也需要加.value:
<template>
<h1>{{Value.counter}}</h1>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</template>
<script>
import { ref,reactive } from 'vue'
export default {
setup(){
let Value = ref({
counter:0
})
let increment = ()=>{
Value.value.counter++
}
let decrement = ()=>{
Value.value.counter--
}
return{
Value,increment,decrement
}
}
}
</script>
<style scoped>
</style>
readonly
一般父组件给子组件传递数据时,不希望子组件对数据进行更改
你们可以使用readonly函数,让数据是只读的
<template>
<h1>{{Value.counter}}</h1>
<page :info="ValueReadonly"></page>
</template>
<script>
import { ref,reactive,readonly } from 'vue'
import page from './components/page.vue'
export default {
components:{page},
setup(){
let Value = ref({
counter:0,
name:'kobe'
})
let ValueReadonly =readonly(Value)
return{
Value,ValueReadonly
}
}
}
</script>
<style scoped>
</style>
toRefs与toRef
在解构时保持数据的响应式
export default {
components:{page},
setup(){
let Value = reactive({
counter:0,
name:'kobe'
})
let{counter,name} = toRefs(Value)
let name1 = toRef(Value,'name')
return{
counter,name,name1
}
}
}
computed
使用时直接在 computed()定义函数,默认调用其getter
<template>
<h1>{{fullname}}</h1>
</template>
<script>
import { ref,reactive,readonly,computed } from 'vue'
// import page from './components/page.vue'
export default {
components:{page},
setup(){
let name = reactive({
firstname :'lee',
lastname :'xl'
})
let fullname = computed(()=>{
return name.firstname+''+name.lastname
})
return{
name,
fullname
}
}
}
</script>
<style scoped>
</style>
$ref
在之前的options api中若想得到dom元素,可以使用ref属性,在js调用中采用 this.$ref.XXX的方法
但是在setup函数中不存在this ,那么可以:
定义一个空的ref,名字与DOM中定义的名字一致
<template>
<h1 ref="h11">123</h1>
<button @click="changeh11"></button>
</template>
<script>
import { ref,reactive,readonly,computed,onMounted} from 'vue'
import page from './components/page.vue'
export default {
components:{page},
setup(){
let h11 = ref()
let changeh11 =function(){
h11.value.innerHTML=222
}
return{
h11,
changeh11
}
}
}
</script>
<style scoped>
</style>
生命周期钩子
与options API类似,Composition API也有生命周期函数,只是其一般是 onX函数
<script>
import {onMounted} from 'vue'
export default {
components:{page},
setup(){
onMounted(()=>{
console.log(111);
})
return{
}
}
}
</script>
与options API对应关系为
如果需要实现beforeCreate和created功能,直接在setup函数中编写就可以了
provide和inject
父组件
<script>
import { ref,reactive,computed,provide} from 'vue'
import page from './components/page.vue'
export default {
components:{page},
setup(){
let name = ref('kobe')
provide('name',name)
return{
name
}
}
}
</script>
子组件
<script>
import { ref,inject } from 'vue'
export default {
setup(){
let name1 = inject('name','hhhh')//默认值为hhhh
return{
name1
}
}
}
</script>
watch侦听
与options api类似
<script>
import { ref,reactive,watch} from 'vue'
export default {
setup(){
let name = ref('kobe')
watch(name,(newvalue,oldvalue)=>{
console.log(newvalue);
console.log(oldvalue);
})
return{
name
}
}
}
</script>
也可以选择immediate和deep参数
watch(name,(newvalue,oldvalue)=>{
console.log(newvalue);
console.log(oldvalue);
},{
immediate:true,
deep:true
})
watchEffect
当侦听到某个数据发生变化时,我们希望进行某种操作,就用watchEffect
1、watchEffect传入的函数默认会直接执行
2、在执行过程中,会自动收集依赖
<script>
import { ref,reactive,watchEffect} from 'vue'
export default {
setup(){
let counter =ref(0)
watchEffect(()=>{
console.log(counter.value);
})
return{
counter
}
}
}
</script>
每次counter发生改变,watchEffect都会自动收集依赖并执行
若希望在某种情况下停止监听:
let stopwatch = watchEffect(()=>{
console.log(counter.value);
if(counter.value>=10){
stopwatch()
}
})
script setup语法
可以简写setup函数,不再需要写setup函数,也不需要写return
直接使用script setup
<script setup>
import { ref, reactive, watchEffect } from 'vue'
import page from './components/page.vue'
import Counter from './js/counter.js'
let counter =ref(0)
function higher(){
counter.value++
}
function lower(){
counter.value--
}
</script>
defineProps和defineEmits
用于在语法糖下接受和发送数据与事件
<template>
<h1>{{props.name}}</h1>
<button @click="btnclick">+</button>
</template>
<script setup>
import { ref,watch } from 'vue'
import Counter from '../js/counter.js'
//接受数据
let props = defineProps({
name:{
type:String,
default:'lee'
}
})
//发出事件
let emits = defineEmits(['btnclick'])
function btnclick(){
emits('btnclick','aaaa')
}
</script>