web前端面试高频考点——Vue组件间的通信及高级特性(多种组件间的通信、自定义v-model、nextTick、插槽)

简介: web前端面试高频考点——Vue组件间的通信及高级特性(多种组件间的通信、自定义v-model、nextTick、插槽)

一、Vue组件间通信

1、props 和 $emit

  • props 常用于 父给子 传递数据
  • this.$emit 常用于 子给父 传递数据
  • event.$emit 常用于 兄弟组件间 传递数据

示例:两个子组件(输入框组件&列表组件)动态添加和删除

父组件(index.vue)

  • 父组件在子组件标签上绑定 @add 和 @delete 自定义事件
<template>
  <div>
    <Input @add="addHandler" />
    <List :list="list" @delete="deleteHandler" />
  </div>
</template>
<script>
import Input from "./Input";
import List from "./List";
export default {
  components: {
    Input,
    List,
  },
  data() {
    return {
      list: [
        {
          id: "id-1",
          title: "标题1",
        },
        {
          id: "id-2",
          title: "标题2",
        },
      ],
    };
  },
  methods: {
    // 添加项目
    addHandler(title) {
      this.list.push({
        id: `id-${Date.now()}`,
        title,
      });
    },
    // 删除项目
    deleteHandler(id) {
      this.list = this.list.filter((item) => item.id !== id);
    },
  },
  // 创建
  created() {
    console.log("index created");
  },
  // 挂载
  mounted() {
    console.log("index mounted");
  },
  // 更新前
  beforeUpdate() {
    console.log("index before update");
  },
  // 更新
  updated() {
    console.log("index updated");
  },
};
</script>

子组件(input.vue)

  • 按钮绑定 addTitle 事件
  • 使用 this.$emit('add', this.title) 调用父组件的事件
  • 使用 event.$emit("onAddTitle", this.title) 调用自定义事件(兄弟组件定义的事件)
<template>
  <div>
    <input type="text" v-model="title" />
    <button @click="addTitle">add</button>
  </div>
</template>
<script>
import event from "./event";
export default {
  data() {
    return {
      title: "",
    };
  },
  methods: {
    addTitle() {
      // 调用父组件的事件
      if (this.title.trim() !== "") {
        this.$emit("add", this.title);
        // 调用自定义事件
        event.$emit("onAddTitle", this.title);
        this.title = "";
      } else {
        alert('输入内容不能为空')
      }
    },
  },
};
</script>

子组件(List.vue)

  • 使用 props,接收父组件传来的的 list,并做了类型限制和默认值
  • 按钮绑定 deleteItem 函数,里面使用 this.$emit("delete", id) 调用父组件的事件,根据 id,进行删除
  • event.$on("onAddTitle", this.addTitleHandler) 挂载(mounted)的时候绑定自定义事件
  • event.$off("onAddTitle", this.addTitleHandler) 销毁前(beforeDestroy)的时候销毁自定义事件
  • 注意:绑定和销毁自定义事件时,第二个参数是传入的一个普通函数,不要写箭头函数(this 指向会发生改变)
<template>
  <div>
    <ul>
      <li v-for="item in list" :key="item.id">
        {{ item.title }}
        <button @click="deleteItem(item.id)">删除</button>
      </li>
    </ul>
  </div>
</template>
<script>
import event from "./event";
export default {
  // props: ['list']
  props: {
    // prop 类型和默认值
    list: {
      type: Array,
      default() {
        return []
      }
    },
  },
  methods: {
    deleteItem(id) {
      this.$emit("delete", id);
    },
    addTitleHandler(title) {
      console.log("on add title", title);
    },
  },
  created() {
    console.log("list created");
  },
  mounted() {
    console.log("list mounted");
    // 绑定自定义事件
    event.$on("onAddTitle", this.addTitleHandler);
  },
  beforeUpdate() {
    console.log("list before update");
  },
  updated() {
    console.log("list updated");
  },
  beforeDestroy() {
    // 及时销毁,否则可能造成内存泄露
    event.$off("onAddTitle", this.addTitleHandler);
  },
};
</script>

event.js 文件

  • 创建一个 vue 实例,可以使用 $emit,$off,$on 等方法
import Vue from 'vue'
export default new Vue()

分割线 ------------------------------------------------------------------------------

d3e8e5201f294fa79538bfe037955878.png分割线 ------------------------------------------------------------------------------

