【VUE3】保姆级基础讲解(三)非父子组件通讯,$refs,动态组件,keep-alive,Composition API

目录

非父子组件通讯

全局事件总线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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值