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

news2024/11/24 18:49:19

目录

非父子组件通讯

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114730.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++必须掌握的知识点

面向对象的三大特性 封装 继承 父类中所有的非静态成员都会被子类继承下去&#xff0c;只是父类的私有成员被编译器屏蔽了&#xff0c;访问不到。可以利用开发人员工具查看对象模型继承中&#xff0c;先构造父类&#xff0c;再构造子类&#xff0c;析构的顺序和构造的顺序完…

QT系列第8节 自定义对话框

在实际业务开发中经常要有各种各样的对话框来处理用户信息&#xff0c;本节就结合例子来说明如何自定义对话框。 目录 1.创建对话框 2.创建非模态对话框 3.创建模态对话框 4.综合案例 1.创建对话框 &#xff08;1&#xff09;项目鼠标右键菜单 - 添加新文件 &#xff08;…

Hexo + Butterfly 自定义页脚

原文链接 &#xff1a;Hexo Butterfly 自定义页脚 推荐阅读 基于 Hexo 从零开始搭建个人博客&#xff08;一&#xff09;: 环境准备基于 Hexo 从零开始搭建个人博客&#xff08;二&#xff09;: 项目初识基于 Hexo 从零开始搭建个人博客&#xff08;三&#xff09;: 主题安装…

CSDN每日一练最长递增的区间长度 C语言

题目名称&#xff1a;最长递增的区间长度 时间限制&#xff1a;1000ms 内存限制&#xff1a;256M 题目描述 给一个无序数组&#xff0c;求最长递增的区间长度。如&#xff1a;[5,2,3,8,1,9] 最长区间 2,3,8 长度为 3 &#xff08;注意&#xff1a;测试用例仅做参考&#xff0c;…

Spring web开发之Request 获取三种方式

在开发 Java Web 项目中&#xff0c;我们经常使用 HttpServletRequest 获取请求参数、请求头等信息。在Spring项目&#xff0c;我们通常会使用 Spring 提供的注解获取参数&#xff0c;如 RequestParam、RequestHeader。 不过在某些场景下&#xff0c;我们可能需要从 HttpServl…

初识Docker:(4)Docker基本操作

初识Docker&#xff1a;&#xff08;4&#xff09;Docker基本操作1 镜像操作1.1 镜像名称1.2 镜像操作命令1.3 案例&#xff1a;docker拉取nginx镜像利用docker save将nginx镜像导出磁盘&#xff0c;然后再通过load加载回来1.4 镜像操作总结2 容器操作2.1 案例创建运行一个ngin…

【阅读笔记】《持续交付2.0》中理解分支、发布策略

文章目录1. 前言1.1 分支、发布 管理上解耦2. 主干 (Trunk) 和分支 (Branch)2.1 Trunk 开发 Trunk 发布2.1.1 Trunk 开发 Trunk 发布需要解决&#xff1a;重构的需求2.1.2 Trunk 开发 Trunk 发布需要解决&#xff1a;未开发完成的功能被带入发布版本2.2 Trunk 开发 Branch 发布…

leetcode:6272. 好分区的数目【思维转换(正难则反) + dp定义 + 背包问题 + 选or不选】

目录题目截图题目分析ac code总结题目截图 题目分析 先特判&#xff0c;如果sum(nums) < 2 * k显然不可能成功&#xff01;返回0出现Mod大概率就是dp1000的话提示我们用平方复杂度的dp这种取子序列的问题&#xff0c;本质就是选or不选的问题如果我们只考虑一维dp,dp[i]肯定…

Linux--信号

目录1. 信号概念2. 信号产生前2.1 信号产生的各种方式3. 信号产生中信号保存的方式3.1 阻塞信号3.2 信号屏蔽字4. 信号产生后信号处理的方式4.1 信号集操作函数4.2 sigprocmask函数4.3 sigpending函数4.4 sigaction函数5. 信号是什么时候被处理的1. 信号概念 信号是进程之间事…