d2791cb7092149bab41d1cfadb803997.png

分割线 ------------------------------------------------------------------------------


9e4483754c4044f4a52e09d5a27c0f80.png

2、生命周期

生命周期详解【参考链接】

  • beforeCreate:vue 实例被创建出来,el 和 data 都还没有初始化,不能访问 data 和 method,一般在这个阶段不进行操作。一般在这个阶段不进行操作

created:vue 实例中的 data、method 已被初始化,属性也被绑定。但是此时还是虚拟dom,真实 dom 还没生成,$el 还不可用。一般在此对数据进行初始化

beforeMount:模板已经编译完成,但还没有被渲染至页面中(即为虚拟dom加载为真实dom)

mounted:模板已经被渲染成真实 DOM,用户已经可以看到渲染完成的页面。执行完 mounted 就表示,实例已经被完全创建好了

beforeUpdate:重新渲染之前触发,然后 vue 的虚拟 dom 机制会重新构建虚拟 dom 与上一次的虚拟 dom 树利用 diff 算法进行对比之后重新渲染。只有 view 上面的数据变化才会触发 beforeUpdate 和 updated,仅属于 data 中的数据改变是并不能触发。

updated:数据已经更改完成,dom 也重新 render 完成。

beforeDestroy:销毁前执行($destroy方法被调用的时候就会执行),一般在这里:清除计时器、清除自定义绑定的事件等等…

destroyed:销毁后 (Dom 元素存在,只是不再受 vue 控制),卸载watcher,事件监听,子组件。

参考 props 和 $emit 的示例的结果,总结父子组件生命周期如下:

01.父组件 before create
02.父组件 created
03.父组件 before mount
04.子组件 before create
05.子组件 created
06.子组件 before mount
07.子组件 mounted
08.父组件 mounted
09.父组件 before update
10.子组件 before update
11.子组件 updated
12.父组件 updated
13.父组件 before destroy
14.子组件 before destroy
15.子组件 destroyed
16.父组件 destroyed

二、Vue的高级特性

自定义 v-model,$nextTick,slot,动态、异步组件,keep-alive,mixin

1、自定义 v-model

示例:自定义实现 v-model

CustomVModel.vue 子组件

  • input 使用了 :value 而不是 v-model
  • change1 属性对应起来
  • text1 属性对应起来
  • prop 也就是调用该组件的父组件中使用 v-model 指令绑定的属性
  • event 对应的是修改 prop 指定属性的值的函数
<template>
  <input
    type="text"
    :text="text1"
    @input="$emit('change1', $event.target.value)"
  />
</template>
<script>
export default {
  name: "CustomVModel",
  model: {
    prop: "text1", // 对应 props text1
    event: "change1",
  },
  props: {
    type: String,
    default() {
      return "";
    },
  },
};
</script>

index.vue 父组件

  • 子组件标签上使用 v-model 双向数据绑定 name
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />
    <p>{{ name }}</p>
    <CustomVModel v-model='name'/>
  </div>
</template>
<script>
import CustomVModel from "./CustomVModel.vue";
export default {
  name: "index",
  components: { CustomVModel },
  data() {
    return {
        name: '杂货铺'
    }
  }
};
</script>

173ca8d4b8354b6fa6d413a7b07eb9bc.png

2、nextTick

  • Vue 是异步渲染
  • data 改变之后,DOM 不会立刻渲染
  • $nextTick 会在 DOM 渲染之后被触发,以获取最新的 DOM 节点

示例:添加子节点,并获取子节点的总长度

NextTick.vue 组件

  • ref 用于打标识
  • refs 用于获取 DOM 元素
  • this.$nextTick(() => {...}) 为异步渲染,待 DOM 渲染完再回调
  • 如果不加 $nextTick 则输出的结果是 3
<template>
  <div>
    <ul ref="ul1">
      <li v-for="(item, index) in list" :key="index">
        {{ item }}
      </li>
    </ul>
    <button @click="addItem">添加一项</button>
  </div>
</template>
<script>
export default {
  name: "NextTick",
  data() {
    return {
      list: ["a", "b", "c"],
    };
  },
  methods: {
    addItem() {
      this.list.push(`${Date.now()}`);
      this.list.push(`${Date.now()}`);
      this.list.push(`${Date.now()}`);
      // 异步渲染,$nextTick 待 DOM 渲染完再回调
      // 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
      this.$nextTick(() => {
        // 获取 DOM 元素
        const ulElem = this.$refs.ul1;
        console.log(ulElem.childNodes.length); // 6
      });
    },
  },
};
</script>

