Web front-end interview high-frequency test site - communication between Vue components and advanced features (communication between multiple components, custom v-model, nextTick, slot)

Series Article Directory

content Reference link
Basic use of Vue The basic use of Vue (one article to master the most basic knowledge points of Vue)
Vue communication and advanced features Communication between Vue components and advanced features (communication between various components, custom v-model, nextTick, slots)
Vue advanced features Advanced features of Vue (dynamic components, asynchronous loading, keep-alive, mixin, Vuex, Vue-Router)
Vue Principle 1 Vue principle (understanding MVVM model, in-depth / monitoring data changes, monitoring array changes, in-depth understanding of virtual DOM)
Vue Principle 2 Vue principle (diff algorithm, template compilation, component rendering and updating, JS implementation routing)
Vue Interview Questions Web front-end interview high-frequency test site - Vue interview questions


1. Communication between Vue components

1. props and $emit

  • propscommonly usedfather to sonpass data
  • this.$emitcommonly usedson to fatherpass data
  • event.$emitcommonly usedsibling roompass data

Example: Two subcomponents (input box component & list component) are dynamically added and deleted

Parent component (index.vue)

  • Parent component binds and custom event @addon child component label@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>

child component (input.vue)

  • Button binding addTitle event
  • this.$emit('add', this.title)Call the parent component's event using
  • Use event.$emit("onAddTitle", this.title)to call custom events (events defined by sibling components)
<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>

Child component (List.vue)

  • Use props to receive the list from the parent component, and make type restrictions and default values
  • The button is bound to the deleteItem function, which uses the event of this.$emit("delete", id)calling the parent component, and deletes it according to the id
  • event.$on("onAddTitle", this.addTitleHandler)Bind custom events when mounted
  • event.$off("onAddTitle", this.addTitleHandler)Destroy custom events before destruction (beforeDestroy)
  • Note: When binding and destroying custom events, the second parameter is an ordinary function passed in, do not write arrow functions (this point will change)
<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 file

  • Create a vue instance, you can use methods such as $emit, $off, $onetc.
import Vue from 'vue'

export default new Vue()

Dividing line------------------------------------------------ -------------------------------------------------- ---------

insert image description here

Dividing line------------------------------------------------ -------------------------------------------------- ---------

insert image description here

Dividing line------------------------------------------------ -------------------------------------------------- ---------

insert image description here

2. Life cycle

Detailed life cycle [reference link]

  • beforeCreate: vue instance is created, el and data have not been initialized, can not access data and method, generally do not operate at this stage.Generally do not operate at this stage
  • created: The data and method in the vue instance have been initialized, and the properties have been bound. But at this time it is still a virtual DOM, the real DOM has not been generated, and $el is not yet available.Data is generally initialized here
  • beforeMount: The template has been compiled, but it has not been rendered into the page (that is, the virtual dom is loaded into the real dom)
  • mounted: The template has been rendered into a real DOM, and the user can already see the rendered page. After executing mounted, it means that the instance has been completely created
  • beforeUpdate: Triggered before re-rendering, and then vue's virtual dom mechanism will rebuild the virtual dom and the last virtual dom tree to compare it with the diff algorithm and then re-render. Only the data changes on the view will trigger beforeUpdate and updated, and only the data changes in the data will not trigger.
  • updated: The data has been changed and the dom is re-rendered.
  • beforeDestroy: Executed before destruction (when the $destroy method is called), generally here: clear timers, clear custom bound events, etc...
  • destroyed: After destruction (the Dom element exists, but is no longer controlled by vue), uninstall the watcher, event listener, and child components.

Referring to the results of the example props and $emit, the parent-child component life cycle is summarized as follows:

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

Second, the advanced features of Vue

Custom v-model, $nextTick, slot, dynamic, asynchronous components, keep-alive, mixin

1. Customize v-model

Example: custom implementation v-model

CustomVModel.vue child component

  • input used :value instead of v-model
  • change1 attribute corresponds to
  • text1 attribute corresponds to
  • Prop is the property that is bound using the v-model directive in the parent component that calls the component
  • event corresponds to a function that modifies the value of the property specified by 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 parent component

  • Use v-model two-way data binding name on the child component label
<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>

insert image description here

2、nextTick

  • Vue is asynchronous rendering
  • After the data is changed, the DOM will not render immediately
  • $nextTick will be fired after DOM rendering to get the latest DOM node

Example: Add child nodes, and get the total length of child nodes

NextTick.vue component

  • ref is used for marking
  • refs are used to get DOM elements
  • this.$nextTick(() => {...})For asynchronous rendering, call back after the DOM is rendered
  • If not added $nextTick, the output result is 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>

insert image description here

3. Slot slot

(1) Default slot

  • Allows parent components to insert htmlstructures
  • <solt>The label body is the default content, that is, when the parent component does not set content, it is displayed here

Example: Basic usage of default slots

index.vue parent component

  • Inside the child component tag of the parent component { { website.title }}is the content to be rendered in the child component slot
<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 subcomponent

  • When there is content in the child component tag of the parent component, the corresponding content is rendered
  • The default content of the slot is rendered when there is no content inside the parent's child component's tag
<template>
    <a :href="url">
        <slot>
            默认内容,即父组件没设置内容时,这里显示
        </slot>
    </a>
</template>

<script>
export default {
    
    
    props: ['url'],
};
</script>

insert image description here

Dividing line------------------------------------------------ -------------------------------------------------- ---------

insert image description here

(2) Scope slot

  • Scenario: The content of a slot may want to use data from both the parent's domain and the child's domain

Example: Display the title of the child component, use the link of the parent component

index.vue parent component

  • Defined inside the child component tag of the parent component<template>
  • <template>Use v-slot to bind custom properties slotProps within the label
  • Then get the title value of the subcomponent through slotProps.soltData.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 subcomponent

  • <slot>Customize the dynamic attribute slotData in the tag, bind the website in the data
  • Display the title of the child component using interpolation syntax within the tag
<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>

insert image description here

(3) Named slot

  • Use in subcomponent <slot>'s name='xxx'to name the slot
  • <template>Use in the parent component's v-slot:xxxor the #xxxcorresponding child component's slot

Example: Use of Named Slots

index.vue parent component

  • <template>Use in the parent component's v-slot:xxxor the #xxxcorresponding child component's slot
<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 subcomponent

  • Use in subcomponent <slot>'s name='xxx'to name the slot
<template>
  <div>
    <header>
      <slot name="header">默认头部</slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer">默认尾部</slot>
    </footer>
  </div>
</template>

<script>
export default {
    
    };
</script>

insert image description here

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

Click to follow and don't get lost, continue to update...

Guess you like

Origin blog.csdn.net/qq_45902692/article/details/126453192