Vue3:组件高级(上)

news2024/11/22 9:22:18

Vue3:组件高级(上)

Date: May 20, 2023
Sum: watch倾听器、组件的生命周期、组件之间的数据共享、vue3.x中全局配置axios


目标:

能够掌握 watch 侦听器的基本使用

能够知道 vue 中常用的生命周期函数

能够知道如何实现组件之间的数据共享

能够知道如何在 vue3.x 的项目中全局配置 axios




前言:以下使用较老的axios,否则会报错

npm i axios@0.21.1 -S

watch 侦听器

  1. 什么是 watch 侦听器

watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。例如,监视用户名的

变化并发起请求,判断用户名是否可用。

  1. watch 侦听器的基本语法

开发者需要在 watch 节点下,定义自己的侦听器。实例代码如下:

export default {
  data() {
    return {
      username: ''
    }
  },
  watch: {
		// 监听 username 的值的变化
		// 形参列表中,第一个值是“变化后的新值”,第二个值是“变化之前的旧值”
   username(newVal, oldVal) {
      console.log(newVal, oldVal);
    }
  }
}
  1. 使用 watch 检测用户名是否可用

监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:

Untitled

复习并理解:

返回promise对象:

const res = axios.get('http://www.escook.cn/api/finduser/' + newVal)

Untitled

若返回promise对象,我们可以通过await/async进行简化,返回的则是一个数据对象,

async username(newVal, oldVal) {
      console.log(newVal, oldVal);
      const res = await axios.get('http://www.escook.cn/api/finduser/' + newVal)
      console.log(res);
    }

Untitled

  1. immediate 选项

默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项。实例代码如下:

Untitled

  1. deep 选项

当 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项,代码示例如下:

data() {
    return {
      username: 'admin',
      info: {
        username: 'zs' //info中包含 username 属性
      }
    }
  },
  watch: {
    info: { //直接监听 info 对象的变化
      async handler(newVal) {
        const { data: res } = await axios.get('https://www.escook.cn/api/finduser' + newVal.username)
        console.log(res);
      },
      deep: true // 需要使用 deep 选项, 否则 username 值的变化无法被监听到
    }
  }
  1. 监听对象单个属性的变化

如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器:

Untitled

  1. 计算属性 vs 侦听器

计算属性和侦听器侧重的应用场景不同:

计算属性侧重于监听多个值的变化,最终计算并返回一个新值

计算属性侧重于得到最后的一个结果

侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值

侦听器侧重于去执行某个业务逻辑,业务逻辑执行完,侦听器的目的就达到了




组件的生命周期

1. 组件运行的过程

Untitled

组件的生命周期指的是:组件从创建 -> 运行(渲染) -> 销毁的整个过程,强调的是一个时间段

2. 如何监听组件的不同时刻

vue 框架为组件内置了不同时刻的生命周期函数,生命周期函数会伴随着组件的运行而自动调用。

例如:

① 当组件在内存中被创建完毕之后,会自动调用 created 函数

② 当组件被成功的渲染到页面上之后,会自动调用 mounted 函数

③ 当组件被销毁完毕之后,会自动调用 unmounted 函数

案例:

  • Code:

    App.vue

    <template>
      <div>
        <h1>App 根组件</h1>
        <hr/>
        <life-cycle v-if="flag"></life-cycle>
        <button @click="flag = !flag">Toggle</button>
      </div>
    </template>
    
    <script>
    import LifeCycle from './LifeCycle.vue'
    export default {
      name: 'MyApp',
      components: {
        LifeCycle
      },
      data() {
        return {
          flag: false,
        }
      },
    
    }
    </script>
    
    <style>
    
    </style>
    

    LifeCycle.vue

    <template>
      <div>
        <h2>LifeCycle</h2>
      </div>
    </template>
    
    <script>
    export default {
      name: 'LifeCycle',
    
      created() {
        console.log('组件在内存中被创建完毕了');
      },
      mounted() {
        console.log('组件被成功渲染到页面上了');
      },
      unmounted() {
        console.log('组件被销毁完毕了');
      }
    }
    </script>
    
    <style>
    
    </style>
    

