【VUE2】第三期——样式冲突、组件通信、异步更新

news2025/3/9 13:16:16

目录

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


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) {
      //更改所触发的事件为input
      this.$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实例

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

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

相关文章

深度学习分类回归(衣帽数据集)

一、步骤 1 加载数据集fashion_minst 2 搭建class NeuralNetwork模型 3 设置损失函数&#xff0c;优化器 4 编写评估函数 5 编写训练函数 6 开始训练 7 绘制损失&#xff0c;准确率曲线 二、代码 导包&#xff0c;打印版本号&#xff1a; import matplotlib as mpl im…

在Linux中开发OpenGL——检查开发环境对OpenGL ES的支持

由于移动端GPU规模有限&#xff0c;厂商并没有实现完整的OpenGL特性&#xff0c;而是实现了它的子集——OpenGL ES。因此如果需要开发的程序要支持移动端平台&#xff0c;最好使用OpenGL ES开发。 1、 下载支持库、OpenGL ES Demo 1.1、下载PowerVRSDK支持库作为准备&#xff…

基于Spring Boot的学院商铺管理系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

os-copilot安装和使用体验测评

简介&#xff1a; OS Copilot是阿里云基于大模型构建的Linux系统智能助手&#xff0c;支持自然语言问答、命令执行和系统运维调优。本文介绍其产品优势、功能及使用方法&#xff0c;并分享个人开发者在云服务器资源管理中的实际应用体验。通过-t/-f/管道功能&#xff0c;OS Cop…

Geo3D建筑材质切换+屋顶纹理

一、简介 基于Threejs开发封装建筑渲染管线&#xff0c;利用简单二维建筑矢量面轮廓程序化生成3D建筑&#xff0c;支持材质一键切换&#xff0c;支持多样化建筑墙面材质和屋顶材质&#xff0c;支持建筑透明&#xff0c;支持地形高程适配&#xff0c;支持按空间范围裁剪挖洞等。…

【GPT入门】第9课 思维树概念与原理

【GPT入门】第9课 思维树概念与原理 1.思维树概念与原理2. 算24游戏的方法 1.思维树概念与原理 思维树&#xff08;Tree of Thought&#xff0c;ToT &#xff09;是一种大模型推理框架&#xff0c;旨在解决更加复杂的多步骤推理任务&#xff0c;让大模型能够探索多种可能的解决…

栈概念和结构

文章目录 1. 栈的概念2. 栈的分类3. 栈的实现&#xff08;数组栈&#xff09;3.1 接口设计&#xff08;Stack.h&#xff09;3.2 接口实现&#xff08;Stack.c&#xff09;1&#xff09;初始化销毁2&#xff09;栈顶插入删除3&#xff09;栈顶元素、空栈、大小 3.3 完整代码Stac…

Spring (八)AOP-切面编程的使用

目录 实现步骤&#xff1a; 1 导入AOP依赖 2 编写切面Aspect 3 编写通知方法 4 指定切入点表达式 5 测试AOP动态织入 图示&#xff1a; 实现步骤&#xff1a; 1 导入AOP依赖 <!-- Spring Boot AOP依赖 --><dependency><groupId>org.springframework.b…

VS Code连接服务器教程

VS Code是什么 VS Code&#xff08;全称 Visual Studio Code&#xff09;是一款由微软推出的免费、开源、跨平台的代码编辑神器。VS Code 支持 所有主流操作系统&#xff0c;拥有强大的功能和灵活的扩展性。 官网&#xff1a;https://code.visualstudio.com/插件市场&#xff1…

Windsuf 连接失败问题:[unavailable] unavailable: dial tcp...

问题描述 3月6日&#xff0c;在使用Windsuf 时&#xff0c;遇到以下网络连接错误&#xff1a; [unavailable] unavailable: dial tcp 35.223.238.178:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of…

docker中kibana启动后,通过浏览器访问,出现server is not ready yet

问题&#xff1a;当我在浏览器访问kibana时&#xff0c;浏览器给我报了server is not ready yet. 在网上试了很多方法&#xff0c;都未能解决&#xff0c;下面是我的方法&#xff1a; 查看kibana日志&#xff1a; docker logs -f kibana从控制台打印的日志可以发现&#xff…

更多文章请查看

更多文章知识请移步至下面链接&#xff0c;期待你的关注 如需查看新文章&#xff0c;请前往&#xff1a; 博主知识库https://www.yuque.com/xinzaigeek

(十 九)趣学设计模式 之 中介者模式!

目录 一、 啥是中介者模式&#xff1f;二、 为什么要用中介者模式&#xff1f;三、 中介者模式的实现方式四、 中介者模式的优缺点五、 中介者模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;…

博弈论算法

一、减法游戏 初始有一个数 n。 两个玩家轮流操作&#xff0c;每次可以减去 1 到 9 之间的任意整数。 将数减到 0 的玩家获胜。 可以发现规律&#xff1a; 减法游戏只需要判断当前数取模是否为0&#xff0c;即可快速判断胜负。 例题&#xff1a; Leetcode 292. Nim 游戏 …

【网络】HTTP协议、HTTPS协议

HTTP与HTTPS HTTP协议概述 HTTP&#xff08;超文本传输协议&#xff09;&#xff1a;工作在OSI顶层应用层&#xff0c;用于客户端&#xff08;浏览器&#xff09;与服务器之间的通信,B/S模式 无状态&#xff1a;每次请求独立&#xff0c;服务器不保存客户端状态&#xff08;通…

GitCode 助力 vue3-element-admin:开启中后台管理前端开发新征程

源码仓库&#xff1a; https://gitcode.com/youlai/vue3-element-admin 后端仓库&#xff1a; https://gitcode.com/youlai/youlai-boot 开源助力&#xff0c;开启中后台快速开发之旅 vue3-element-admin 是一款精心打造的免费开源中后台管理前端模板&#xff0c;它紧密贴合…

Qt常用控件之表格QTableWidget

表格QTableWidget QTableWidget 是一个表格控件&#xff0c;行和列交汇形成的每个单元格&#xff0c;是一个 QTableWidgetItem 对象。 1. QTableWidget属性 QTableWidget 的属性只有两个&#xff1a; 属性说明rowCount当前行的个数。columnCount当前列的个数。 2. QTableW…

FFmpeg入门:最简单的音视频播放器

FFmpeg入门&#xff1a;最简单的音视频播放器 前两章&#xff0c;我们已经了解了分别如何构建一个简单和音频播放器和视频播放器。 FFmpeg入门&#xff1a;最简单的音频播放器 FFmpeg入门&#xff1a;最简单的视频播放器 本章我们将结合上述两章的知识&#xff0c;看看如何融…

【Python爬虫】爬取公共交通路网数据

程序来自于Github&#xff0c;以下这篇博客作为完整的学习记录&#xff0c;也callback上一篇爬取公共交通站点的博文。 Bardbo/get_bus_lines_and_stations_data_from_gaode: 这个项目是基于高德开放平台和公交网获取公交线路及站点数据&#xff0c;并生成shp文件&#xff0c;…

009---基于Verilog HDL的单比特信号边沿检测

文章目录 摘要一、边沿检测二、时序逻辑实现2.1 rtl2.2 tb 三、组合逻辑实现3.1 rtl3.2 tb 摘要 文章为学习记录。采用时序逻辑和组合逻辑实现边沿检测的核心逻辑。组合逻辑实现的上升沿和下降沿的脉冲比时序逻辑实现的上升沿和下降沿的脉冲提前一拍。 一、边沿检测 边沿检测…