golang访问KingbaseES V8R6

概述 本文介绍go语言连接KingbaseES V8R6数据库的步骤 测试环境 操作系统&#xff1a;CentOS 7.2.1511 数据库版本&#xff1a;KINGBASE (KingbaseES) V008R006C007B0012 go版本&#xff1a;go version go1.19.4 linux/amd64 KingbaseES go驱动获取 go连接kingbase数据库需…

MySQL为什么使用B+树为索引结构

目录 1、什么是索引 2、索引的类型 3、为什么要用索引 4、索引的使用场景 5、索引为什么要用B树&#xff0c;为什么不能用二叉树、红黑树、B树&#xff1f; 介绍一款可以帮助理解数据结构的网站&#xff08;很好用&#xff09;&#xff1a;Data Structure Visualization …

hadoop生产调优之Hadoop-Yarn 生产经验(参数调优)

一、常用的调优参数 1&#xff09;调优参数列表 &#xff08;1&#xff09;Resourcemanager 相关 yarn.resourcemanager.scheduler.client.thread-count ResourceManager 处理调度器请求的线程数量 yarn.resourcemanager.scheduler.class 配置调度器&#xff08;2&#xff0…

js中ArrayBuffer和node中Buffer的关系和区别

ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。 它是一个字节数组&#xff0c;通常在其他语言中称为“byte array”。你不能直接操作 ArrayBuffer 中的内容&#xff1b;而是要通过类型化数组对象或 DataView 对象来操作&#xff0c;它们会将缓冲区中的数据…

C++、python、VS code插件安装与SSH使用

下载按照VS coda 官网&#xff1a;https://code.visualstudio.com 1.安装相关插件 1.中文插件&#xff08;可选&#xff09; MS-CEINTL.vscode-language-pack-zh-hans 2.C插件&#xff08;必选&#xff09; ms-vscode.cpptools 3.ssh 远程&#xff08;必选&#xff09; ms-vs…

数据结构——快排的三种实现方式

坚持看完&#xff0c;结尾有思维导图总结 这里写目录标题什么是快排&#xff1f;如何实现递归单次的排序要如何实现hore 的办法![在这里插入图片描述](https://img-blog.csdnimg.cn/40b2ac63f2424bd1828a45f8509ff116.gif#pic_center)坑位法双指针法总结什么是快排&#xff1f;…

线程池(一)

个人博客地址&#xff1a; http://xiaohe-blog.top/index.php/archives/14/ 文章目录1. 为什么要使用线程池2. Executor3. ThreadPoolExecutor3.1 七个参数3.2 任务队列3.3 拒绝策略4. 创建线程池5. Executors5.1 CachedThreadPool5.2 FixedThreadPool5.3 SingleThreadExecutor…

SAP UI5 应用里一些容器控件的介绍

sap.m.Shell 控件可用作应用程序的根元素。 它可以包含 App 或 SplitApp 控件。 Shell 为整个应用程序提供了一些总体功能&#xff0c;并负责在桌面浏览器平台上进行视觉适配&#xff0c;例如应用程序周围的框架。 sap.m.App: 该 App 继承自 sap.m.NavContainer 并因此提供其导…

VUEElement 学习笔记

1 VUE 1.1 示例 新建test_vue.jsp <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>Title</title> </head> <body><div id"app"><input name"…

三、SpringBoot启动流程及自动化配置

一、Springboot启动流程 图一:Springboot项目的启动流程 首先,针对上图中自己不太明确的两个知识点,这里做如下总结: 1.Banner:参考这篇文章:SpringBoot之Banner介绍 - MarkLogZhu - 博客园 (cnblogs.com) ; 2.钩子方…

【Javassist】快速入门系列07 当检测到字段被访问时使用语句块替换访问

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 04 使用Javassist更改整个方法体 05 当有指定方法调用时替换方法调用的内容 06 当有构造方法调用时替换方法调用的内容 07 当检测到字段被访问时使用语…