理解:代码中的createde mounted() unmounted函数放到子组件LifeCycle中,当子组件创建完毕之后,会调用created函数,当组件被渲染到页面上后,会调用mounted函数,当组件被销毁完毕之后,会调用unmounted函数。

  1. 如何监听组件的更新

当组件的 data 数据更新之后,vue 会自动重新渲染组件的 DOM 结构,从而保证 View 视图展示的数据和Model 数据源保持一致。

当组件被重新渲染完毕之后,会自动调用 updated 生命周期函数。

案例:

  • Code:

    <template>
      <div>
        <h2>LifeCycle</h2>
        <p>{{ count }}</p>
        <button @click="count += 1">+1</button>
      </div>
    </template>
    
    export default {
      name: 'LifeCycle',
      data() {
        return {
          count: 0,
        }
      },
      updated() {
        console.log('组件被重新渲染完毕了');
      },
    }
    

4. 组件中主要的生命周期函数

Untitled

注意:在实际开发中,created 是最常用的生命周期函数!

5. 组件中全部的生命周期函数

Untitled

疑问:为什么不在 beforeCreate 中发 ajax 请求初始数据

发起Ajax请求最好都在creat中

  1. 完整的生命周期图示

可以参考 vue 官方文档给出的“生命周期图示”,进一步理解组件生命周期执行的过程:
https://www.vue3js.cn/docs/zh/guide/instance.html#生命周期图示




组件之间的数据共享

组件之间的关系

在项目开发中,组件之间的关系分为如下 3 种:

① 父子关系 ② 兄弟关系 ③ 后代关系



父子组件之间的数据共享

父子组件之间的数据共享又分为:

① 父 -> 子共享数据 ② 子 -> 父共享数据 ③ 父 <-> 子双向数据同步

Untitled

AB是父子关系,BC有一个共同的父级节点,故二者为兄弟关系。B和EFI都为特殊的兄弟关系。

A和DGH属于后代关系

2.1 父组件向子组件共享数据

父组件通过 v-bind 属性绑定向子组件共享数据。同时,子组件需要使用 props 接收数据。

案例:

<template>
  <div>
    <h1>MyAPP -- {{ count }}</h1>
    <button @click="count += 1">父+1</button>
    <my-son :num="count"></my-son>
  </div>
</template>

<script>
import MySon from './Son.vue'
export default {
  name: 'MyApp',
  components: {
    MySon,
  },
  data() {
    return {
      count: 0,
    }
  }
}
</script>
<template>
  <div>
    <h2>MySon -- {{ num }}</h2>
  </div>
</template>

<script>
export default {
  name: 'MySon',
  props: ['num']
}
</script>

效果:

Untitled


2.2 子组件向父组件共享数据

子组件通过自定义事件的方式向父组件共享数据。

具体步骤:

子组件:

  1. 声明自定义事件 2. 数据变化时,触发自定义事件

父组件:

  1. 监听子组件的自定义事件 numchang 2.通过形参,接收子组件传递过来的数据

案例:

  • Code:

    App.vue

    <template>
      <div>
        <h1>MyAPP -- {{ count }}</h1>
        <button @click="count += 1">父+1</button>
    		<!-- 1. 监听子组件的自定义事件 numchange -->
        <my-son :num="count" @numchange="getNum"></my-son>
      </div>
    </template>
    
    <script>
    import MySon from './Son.vue'
    export default {
      name: 'MyApp',
      components: {
        MySon,
      },
      data() {
        return {
          count: 0,
        }
      },
      methods: {
        getNum(num) { // 2. 通过形参,接收子组件传递过来的数据
          this.count = num
        }
      }
    }
    </script>
    

    Son.vue

    <template>
      <div>
        <h2>MySon -- {{ num }}</h2>
        <button @click="add">+1</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MySon',
      props: ['num'],
      emits: ['numchange'], //1. 声明自定义事件
      methods: {
        add() {
          this.$emit('numchange', this.num + 1) //2,数据变化时,触发自定义事件
        }
      }
    }
    </script>
    

