【VUE2】第三期——样式冲突、组件通信、异步更新、自定义指令、插槽

目录

1 scoped解决样式冲突

2 data写法

3 组件通信

3.1 父子关系

3.1.1 父向子传值 props

3.1.2 子向父传值 $emit

3.2 非父子关系

3.2.1 event bus 事件总线

3.2.2 跨层级共享数据 provide&inject

4 props

4.1 介绍 

4.2 props校验完整写法

5 v-model原理 

5.1 应用:表单类组件封装 

5.1.1 不用v-model的写法 

5.1.2 用v-model的写法

5.1.3 sync 修饰符

6 ref 与 $refs

6.1 获取dom元素

6.2 获取组件

7 Vue异步更新与$nextTick

8 自定义指令

8.1 基本用法

8.2 传值

9 插槽

9.1 基本用法

9.2 默认值

9.3 具名插槽

9.4 作用域插槽


1 scoped解决样式冲突

问题:

写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。

解决方法:

可以给组件加上scoped 属性,可以让样式只作用于当前组件

如: 

<style scoped></style>

scoped原理:

自动执行: 

  1. 当前组件内标签都被添加data-v-hash值 的属性

  2. css选择器都被添加 [data-v-hash值] 的属性选择器

最终效果:

     必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到


2 data写法

一个组件的 data 选项必须是一个函数

目的是为了:保证每个组件实例,维护独立的一份数据对象

每次创建新的组件实例,都会新执行一次data 函数,得到一个新对象

如:

<script>
export default {data: function () {return {count: 100,}},
}
</script>

3 组件通信

总结:

父子通信:

1.props & $emit 父向子传值,子通知父改值        3.1

        1.1 v-model简写(不可自定义属性值)        5.1.2

        1.2 sync简写(可自定义属性值)                 5.1.3

2.ref & $refs 父主动拿子的方法与属性                  6.2

非父子通信:

1.event bus 事件总线(任意组件)                       3.2.1

2.跨层级通信(同一家族组件)                             3.2.2


3.1 父子关系

父组件通过 props 将数据传递给子组件
子组件利用 $emit 通知父组件修改更新


3.1.1 父向子传值 props

步骤:

  1. 给子组件以添加属性的方式传值
  2. 子组件内部通过props接收
  3. 模板中直接使用 props接收的值

App.vue(父组件):

<template><!-- 主体区域 --><section id="app"><!--第一步:给子组件挂上list属性,并将值赋值给list属性--><TodoMain :list="list" @del="del"></TodoMain></section>
</template><script>
//...data() {return {list: [{id: 1, content: "吃饭"},{id: 2, content: "睡觉"},{id: 3, content: "打豆豆"}],}},}
</script>

TodoMain.vue(子组件):

注意:此处props为简略用法,完整写法见下文4.2 props校验完整写法

<template><!-- 列表区域 --><section class="main"><ul class="todo-list"><!--第三步:直接使用list变量--><li class="todo" v-for="(item, index) in list" :key="item.id"><!--...--></li></ul></section>
</template><script>
export default {//第二步:props接收数据(简略写法)props: ["list"],
}
</script>

3.1.2 子向父传值 $emit

(此处为子通知父,父为被动,下文中的$refs为父主动拿子) 

步骤

  1. $emit触发事件,给父组件发送消息通知

  2. 父组件监听$emit触发的事件

  3. 提供处理函数,在函数的性参中获取传过来的参数

 TodoMain.vue(子组件):

<template><!-- 列表区域 --><section class="main"><button @click="del(item.id)" ></button></section>
</template><script>
export default {
//...methods: {dela(id) {//第一步:触发delb事件this.$emit("delb", id1)}}
}
</script>

App.vue(父组件):