93fe30ec4b7b4a29a799b86559b2d9dd.png

3、slot 插槽

(1)默认插槽

  • 让父组件可以向子组件指定位置插入 html 结构
  • <solt> 标签体内为默认内容,即父组件没设置内容时,这里显示

示例:默认插槽的基本使用

index.vue 父组件

  • 父组件的子组件标签内的 {{ website.title }} 是子组件插槽中要呈现的内容
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />
    <SlotDemo :url="website.url">
      {{ website.title }}
    </SlotDemo>
  </div>
</template>
<script>
import SlotDemo from "./SlotDemo.vue";
export default {
  components: { SlotDemo },
  data() {
    return {
      website: {
        url: "http://baidu.com/",
        title: "Baidu",
        subTitle: "百度",
      },
    };
  },
};
</script>

SlotDemo.vue 子组件

  • 当父组件的子组件标签内有内容时,则呈现相应内容
  • 当父组件的子组件标签内没有内容时,则呈现插槽的默认内容
<template>
    <a :href="url">
        <slot>
            默认内容,即父组件没设置内容时,这里显示
        </slot>
    </a>
</template>
<script>
export default {
    props: ['url'],
};
</script>

dd0d623af81b4323b4dd67283ce905a2.png

分割线 ------------------------------------------------------------------------------


ef4b1a3224a44438a413fe3297307ccd.png

(2)作用域插槽

  • 场景:插槽的内容可能想要同时使用父组件域内和子组件域内的数据

示例:显示子组件的 title,使用父组件的链接

index.vue 父组件

  • 在父组件的子组件标签内定义 <template>
  • <template> 标签内使用 v-slot 绑定自定义属性 slotProps
  • 之后通过 slotProps.soltData.title 获取子组件的 title 值
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />
    <ScopedSlot:url="website.url">
      <template v-slot="slotProps">
        {{ slotProps.slotData.title }}
      </template>
    </ScopedSlot>
  </div>
</template>
<script>
import ScopedSlot from "./ScopedSlot.vue";
export default {
  components: { ScopedSlot},
  data() {
    return {
      website: {
        url: "http://baidu.com/",
        title: "Baidu",
        subTitle: "百度",
      },
    };
  },
};
</script>

ScopedSlot.vue 子组件

  • <slot> 标签内自定义动态属性 slotData,绑定 data 中的 website
  • 在标签内通过插值语法显示子组件的 title
<template>
  <a :href="url">
    <slot :slotData="website">
      {{ website.subTitle }}
    </slot>
  </a>
</template>
<script>
export default {
  props: ["url"],
  data() {
    return {
      website: {
        url: "http://bilibili.com/",
        title: "Bilibili",
        subTitle: "哔哩哔哩",
      },
    };
  },
};
</script>

1bbc07aae35142d6ba3bf372994a24c7.png

(3)具名插槽

  • 在子组件的 <slot> 中使用 name='xxx' 给插槽命名
  • 在父组件的 <template> 中使用 v-slot:xxx 或者 #xxx 对应子组件的插槽

示例:具名插槽的使用

index.vue 父组件

  • 在父组件的 <template> 中使用 v-slot:xxx 或者 #xxx 对应子组件的插槽
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />
    <NamedSlot>
      <template v-slot:header>
        <h4>将插入到 header slot 中</h4>
      </template>
      <p>将插入到 main slot 中,即未命名的 slot</p>
      <template #footer>
        <p>将插入到 footer slot 中</p>
      </template>
    </NamedSlot>
  </div>
</template>
<script>
import NamedSlot from "./NamedSlot.vue";
export default {
  components: { NamedSlot }
};
</script>

NamedSlot.vue 子组件

  • 在子组件的 <slot> 中使用 name='xxx' 给插槽命名
<template>
  <div>
    <header>
      <slot name="header">默认头部</slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer">默认尾部</slot>
    </footer>
  </div>
</template>
<script>
export default {};
</script>

35d34695a9e3429da8d4373f1ac8e6a0.png

不积跬步无以至千里 不积小流无以成江海

点个关注不迷路,持续更新中…