效果:

Untitled


2.3 父子组件之间数据的双向同步

父组件在使用子组件期间,可以使用 v-model 指令维护组件内外数据的双向同步:

具体步骤:

  1. 父组件向子组件的props中传递数据
    1. 这里通过 v-model 方式进行双向数据绑定,维护组件两方数据同步
  2. 子组件声明emits属性,组件内的元素需要以 update: 的方式开头,这里需要更新哪个数据,就把相应数据的值丢过来,比如number
    1. 通过 $emits 的方式将数据发送出去

Untitled

好处:父组件中不用再监听自定义事件,也不用再额外定义事件处理函数

案例:

  • Code:

    App.vue

    <template>
      <div>
        <h1>MyAPP -- {{ count }}</h1>
        <button @click="count += 1">父+1</button>
        <my-son v-model:num="count" ></my-son>
      </div>
    </template>
    
    <script>
    import MySon from './Son.vue'
    export default {
      name: 'MyApp',
      components: {
        MySon,
      },
      data() {
        return {
          count: 0,
        }
      },
    }
    </script>
    

    Son.vue

    <template>
      <div>
        <h2>MySon -- {{ num }}</h2>
        <button @click="add">+1</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MySon',
      props: ['num'],
      emits: ['update:num'],
      methods: {
        add() {
          // this.$emit('numchange', this.num + 1)
          this.$emit('update:num', this.num + 1)
        }
      }
    }
    </script>
    

效果:

Untitled



兄弟组件之间的数据共享

兄弟组件之间实现数据共享的方案是 EventBus。可以借助于第三方的包 mitt 来创建 eventBus 对象,从而实现兄弟组件之间的数据共享。示意图如下:

Untitled

理解:在数据接收方调用on方法来声明自定义事件,在数据发送方通过emit方法来触发emit事件

3.1 安装 mitt 依赖包

在项目中运行如下的命令,安装 mitt 依赖包:

npm install mitt@2.1.0

3.2 创建公共的 EventBus 模块

在项目中创建公共的 eventBus 模块如下:

// eventBus.js

// 导入 mitt 包
import mitt from 'mitt'
// 创建 EventBus 的实例对象
const bus = mitt()

// 将 EventBus 的实例对象共享出去
export default bus

3.3 在数据接收方自定义事件

在数据接收方,调用 bus.on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件。

示例代码如下:

// 导入 eventBus.js 模块, 得到共享的bus对象

export default {
	data() {return { count: 0}},
	created() {
		// 在created生命周期函数中声明自定义事件
		// 调用 bus.on 方法注册一个自定义事件,通过事件处理函数的形参数接收数据
		bus.on('countChange', (count) => {
			this.count = count
		})
	}
}

3.4 在数据接发送方触发事件

在数据发送方,调用 bus.emit(‘事件名称’, 要发送的数据) 方法触发自定义事件。示例代码如下:

// 导入 eventBus.js 模块,得到共享的 bus 对象
import bus from './eventBus.js'

export default {
	data() {return { count: 0}},
	methods: {
		addCount() {
			this.count++
			bus.emit('countChange', this.count) // 调用 bus.emit() 方法触发自定义事件,并发送数据
		}	
	}
}

