目录
非父子组件通讯
全局事件总线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')//默认值为hhhhreturn{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>