相关文章
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
1300 1
|
移动开发 前端开发 JavaScript
Vue与React两大前端框架的主要差异点
以上就是Vue和React的主要差异点,希望对你有所帮助。在选择使用哪一个框架时,需要根据项目的具体需求和团队的技术栈来决定。
784 83
|
JavaScript 前端开发 编译器
Vue与TypeScript:如何实现更强大的前端开发
Vue.js 以其简洁的语法和灵活的架构在前端开发中广受欢迎,而 TypeScript 作为一种静态类型语言,为 JavaScript 提供了强大的类型系统和编译时检查。将 Vue.js 与 TypeScript 结合使用,不仅可以提升代码的可维护性和可扩展性,还能减少运行时错误,提高开发效率。本文将介绍如何在 Vue.js 项目中使用 TypeScript,并通过一些代码示例展示其强大功能。
550 22
|
存储 安全 算法
Java 集合面试题 PDF 下载及高频考点解析
本文围绕Java集合面试题展开,详细解析了集合框架的基本概念、常见集合类的特点与应用场景。内容涵盖`ArrayList`与`LinkedList`的区别、`HashSet`与`TreeSet`的对比、`HashMap`与`ConcurrentHashMap`的线程安全性分析等。通过技术方案与应用实例,帮助读者深入理解集合类的特性和使用场景,提升解决实际开发问题的能力。文末附带资源链接,供进一步学习参考。
356 4
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
638 3
|
人工智能 JavaScript 前端开发
Vue 性能革命:揭秘前端优化的终极技巧;Vue优化技巧,解决Vue项目卡顿问题
Vue在处理少量数据和有限dom的情况下技术已经非常成熟了,但现在随着AI时代的到来,海量数据场景会越来越多,Vue优化技巧也是必备技能。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
10月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
684 4
|
缓存 JavaScript 前端开发
鸿蒙5开发宝藏案例分享---Web开发优化案例分享
本文深入解读鸿蒙官方文档中的 `ArkWeb` 性能优化技巧,从预启动进程到预渲染,涵盖预下载、预连接、预取POST等八大优化策略。通过代码示例详解如何提升Web页面加载速度,助你打造流畅的HarmonyOS应用体验。内容实用,按需选用,让H5页面快到飞起!
|
JavaScript 前端开发 API
鸿蒙5开发宝藏案例分享---Web加载时延优化解析
本文深入解析了鸿蒙开发中Web加载完成时延的优化技巧,结合官方案例与实际代码,助你提升性能。核心内容包括:使用DevEco Profiler和DevTools定位瓶颈、四大优化方向(资源合并、接口预取、图片懒加载、任务拆解)及高频手段总结。同时提供性能优化黄金准则,如首屏资源控制在300KB内、关键接口响应≤200ms等,帮助开发者实现丝般流畅体验。
|
前端开发 JavaScript Shell
鸿蒙5开发宝藏案例分享---Web页面内点击响应时延分析
本文为鸿蒙开发者整理了Web性能优化的实战案例解析,结合官方文档深度扩展。内容涵盖点击响应时延核心指标(≤100ms)、性能分析工具链(如DevTools时间线、ArkUI Trace抓取)以及高频优化场景,包括递归函数优化、网络请求阻塞解决方案和setTimeout滥用问题等。同时提供进阶技巧,如首帧加速、透明动画陷阱规避及Web组件初始化加速,并通过优化前后Trace对比展示成果。最后总结了快速定位问题的方法与开发建议,助力开发者提升Web应用性能。

热门文章

最新文章

  • 1
    前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
    1282
  • 2
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(九):强势分析Animation动画各类参数;从播放时间、播放方式、播放次数、播放方向、播放状态等多个方面,完全了解CSS3 Animation
    589
  • 3
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(八):学习transition过渡属性;本文学习property模拟、duration过渡时间指定、delay时间延迟 等多个参数
    447
  • 4
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
    452
  • 5
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
    575
  • 6
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(五):背景属性;float浮动和position定位;详细分析相对、绝对、固定三种定位方式;使用浮动并清除浮动副作用
    748
  • 7
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(四):元素盒子模型;详细分析边框属性、盒子外边距
    1463
  • 8
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(三):元素继承关系、层叠样式规则、字体属性、文本属性;针对字体和文本作样式修改
    324
  • 9
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
    1184
  • 10
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(一):CSS发展史;CSS样式表的引入;CSS选择器使用,附带案例介绍
    523