案例:

  • Code:

    Left.vue

    <template>
      <div>
        <h2>Left--数据发送方--num的值为: {{ count }}</h2>
        <button @click="addCount">+1</button>
      </div>
    </template>
    
    <script>
    import bus from './eventBus.js'
    export default {
      name: 'MyLeft',
      data() {
        return {
          count: 0,
        }
      },
      methods: {
        addCount() {
          this.count++
          bus.emit('countChange', this.count)
        }
      }
    }
    </script>
    

    Right.vue

    <template>
      <div>
        <h2>Right--数据接收方--num的值为:{{ num }}</h2>
      </div>
    </template>
    
    <script>
    import bus from './eventBus.js'
    
    export default {
      name: 'MyRight',
      data() {
        return {
          num: 0,
        }
      },
      created() {
        bus.on('countChange', count => {
          this.num = count
        })
      }
    }
    </script> 
    

效果:

Untitled



后代关系组件之间的数据共享

后代关系组件之间共享数据,指的是父节点的组件向其子孙组件共享数据。此时组件之间的嵌套关系比较复杂,可以使用 provide 和 inj ect 实现后代关系组件之间的数据共享。

4.1 父节点通过 provide 共享数据

父节点的组件可以通过 provide 方法,对其子孙组件共享数据:

Untitled


4.2 子孙节点通过 inject 接收数据

子孙节点可以使用 inject 数组,接收父级节点向下共享的数据。示例代码如下:

Untitled


4.3 父节点对外共享响应式的数据

值得注意的是,provide中return回去的数据,并非是响应式的数据,即若我在父组件中用button修改p标签的颜色,子组件的中的p标签颜色不会跟着一块变。

父节点使用 provide 向下共享数据时,可以结合 computed 函数向下共享响应式的数据。示例代码如下:

Untitled


4.4 子孙节点使用响应式的数据

如果父级节点共享的是响应式的数据,则子孙节点必须以 .value 的形式进行使用。示例代码如下:

Untitled



vuex

vuex 是终极的组件之间的数据共享方案。在企业级的 vue 项目开发中,vuex 可以让组件之间的数据共享变得高效、清晰、且易于维护。


总结:

父子关系

① 父 -> 子 属性绑定
② 子 -> 父 事件绑定
③ 父 <-> 子 组件上的 v-model

兄弟关系

④ EventBus

后代关系

⑤ provide & inject

全局数据共享

⑥ vuex




vue 3.x 中全局配置 axios

  1. 为什么要全局配置 axios

在实际项目开发中,几乎每个组件中都会用到 axios 发起数据请求。此时会遇到如下两个问题:

① 每个组件中都需要导入 axios(代码臃肿)

② 每次发请求都需要填写完整的请求路径(不利于后期的维护)

Untitled

2. 如何全局配置 axios

在 main.js 入口文件中,通过 app.config.globalProperties 全局挂载 axios,示例代码如下:

Untitled

  • Code:

    main.js

    const app = createApp(MyApp)
    axios.defaults.baseURL = 'https://www.escook.cn' //为axios配置请求的根路径
    //将axios挂载为app的全局自定义属性之后,
    //每个组件可以通过this直接访问到全局挂载的自定义属性
    app.config.globalProperties.$http = axios
    

    GetInfo:

    export default {
      name: 'GetInfo',
      methods: {
        async getInfo() {
          const { data: res } = await this.$http.get('/api/get', {
            params: {
              name: 'ls',
              age: 33,
            },
          })
    
          console.log(res)
        },
      },
    }
    

    PostInfo:

    export default {
      name: 'PostInfo',
      methods: {
        async postInfo() {
          const { data: res } = await this.$http.post('/api/post', { name: 'zs', age: 20 })
          console.log(res)
        },
      },
    }
    



总结:

① 能够掌握 watch 侦听器的基本使用

定义最基本的 watch 侦听器

immediate、 deep、监听对象中单个属性的变化

② 能够知道 vue 中常用的生命周期函数

创建阶段、运行阶段、销毁阶段

created、mounted

③ 能够知道如何实现组件之间的数据共享

父子组件、兄弟组件、后代组件

④ 能够知道如何在 vue3 的项目中全局配置 axios