<template><!-- 主体区域 --><section id="app"><!--监听子组件触发的delb事件,监听到后执行delc函数,并将子组件传过来的id1值用id2接收--><TodoMain @delb="delc"></TodoMain></section>
</template><script>
//...
export default {//...methods: {delc(id2) {console.log(id2)},},}
</script>

此为最基本写法,可以进一步化简:使用v-model或sync修饰符,具体见 5 v-model原理 


3.2 非父子关系

3.2.1 event bus 事件总线

(任意两个组件之间的通信) 

 非父子组件之间,进行简易消息传递。(复杂场景→ Vuex)

Event Bus(事件总线)可以有多个发送方与多个接收方

 第一步:创建事件总线实例

首先创建一个新的 Vue 实例作为事件总线,通常命名为 eventBus

这个实例就像一个中央枢纽,负责管理所有的事件

因为 Vue 实例本身具有 $on(监听事件)、$emit(触发事件)和 $off(移除事件监听器)方法,所以可以利用这些方法来实现事件的发布和订阅

// EventBus.js
import Vue from 'vue'const Bus = new Vue()export default Bus

在使用Bus事件总线时,需要先导入Bus这个实例对象

import Bus from '../utils/EventBus'

第二步:订阅事件(监听事件):

在需要接收数据或响应某个事件的组件中,使用 Bus.$on 方法来监听特定的事件

$on 方法接受两个参数:

第一个参数是事件名称(字符串)

第二个参数是当事件触发时要执行的回调函数

// 组件 A 中监听事件
Bus.$on('sendMsg', (msg) => {this.msg = msg
})

第三步:发布事件(触发事件):

在需要发送数据或通知其他组件的组件中,使用 eventBus.$emit 方法来触发特定的事件

$emit 方法的参数:

第一个参数是事件名称(必须与要监听的事件名称一致)

后面参数不限制个数及数据类型,这些参数会传递给监听该事件的回调函数

Bus.$emit('sendMsg', '这是一个消息')

第四步:取消事件监听(移除事件监听器)

(选学)

在组件销毁或不再需要监听事件时,可以使用 Bus.$off 方法来移除事件监听器,防止内存泄漏

Bus.$off([event, callback])

参数说明:

event(可选):要移除的事件名,可以是一个字符串或一个包含多个事件名的数组。如果省略该参数,则会移除所有事件监听器

callback(可选):要移除的特定回调函数。如果省略该参数,则会移除该事件名下的所有回调函数


3.2.2 跨层级共享数据 provide&inject

(父子、爷孙等之间的通信) 

父组件 provide提供数据:

export default {provide () {return {// 普通类型【非响应式】color: this.color, // 复杂类型【响应式】userInfo: this.userInfo, }}
}

子/孙组件 inject获取数据:

export default {inject: ['color','userInfo'],created () {console.log(this.color, this.userInfo)}
}

注意:

provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式(推荐提供复杂类型数据)

子/孙组件通过inject获取的数据,不能在自身组件内修改

inject中的属性值值也可以写成对象形式,可以设置的值:

from 用于指定从祖先组件注入的属性名。如果不使用 from,则默认使用当前定义的属性名作为注入的属性名

default 用于指定注入属性的默认值,当祖先组件没有提供对应的属性时,会使用这个默认值

      inject: {// 使用 from 指定注入属性的来源名称myUserInfo: {from: 'userInfo',// 设置默认值default: {name: '默认姓名',age: 0}},// 没有使用 from,默认使用当前属性名注入someValue: {default: '默认值'}},

4 props

4.1 介绍 

定义:

组件上 注册的一些 自定义属性

作用:

向子组件传递数据

特点:

  1. 可以 传递 任意数量 的prop

  2. 可以 传递 任意类型 的prop


4.2 props校验完整写法

语法:

props: {校验的属性名: {type: 类型,  // Number String Boolean ...required: true, // 是否必填default: 默认值, // 默认值validator (value) {// 自定义校验逻辑return 是否通过校验}}
},

示例:

<script>
export default {// 完整写法(类型、默认值、非空、自定义校验)props: {w: {type: Number,//required: true,default: 0,validator(val) {// console.log(val)if (val >= 100 || val <= 0) {console.error('传入的范围必须是0-100之间')return false} else {return true}},},},
}
</script>

注意:

1.default和required一般不同时写(因为当时必填项时,肯定是有值的)

2.default后面如果是简单类型的值,可以直接写。如果是复杂类型的值,则需要以函数的形式return一个默认


5 v-model原理 

v-model本质上是一个语法糖。例如应用在输入框上,就是value属性 和 input事件 的合写

<template><div id="app" ><input v-model="msg" type="text"><input :value="msg" @input="msg = $event.target.value" type="text"></div>
</template>

注意:

1.$event 用于在模板中,获取事件的形参

2.不同的表单元素, v-model在底层的处理机制是不一样的,比如给checkbox使用v-model

底层处理的是 checked属性和change事件 


5.1 应用:表单类组件封装 

5.1.1 不用v-model的写法 

(此处就是父子组件通讯的方法 3.1) 

App.vue

<template><div class="app"><BaseSelect :selectId="selectId" @changeCity="selectId = $event"></BaseSelect></div>
</template><script>
import BaseSelect from './components/BaseSelect.vue'
export default {data() {return {selectId: '102',}},components: {BaseSelect,},
}
</script><style>
</style>

BaseSelect.vue

<template><div><select :value="selectId" @change="selectCity"><option value="101">北京</option><option value="102">上海</option><option value="103">武汉</option><option value="104">广州</option><option value="105">深圳</option></select></div>
</template><script>
export default {props: {selectId: String,},methods: {selectCity(e) {this.$emit('changeCity', e.target.value)},},
}
</script><style>
</style>

5.1.2 用v-model的写法

注意:使用前需要先基于5.1.1的代码稍作修改 

 App.vue

<template><div class="app"><!--1.给子组件挂在的属性名为:value 2.监听input事件--><BaseSelect :value="selectId" @input="selectId = $event"></BaseSelect></div>
</template><script>
import BaseSelect from './components/BaseSelect.vue'
export default {data() {return {selectId: '102',}},components: {BaseSelect,},
}
</script><style>
</style>

BaseSelect.vue

<template><div><!--绑定value值--><select :value="value" @change="selectCity"><option value="101">北京</option><option value="102">上海</option><option value="103">武汉</option><option value="104">广州</option><option value="105">深圳</option></select></div>
</template><script>
export default {props: {//接收value值value: String,},methods: {selectCity(e) {//更改所触发的事件为inputthis.$emit('input', e.target.value)},},
}
</script><style>
</style>

完成上述修改后,可以对父组件(App.vue)中的代码进行简化:

原:

<BaseSelect :value="selectId" @input="selectId = $event"></BaseSelect>

改:

<BaseSelect v-model="selectId" ></BaseSelect>

此种方法缺点也显而易见,固定死了属性名必须传value ,

以下方法可以自定义属性名


5.1.3 sync 修饰符

.sync修饰符 就是 :属性名@update:属性名 合写

<!--父组件-->
<!-- .sync写法 -->
<BaseDialog :visible.sync="isShow" />
--------------------------------------
<!-- 完整写法 -->
<BaseDialog :visible="isShow" @update:visible="isShow = $event" />

对比一下上面的v-model

:value=""               变成了        :属性名=""   

@事件类型=""       变成了        @update:属性名=""

因此在组件中可以使用自定义属性名了,

但是在子组件触发的事件变成了  update:属性名

//子组件
props: {visible: Boolean
},this.$emit('update:visible', false)

除此之外其他与上述一样,本质上还是最基本的props、$emit通信


6 ref 与 $refs

利用ref 和 $refs 可以用于 获取 dom 元素 或 组件实例  

查找范围 → 当前组件内(更精确稳定),

而之前用document.querySelect('.box') 获取的是整个页面中的盒子

6.1 获取dom元素

1.给要获取的盒子添加ref属性

<div ref="chartRef">我是渲染图表的容器</div>

2.恰当时机,通过 $refs获取 this.$refs.xxx 获取

console.log(this.$refs.chartRef)

恰当时机:指的是页面渲染完(mouted时期及以后) 


6.2 获取组件

1.给目标组件添加ref属性

<BaseForm ref="baseForm"></BaseForm>

2.恰当时机,通过 this.$refs.xxx 获取目标组件

this.$refs.baseForm.组件方法()this.$refs.baseForm.属性名

7 Vue异步更新与$nextTick

需求:

编辑标题, 编辑框自动聚焦

  1. 点击编辑,显示编辑框

  2. 让编辑框,立刻获取焦点

代码:

<template><div class="app"><div v-if="isShowEdit"><input type="text" v-model="editValue" ref="inp" /><button>确认</button></div><div v-else><span>{{ title }}</span><button @click="editFn">编辑</button></div></div>
</template><script>
export default {data() {return {title: '大标题',isShowEdit: false,editValue: '',}},methods: {editFn() {// 显示输入框this.isShowEdit = true  // 获取焦点this.$refs.inp.focus() }  },
}
</script> 

问题:

"显示之后",立刻获取焦点是不能成功的!

原因:Vue 是异步更新DOM (提升性能)

解决方案:

$nextTick:等 DOM更新后,才会触发执行此方法里的函数体

语法: this.$nextTick(函数体)

this.$nextTick(() => {this.$refs.inp.focus()
})

注意:$nextTick 内的函数体 一定是箭头函数,这样才能让函数内部的this指向Vue实例


8 自定义指令

8.1 基本用法

全局注册:

//在main.js中
Vue.directive('指令名', {inserted(el) {// 可以对 el 标签,扩展额外功能el.focus()}
})

局部注册:

//在Vue组件的配置项中
directives: {"指令名": {inserted () {// 可以对 el 标签,扩展额外功能el.focus()}}
}

参数介绍:

inserted:被绑定元素插入父节点时调用的钩子函数

el:使用指令的那个DOM元素

注意:

在使用指令的时候,一定要先注册再使用,否则会报错使用指令语法: v-指令名

如:<input type="text" v-focus/>

注册指令时不用v-前缀,但使用时一定要加v-前缀


8.2 传值

1.在绑定指令时,可以通过“等号”的形式为指令 绑定 具体的参数值

<div v-color="color">我是内容</div>

2.通过 binding.value 可以拿到指令值,指令值修改会 触发 update 函数

directives: {color: {inserted (el, binding) {el.style.color = binding.value},update (el, binding) {el.style.color = binding.value}}
}

9 插槽

9.1 基本用法

  1. 组件内需要定制的结构部分,改用<slot></slot>占位

  2. 使用组件时, 如<MyDialog></MyDialog>标签,内部传入结构替换slot

  3. 给插槽传入内容时,可以传入纯文本、html标签、组件


9.2 默认值

在 <slot> 标签内,放置内容, 作为默认显示内容

效果: 

  • 外部使用组件时,不传东西,则slot会显示后备内容

  • 外部使用组件时,传东西了,则slot整体会被换掉


9.3 具名插槽

多个slot使用name属性区分名字

template配合v-slot:名字来分发对应标签

v-slot写起来太长,vue给我们提供一个简单写法 v-slot —> #


9.4 作用域插槽

作用域插槽不是插槽的一种,插槽只有两种:默认插槽与具名插槽 

定义slot 插槽的同时, 是可以传值的。给 插槽 上可以 绑定数据,将来 使用组件时可以用

使用步骤:

1.给 slot 标签, 以 添加属性的方式传值

<slot :id="item.id" msg="测试文本"></slot>

注意:所有添加的属性, 都会被收集到一个对象中

2.在template中, 通过 #插槽名= "obj" 接收,默认插槽名为 default

<MyTable :list="list"><template #default="obj"><button @click="del(obj.id)">删除</button></template>
</MyTable>

注意:传给组件数据,只能写在组价标签身上,不能写在template上,

但是接收组件的数据(#default)可以写在组件标签上(仅限默认插槽)

插槽与组件的选择:

具有逻辑操作的选组件,修改组件部分标签结构选插槽

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯刷题周计划(第二周)

目录 前言题目一题目代码题解分析 题目二题目代码题解分析 题目三题目代码题解分析 题目四题目代码题解分析 题目五题目代码题解分析 题目六题目代码题解分析 题目七题目代码题解分析 题目八题目题解分析 题目九题目代码题解分析 题目十题目代码题解分析 题目十一题目代码题解分…

Redis渐进式遍历数据库

目录 渐进式遍历 数据库 渐进式遍历 keys*可以一次性的把整个redis中所有key都获取到&#xff0c;这个操作是非常危险的&#xff0c;因为可能一下获取到太多的key&#xff0c;阻塞redis服务器。要想很好的获取到所有的key&#xff0c;又不想出现卡死的情况&#xff0c;就可以…

一周学会Flask3 Python Web开发-使用SQLAlchemy动态创建数据库表

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 前面我们定义了模型&#xff0c;我们可以通过sqlalchemy对象提供的create_all()方法来映射和动态创建数据库表。 因为我们用到…

android studio2024最新详解(完全小白)安装-运行第一个程序

前面我用2023最新版本的&#xff0c;死活就卡在引入依赖那里卡了两天&#xff0c;俺的崩溃谁知啊&#xff01;&#xff01; 后面我就换了个思维&#xff0c;看着网上大多的教程都是基于2022或者2020的&#xff0c;我就找了个看起来非常详细的视频&#xff0c;里面的是2020的&am…

laravel中 添加公共/通用 方法/函数

一&#xff0c;现在app 下面创建Common目录&#xff0c;然后在创建Common.php 文件 二&#xff0c;修改composer.json文件 添加这个到autoload 中 "files": ["app/Common/Common.php"]"autoload": {"psr-4": {"App\\": &quo…

c语言笔记 函数参数的等价(上)

这三种写法在 C 语言中是等价的&#xff0c;因为它们都用于声明一个指向二维数组的指针&#xff0c;或者用于声明一个二维数组作为函数参数。它们的等价性源于 C 语言中数组和指针之间的密切关系。让我们逐一分析这三种写法&#xff1a; 在C语言中&#xff0c;当数组作为函数参…

ubuntu局域网部署stable-diffusion-webui记录

需要局域网访问&#xff0c;如下设置&#xff1a; 过程记录查看源码&#xff1a; 查看源码&#xff0c;原来修改参数&#xff1a;--server-name 故启动&#xff1a; ./webui.sh --server-name0.0.0.0 安装下载记录&#xff1a; 快速下载可设置&#xff1a; export HF_ENDPOI…

CTFHub-FastCGI协议/Redis协议

将木马进行base64编码 <?php eval($_GET[cmd]);?> 打开kali虚拟机&#xff0c;使用虚拟机中Gopherus-master工具 Gopherus-master工具安装 git clone https://github.com/tarunkant/Gopherus.git 进入工具目录 cd Gopherus 使用工具 python2 "位置" --expl…

MetaGPT发布的MGX与Devin深度对比

家人们&#xff0c;搞编程的都知道&#xff0c;工具选对了&#xff0c;效率能翻倍&#xff01;今天必须给大伙唠唠MetaGPT发布的MGX编程助手和Devin编程助手 。 先看MGX&#xff0c;简直是编程界的王炸&#xff01;它就像一个超神的虚拟开发团队&#xff0c;一堆智能助手分工明…

Git学习笔记(二)

Git学习笔记&#xff08;二&#xff09; 下载VSCode创建本地仓库推送远程仓库界面功能 使用 VSCode 进行Git仓库的项目管理 这篇文章是我学完使用 命令行终端 管理Git仓库额外学习的 文章主要用于巩固和方便后续复习 下载VSCode 可以看我这篇文章下载VSCode 创建本地仓库 …

本地fake server,

C# 制作的系统级tcp 重定向&#xff0c;整个系统只要有访问指定url&#xff0c;返回自定义内容到访问端。不局限在浏览器单一方面。 再者请理解这个图的含金量&#xff0c;服务器down机都可以模拟。 用途那就太多了&#xff0c;当然很多用途都不正当。嘿嘿 如果你很想要源代…

C语言_数据结构总结4:不带头结点的单链表

纯C语言代码&#xff0c;不涉及C 0. 结点结构 typedef int ElemType; typedef struct LNode { ElemType data; //数据域 struct LNode* next; //指针域 }LNode, * LinkList; 1. 初始化 不带头结点的初始化&#xff0c;即只需将头指针初始化为NULL即可 void Init…

是德科技十周年:以创新丈量未来,用科技赋能世界

是德科技成立十周年&#xff0c;以全球测试测量领域领军者的姿态&#xff0c;书写了一部突破与创新的发展史诗。作为从惠普、安捷伦深厚技术积淀中孕育而生的行业标杆&#xff0c;十年来是德科技始终站在科技浪潮之巅&#xff0c;构建起覆盖5G通信、人工智能、汽车电子、量子计…

循环神经网络(RNN):时序建模的核心引擎与演进之路

在人工智能处理序列数据的战场上&#xff0c;循环神经网络&#xff08;RNN&#xff09;如同一个能够理解时间的智者。从 2015 年谷歌神经机器翻译系统颠覆传统方法&#xff0c;到 2023 年 ChatGPT 实现对话连续性&#xff0c;这些突破都植根于 RNN 对时序建模的深刻理解。本文将…

【单片机通信技术】STM32 HAL库 SPI主从机通过串口发送数据

一、说明 使用STM32F103C8T6最小系统板&#xff0c;让板载SPI1与SPI2通信&#xff0c;通过串口收发数据。本文章说明了在配置与编写时遇到的一些问题&#xff0c;以及详细说明如何使用cubeMAX进行代码编写。 二、CubeMAX配置 1.时钟配置选择外部高速时钟 2.系统模式与时钟配…

批量删除 Excel 中所有图片、某张指定图片以及二维码图片

在 Excel 文档中&#xff0c;我们可以在工作表中插入大量的图片&#xff0c;我们也可以删除工作表中的图片。少量的图片我们可以直接删除&#xff0c;但是我们我们有大量的 Excel 文档&#xff0c;那如何快速删除所有 Excel 表格中的所有图片呢&#xff1f;我们除了常规删除 Ex…

【算法】大数据查重

大数据查重 哈希表 找出第一个出现重复的数字 || 找所有重复出现的数字 #include <iostream> #include <vector> #include <unordered_map> #include <unordered_set> #include <stdlib.h> #include <time.h> #include <string> …

网格图学习(附题单与做题思路)

文章目录 一、DFS 经典题型695. 岛屿的最大面积 二、BFS 经典题型994. 腐烂的橘子**算法选择对照表** 一、DFS 经典题型 岛屿的最大面积 LeetCode 695描述&#xff1a;求网格中最大的陆地连通区域面积解题&#xff1a;DFS 遍历所有相邻陆地&#xff0c;标记已访问关键点&#…

安装树莓派3B+环境(嵌入式开发)

一、环境配置 1、下载树莓派镜像工具 点击进入下载连接 进入网站&#xff0c;点击下载即可。 2、配置wifi及ssh 将SD卡插入读卡器&#xff0c;再接入电脑&#xff0c;随后打开Raspberry Pi Imager下载工具&#xff0c; 选择Raspberry Pi 3 选择64位的操作系统 选择SD卡 选择…

Leetcode 刷题记录 06 —— 矩阵

本系列为笔者的 Leetcode 刷题记录&#xff0c;顺序为 Hot 100 题官方顺序&#xff0c;根据标签命名&#xff0c;记录笔者总结的做题思路&#xff0c;附部分代码解释和疑问解答。 目录 01 矩阵置零 方法一&#xff1a;标记数组 方法二&#xff1a;两个标记变量 02 螺旋矩阵…