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 |
Article directory
1. Communication between Vue components
1. props and $emit
props
commonly usedfather to sonpass datathis.$emit
commonly usedson to fatherpass dataevent.$emit
commonly 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
@add
on 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 mountedevent.$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
,$on
etc.
import Vue from 'vue'
export default new Vue()
Dividing line------------------------------------------------ -------------------------------------------------- ---------
Dividing line------------------------------------------------ -------------------------------------------------- ---------
Dividing line------------------------------------------------ -------------------------------------------------- ---------
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>
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>
3. Slot slot
(1) Default slot
- Allows parent components to insert
html
structures <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>
Dividing line------------------------------------------------ -------------------------------------------------- ---------
(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>
(3) Named slot
- Use in subcomponent
<slot>
'sname='xxx'
to name the slot <template>
Use in the parent component'sv-slot:xxx
or the#xxx
corresponding child component's slot
Example: Use of Named Slots
index.vue parent component
<template>
Use in the parent component'sv-slot:xxx
or the#xxx
corresponding 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>
'sname='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>
不积跬步无以至千里 不积小流无以成江海
Click to follow and don't get lost, continue to update...