main.js 入口文件中进行配置

app.config.globalProperties.$http = axios

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

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

相关文章

写 bug 速度提升200%!吊爆的 IDEA 使用技巧

背景 Java 开发过程经常需要编写有固定格式的代码&#xff0c;例如说声明一个私有变量&#xff0c;logger或者bean等等。 对于这种小范围的代码生成&#xff0c;我们可以利用 IDEA 提供的 Live Templates功能。 刚开始觉得它只是一个简单的Code Snippet&#xff0c;后来发现…

msf渗透练习-震网三代

说明&#xff1a; 本章内容&#xff0c;仅供学习&#xff0c;不要用于非法用途&#xff08;做个好白帽&#xff09; &#xff08;一&#xff09;震网三代漏洞 “震网三代”官方漏洞编号是CVE-2017-8464&#xff0c;2017年6月13日&#xff0c;微软官方发布编号为CVE-2017-8464的…

Redis Cluster集群运维-03

1、Redis集群方案比较 哨兵模式 在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态&#xff0c;如果master节点异 常&#xff0c;则会做主从切换&#xff0c;将某一台slave作为master&#xff0c;哨兵的配置略微复杂&#xff0c;并且性能和高可…

【CSS】常见的选择器

1.标签选择器 语法 标签 { }作用 标签选择器用于选择某种标签比如 选择p标签&#xff0c;并设置背景颜色 p { background-color:yellow; }例子 选择div标签&#xff0c;并将其字体大小设置为100px&#xff0c;字体设置为"微软雅黑"&#xff0c;文字颜色设置为r…

怎么学习渗透测试?路线是什么

我知道很多人肯定觉得&#xff0c;报班什么的太贵了&#xff0c;但是人家贵有贵的道理 owap讲来讲去&#xff0c;还是那个样&#xff0c;但是有人给你解答问题是两个概念&#xff0c;有时候一个虚拟机都能卡死你很久&#xff0c;我就随便说说&#xff0c;我给你们想的学习路线…

2023网络安全工程师面试题汇总(附答案)

又到了毕业季&#xff0c;大四的漂亮学姐即将下架&#xff0c;大一的小学妹还在来的路上&#xff0c;每逢这时候我心中总是有些小惆怅和小激动…… 作为学长&#xff0c;还是要给这些马上要初出茅庐的学弟学妹们&#xff0c;说说走出校园、走向职场要注意哪些方面。 走出校园后…

基于XC7Z100的PCIe采集卡(GMSL FMC采集卡)

GMSL 图像采集卡 特性 ● PCIe Gen2.0 X8 总线&#xff1b; ● 支持V4L2调用&#xff1b; ● 1路CAN接口&#xff1b; ● 6路/12路 GMSL1/2摄像头输入&#xff0c;最高可达8MP&#xff1b; ● 2路可定义相机同步触发输入/输出&#xff1b; 优势 ● 采用PCIe主卡与FMC子…

服务器是什么?它是用来干什么的?

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、服务器是什么&#xff1f; 二、服务器的作用 1、提高访问速度 2、提高安全性 三、云服务器与物理服务器 1、云服务器 云服务…

[架构之路-210]- 人人都是产品经理 - 互联网产品需求分析思路和方法笔记

目录 前言&#xff1a; 一、产品需求分析思路和方法--产品需求 1、产品需求的内涵 ①什么是产品&#xff1f; ②什么是需求&#xff1f; ③需求的产品的关系 ④案例分析&#xff1a; ⑤理解需求的误区 2、需求的分类及层次、规律、拆解用户需求 ①需求分类 ②需求层…

算法刷题-链表-设计链表

设计链表 707.设计链表思路代码其他语言版本 听说这道题目把链表常见的五个操作都覆盖了&#xff1f; 707.设计链表 力扣题目链接 题意&#xff1a; 在链表类中实现这些功能&#xff1a; get(index)&#xff1a;获取链表中第 index 个节点的值。如果索引无效&#xff0c;则…

MySQL数据库,从入门到精通:第二篇——MySQL关系型数据库与非关系型数据库的比较

MySQL数据库&#xff0c;从入门到精通&#xff1a;第二篇——MySQL关系型数据库与非关系型数据库的比较 1. RDBMS 与 非RDBMS1.1 关系型数据库(RDBMS)1.1.1 实质1.1.2 优势1.2 非关系型数据库(非RDBMS)1.2.1 介绍1.2.2 有哪些非关系型数据库1.2.3 NoSQL的演变1.3 小结 2. 关系型…

SQL开源替代品,诞生了

发明 SQL 的初衷之一显然是为了降低人们实施数据查询计算的难度。SQL 中用了不少类英语的词汇和语法&#xff0c;这是希望非技术人员也能掌握。确实&#xff0c;简单的 SQL 可以当作英语阅读&#xff0c;即使没有程序设计经验的人也能运用。 然而&#xff0c;面对稍稍复杂的查…

Python+QT停车场车牌识别计费管理系统-升级版

程序示例精选 PythonQT停车场车牌识别计费管理系统-升级版 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonQT停车场车牌识别计费管理系统-升级版>>编写代码&#xff0c;代…

RabbitMQ入门案例之Direct模式

前言 RabbitMQ的Direct模式是一种可以根据指定路由key&#xff0c;Exchang将消息发送到具有该路由key下的Queue下进行存储。也就类似于将数据写进指定数据库表中。这个路由Key可以类比为SQL语句中的&#xff1a;where routeKey … 官方文档地址&#xff1a;https://www.rabbi…

DragGAN部署全流程

写在前面 看了DragGAN 官方&#xff0c;并没有找到软件&#xff0c;或者程序&#xff0c;github上也没有程序&#xff0c;如果大佬们能找到&#xff0c;可以评论通知下。不过也有技术大佬已经提前开发出来了&#xff0c;我们抢先体验下。 这里本地部署了 DragGAN。经历了报错&…

【LeetCode】HOT 100(5)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

CTFShow-WEB入门篇--命令执行详细Wp

WEB入门篇--命令执行详细Wp 命令执行&#xff1a;Web29&#xff1a;Web30&#xff1a;Web31&#xff1a;web32&#xff1a;web33&#xff1a;web34&#xff1a;web35&#xff1a;web36&#xff1a;web37&#xff1a;web38&#xff1a; CTFShow 平台&#xff1a;https://ctf.sho…

【Kubernetes资源篇】Service四层代理入门实战详解

文章目录 一、Service四层代理概念、原理1、Service四层代理概念2、Service工作原理3、Service原理解读4、Service四种类型 二、Service四层代理三种类型案例1、创建ClusterIP类型Service2、创建NodePort类型Service3、创建ExternalName类型Service 三、拓展1、Service域名解析…

Nvidia AGX Orin MAX9296 GMSL 载板设计要点

因为项目的需求&#xff0c;我们设计了Nvidia AGX Orin MAX9296 GMSL 载板(绿板&#xff09;&#xff0c;项目完成&#xff0c;总结以下。需要参考原理图的&#xff0c;可以微我&#xff0c;索取。共同交流。 Jetson AGX Orin建立在NVIDIA Ampere架构之上&#xff0c;全新Jets…

AUTOSAR-BSW EEPROM模块解读

参考文件 AUTOSAR_SWS_EEPROMDriver&#xff08;4.3.0&#xff09; AUTOSAR_SWS_BSWGeneral&#xff08;4.3.0&#xff09; EEPROM Module 文件结构 如上图所示 EEPROM Module应该主要包含Eep.c,Eep.h,Eep_Cfg.c,Eep_MemMmap.h,Eep_Lcfg.c和Eep_PBcfg.c文件&#xff0c;如果使…