19、组件之间传递数据
组件与组件之间不是完全独立的,而是有交集的,那就是组件与组 件之间是可以传递数据的 传递数据的解决方案就是 props
ComponentA.vue
html
<template>
<!-- 使用ComponentB组件,并传递title属性 -->
<h3>ComponentA</h3>
<ComponentB title="传递数据"/>
</template>
<script>
// 导入ComponentB组件
import ComponentB from "./ComponentB.vue";
export default {
// 定义组件选项对象
components: {
// 将ComponentB注册为当前组件的局部组件
ComponentB
}
};
</script>
ComponentB.vue
html
<template>
<!-- 使用props接收来自父组件的title属性 -->
<h3>ComponentB</h3>
<p>{
{ title }}</p>
</template>
<script>
export default {
// 定义组件选项对象
props: ["title"]
};
</script>
动态数据传递
html
<template>
<!-- 使用ComponentA和ComponentB组件 -->
<h3>ComponentA</h3>
<!-- 将父组件的数据message传递给ComponentB -->
<ComponentB :title="message" />
</template>
<script>
// 导入ComponentB组件
import ComponentB from "./ComponentB.vue";
export default {
// 定义组件选项对象
data() {
return {
message: "动态数据" // 定义数据message
};
},
components: {
ComponentB // 将ComponentB注册为当前组件的局部组件
}
};
</script>
注意事项:
props 传递数据,只能从父级传递到子级,不能反其道而行
组件传递多种数据类型
通过 props 传递数据,不仅可以传递字符串类型的数据,还可以是其 他类型,例如:数字、对象、数组等 但实际上任何类型的值都可以作为 props 的值被传递.
传递Number类型
ComponentA.vue
html
<template>
<!-- 显示ComponentA标题 -->
<h3>ComponentA</h3>
<!-- 使用ComponentB组件,并传递age属性 -->
<ComponentB :age="age"/>
</template>
<script>
// 导入ComponentB组件
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
age: 20 // 定义数据age
};
},
components: {
ComponentB // 将ComponentB注册为当前组件的局部组件
}
};
</script>
ComponentB.vue
html
<template>
<!-- 显示ComponentB标题 -->
<h3>ComponentB</h3>
<!-- 显示从父组件传递过来的age属性 -->
<p>{
{ age }}</p>
</template>
<script>
export default {
props: ["age"] // 定义props接收来自父组件的age属性
};
</script>
传递Array类型
ComponentA.vue
html
<template>
<!-- 显示ComponentA标题 -->
<h3>ComponentA</h3>
<!-- 使用ComponentB组件,并传递names属性 -->
<ComponentB :names="names" />
</template>
<script>
// 导入ComponentB组件
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
names: ["iwen", "ime", "frank"] // 定义数据names
};
},
components: {
ComponentB // 将ComponentB注册为当前组件的局部组件
}
};
</script>
ComponentB.vue
html
<template>
<!-- 显示ComponentB标题 -->
<h3>ComponentB</h3>
<!-- 使用v-for指令遍历names数组,并显示每个元素 -->
<p v-for="(item, index) of names" :key="index">{
{ item }}</p>
</template>
<script>
export default {
props: ["names"] // 定义props接收来自父组件的names属性
};
</script>
传递Object类型
ComponentA.vue
html
<template>
<!-- 显示ComponentA标题 -->
<h3>ComponentA</h3>
<!-- 使用ComponentB组件,并传递userInfo属性 -->
<ComponentB :userInfo="userInfo" />
</template>
<script>
// 导入ComponentB组件
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
userInfo: { // 定义数据userInfo
name: "iwen",
age: 20
}
};
},
components: {
ComponentB // 将ComponentB注册为当前组件的局部组件
}
};
</script>
ComponentB.vue
html
<template>
<!-- 显示ComponentB标题 -->
<h3>ComponentB</h3>
<!-- 显示从父组件传递过来的userInfo.name属性的值 -->
<p>{
{ userInfo.name }}</p>
<!-- 显示从父组件传递过来的userInfo.age属性的值 -->
<p>{
{ userInfo.age }}</p>
</template>
<script>
export default {
props: ["userInfo"] // 定义props接收来自父组件的userInfo属性
};
</script>
组件传递Props效验
Vue 组件可以更细致地声明对传入的 props 的校验要求
ComponentA.vue
html
<template>
<!-- 显示ComponentA标题 -->
<h3>ComponentA</h3>
<!-- 使用ComponentB组件,并传递title和userInfo属性 -->
<ComponentB title="Props效验" :userInfo="userInfo" />
</template>
<script>
// 导入ComponentB组件
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
userInfo: { // 定义数据userInfo
name: "iwen",
age: 20
}
},
components: {
ComponentB // 将ComponentB注册为当前组件的局部组件
}
};
</script>
ComponentB.vue
html
<template>
<!-- 显示ComponentB标题 -->
<h3>ComponentB</h3>
<!-- 显示从父组件传递过来的title属性的值 -->
<p>{
{ title }}</p>
<!-- 显示从父组件传递过来的userInfo.name属性的值 -->
<p>{
{ userInfo.name }}</p>
<!-- 显示从父组件传递过来的userInfo.age属性的值 -->
<p>{
{ userInfo.age }}</p>
</template>
<script>
export default {
props: { // 定义props接收来自父组件的title和userInfo属性
title: {
type: String
},
userInfo: {
type: Object
}
}
};
</script>
默认值default
html
<template>
<!-- 显示ComponentB标题 -->
<h3>ComponentB</h3>
<!-- 显示从父组件传递过来的title属性的值 -->
<p>{
{ title }}</p>
<!-- 显示从父组件传递过来的userInfo.name属性的值 -->
<p>{
{ userInfo.name }}</p>
<!-- 显示从父组件传递过来的userInfo.age属性的值 -->
<p>{
{ userInfo.age }}</p>
<!-- 显示从父组件传递过来的age属性的值 -->
<p>{
{ age }}</p>
</template>
<script>
export default {
// 定义props选项,接收来自父组件的title、userInfo和age属性
props: {
title: {
type: String // title属性的类型为String
},
userInfo: {
type: Object, // userInfo属性的类型为Object
default() {
return {} // 默认值为空对象
}
},
age: {
type: Number, // age属性的类型为Number
default: 20 // 默认值为20
}
}
};
</script>
必选项required
html
<template>
<!-- 显示ComponentB标题 -->
<h3>ComponentB</h3>
<!-- 显示从父组件传递过来的title属性的值 -->
<p>{
{ title }}</p>
<!-- 显示从父组件传递过来的userInfo.name属性的值 -->
<p>{
{ userInfo.name }}</p>
<!-- 显示从父组件传递过来的userInfo.age属性的值 -->
<p>{
{ userInfo.age }}</p>
<!-- 显示从父组件传递过来的age属性的值 -->
<p>{
{ age }}</p>
</template>
<script>
export default {
// 定义props选项,接收来自父组件的title、userInfo和age属性
props: {
title: {
type: String, // title属性的类型为String
required: true // title属性是必需的
},
userInfo: {
type: Object, // userInfo属性的类型为Object
// 对象或者数组应当用工厂函数返回
default() {
return {} // 默认值为空对象
}
},
age: {
type: Number, // age属性的类型为Number
default: 20 // 默认值为20
}
};
</script>
在Vue.js中,
prop
是一种特殊的数据属性,用于父组件向子组件传递数据。prop
是只读的,这意味着子组件不能修改从父组件接收到的prop
数据。如果尝试修改prop
的值,Vue 会发出警告信息,提示prop
是只读的。
20、组件事件
在Vue.js框架中,组件之间的通信是一个常见的需求。$emit
方法就是用来在组件的模板表达式中触发自定义事件的,这使得父子组件之间可以进行通信。通过这种方式,子组件可以向父组件发送消息,比如通知某个操作已经完成,或者请求父组件执行某个操作。
自定义事件的触发可以用于多种目的,其中之一就是组件之间传递数据。例如,当用户在子组件中进行某种操作时,子组件可以通过$emit
方法向父组件发送一个事件,携带必要的数据。父组件可以监听这个事件,并在事件触发时接收数据,从而实现数据的传递。
父组件(ComponentA.vue)
html
<template>
<!-- 显示标题 -->
<h3>ComponentA</h3>
<!-- 引入子组件ComponentB,并监听名为some-event的自定义事件 -->
<ComponentB @some-event="getHandle"/>
<!-- 显示从子组件接收到的数据 -->
<p>ComponentA接受的数据: {
{ message }}</p>
</template>
<script>
// 导入子组件ComponentB
import ComponentB from "./ComponentB.vue"
export default {
data() {
return {
// 初始化一个空字符串用于存储从子组件接收到的数据
message: ""
}
},
components: {
// 注册子组件ComponentB
ComponentB
},
methods: {
// 定义一个方法getHandle,用于处理从子组件接收到的数据
getHandle(data) {
this.message = data; // 将接收到的数据赋值给message属性
}
}
}
</script>
子组件(ComponentB.vue)
html
<template>
<!-- 显示标题 -->
<h3>ComponentB</h3>
<!-- 定义一个按钮,点击时触发sendHandle方法 -->
<button @click="sendHandle">发送数据</button>
</template>
<script>
export default {
methods: {
// 定义一个方法sendHandle,用于触发自定义事件并发送数据
sendHandle() {
this.$emit("someEvent", "ComponentB的数据"); // 触发名为someEvent的事件,并传递数据
}
}
}
</script>
组件之间传递数据:
父传子:使用
props
在Vue.js中,
props
是父组件向子组件传递数据的一种方式。父组件可以通过在子组件标签中定义属性来传递数据,这些属性在子组件内部可以通过props
对象访问。这种方式是单向数据流,确保了数据的流向是从父组件到子组件,有助于避免组件之间的耦合。
子传父:使用自定义事件 (
this.$emit
)
当需要从子组件向父组件传递数据时,可以使用自定义事件。子组件通过
this.$emit
方法触发一个事件,并将数据作为参数传递。父组件需要监听这个事件,并在事件触发时接收数据。这种方式允许子组件在需要时通知父组件,例如用户交互或数据变化。
组件事件配合 v-model 使用
如果是用户输入,我们希望在获取数据的同时发送数据配合 v-model 来使用
父组件(ComponentA.vue)
html
<template>
<div>
<h3>ComponentA</h3>
<!-- 使用@some-event监听来自ComponentB的自定义事件 -->
<componentB @some-event="getHandle"/>
<!-- 显示从ComponentB接收到的数据 -->
<p>ComponentA接受的数据: {
{ message }}</p>
</div>
</template>
<script>
// 导入子组件ComponentB
import ComponentB from "./ComponentB.vue"
export default {
data() {
return {
// 初始化message为空字符串,用于存储从子组件接收的数据
message: ""
}
},
components: {
// 注册子组件ComponentB
ComponentB
},
methods: {
// 定义getHandle方法来接收从子组件传递的数据
getHandle(data) {
this.message = data;
}
}
}
</script>
子组件(ComponentB.vue)
html
<template>
<div>
<h3>ComponentB</h3>
<!-- 使用v-model绑定输入框的值到searchText -->
<input v-model="searchText" />
</div>
</template>
<script>
export default {
data() {
return {
// 初始化searchText为空字符串
searchText: ""
}
},
watch: {
// 监听searchText的变化
searchText(newVal, oldVal) {
// 当searchText变化时,触发someEvent事件,并将新值作为参数传递
this.$emit("someEvent", newVal);
}
}
}
</script>
组件数据传递
通常我们使用 props
来实现父组件向子组件的数据传递(单向下行绑定),但Vue.js也提供了一种方式,使得子组件可以通过 props
实现数据的"回传"给父组件,这通常通过使用 .sync
修饰符或在Vue 3中使用 v-model
来实现。
父组件(ParentComponent.vue)
html
<template>
<div>
<h3>ComponentA</h3>
<!-- 使用:onFnEvent监听来自ChildComponent的自定义事件 -->
<Child :onFnEvent="fn" />
<!-- 显示从ChildComponent接收到的数据 -->
<p>{
{ message }}</p>
</div>
</template>
<script>
// 导入子组件Child
import Child from "./components/Child.vue";
export default {
data() {
return {
// 初始化message为空字符串,用于存储从子组件接收的数据
message: ""
}
},
methods: {
// 定义fn方法来接收从子组件传递的数据
fn(data) {
this.message = data;
}
},
components: {
// 注册子组件Child
Child
}
}
</script>
子组件(Child.vue)
html
<template>
<div>
<h3>组件传递数据</h3>
<!-- 调用onFnEvent方法并传递数据 -->
<p>{
{ onFnEvent('测试数据') }}</p>
</div>
</template>
<script>
export default {
props: {
// 定义一个名为onFnEvent的prop,类型为Function
onFnEvent: {
type: Function
}
}
}
</script>
代码解释
父组件(ParentComponent.vue):
在模板中,使用
<Child :onFnEvent="fn" />
标签引入子组件,并使用:onFnEvent="fn"
来监听子组件触发的事件。当子组件触发事件时,父组件的
fn
方法会被调用,并接收子组件传递的数据。
message
数据属性用于存储从子组件接收到的数据,并在模板中显示。子组件(Child.vue):
在模板中,调用
onFnEvent
方法并传递数据'测试数据'
。定义一个名为
onFnEvent
的prop
,类型为Function
,用于接收父组件传递的事件处理函数。
21、插槽 Slots
我们已经了解到组件能够接收任意类型的 JavaScript 值作为 props,但组件要如何接收模板内容呢?在某些场景中,我们可能 想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这 些片段。
<solt>元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽 内容 (slot content) 将在哪里被渲染.
父组件(ComponentA.vue)
html
<template>
<div>
<h3>ComponentA</h3>
<!-- 使用ComponentB,并在插槽中传递内容 -->
<ComponentB>
<h3>插槽传递视图内容</h3>
</ComponentB>
</div>
</template>
<script>
// 导入子组件ComponentB
import ComponentB from "./ComponentB.vue";
export default {
components: {
// 注册子组件ComponentB
ComponentB
}
}
</script>
子组件(ComponentB.vue)
html
<template>
<div>
<h3>ComponentB</h3>
<!-- 使用<slot>标签来接收从父组件传递的内容 -->
<slot></slot>
</div>
</template>
<script>
export default {
// ComponentB不需要额外的逻辑,只需定义插槽
}
</script>
渲染作用域
插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在 父组件模板中定义的.
父组件(ComponentA.vue)
html
<template>
<div>
<h3>ComponentA</h3>
<!-- 使用ComponentB,并在插槽中传递内容 -->
<ComponentB>
<h3>{
{ message }}</h3>
</ComponentB>
</div>
</template>
<script>
// 导入子组件ComponentB
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
// 定义message数据属性,用于存储要传递给子组件的内容
message: "message在父级"
}
},
components: {
// 注册子组件ComponentB
ComponentB
}
}
</script>
子组件(ComponentB.vue)
html
<template>
<div>
<h3>ComponentB</h3>
<!-- 使用<slot>标签来接收从父组件传递的内容 -->
<slot></slot>
</div>
</template>
<script>
export default {
// ComponentB不需要额外的逻辑,只需定义插槽
}
</script>
默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容.
html
<template>
<div>
<h3>ComponentB</h3>
<!-- 使用<slot>标签定义插槽,并设置默认内容 -->
<slot>插槽默认值</slot>
</div>
</template>
<script>
export default {
// ComponentB不需要额外的逻辑,只需定义插槽和默认值
}
</script>
具名插槽
父组件(ComponentA.vue)
html
<template>
<div>
<h3>ComponentA</h3>
<!-- 使用ComponentB,并为header和main插槽传递内容 -->
<ComponentB>
<template v-slot:header>
<h3>标题</h3>
</template>
<template v-slot:main>
<p>内容</p>
</template>
</ComponentB>
</div>
</template>
<script>
// 导入子组件ComponentB
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
// 定义message数据属性,虽然在这个例子中没有用到
message: "message在父级"
}
},
components: {
// 注册子组件ComponentB
ComponentB
}
}
</script>
子组件(ComponentB.vue)
html
<template>
<div>
<h3>ComponentB</h3>
<!-- 使用<slot>标签定义具名插槽header -->
<slot name="header"></slot>
<hr>
<!-- 使用<slot>标签定义具名插槽main -->
<slot name="main"></slot>
</div>
</template>
<script>
export default {
// ComponentB不需要额外的逻辑,只需定义具名插槽
}
</script>
代码解释
父组件(ComponentA.vue):
在模板中,使用
<ComponentB>
标签引入子组件,并为header
和main
插槽传递内容。使用
<template v-slot:header>
和<template v-slot:main>
来指定内容应该填充到哪个插槽中。
ComponentB
组件被注册在父组件的components
对象中,使其可以在父组件的模板中使用。子组件(ComponentB.vue):
在模板中,使用
<slot name="header"></slot>
和<slot name="main"></slot>
来定义具名插槽。这些插槽用于接收从父组件传递的内容。当父组件使用
<ComponentB>
标签并包含具名插槽的内容时,这些内容将替换子组件中的相应插槽标签。
在Vue.js中,
v-slot
指令用于定义插槽,而#
符号是v-slot
的简写形式,用于指定插槽的名称。
父组件(ComponentA.vue)
html
<template>
<div>
<h3>ComponentA</h3>
<!-- 使用ComponentB,并为header和main插槽传递内容 -->
<ComponentB>
<template #header>
<h3>标题</h3>
</template>
<template #main>
<p>内容</p>
</template>
</ComponentB>
</div>
</template>
<script>
// 导入子组件ComponentB
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
// 定义message数据属性,用于存储要传递给子组件的内容
message: "message在父级"
}
},
components: {
// 注册子组件ComponentB
ComponentB
}
}
</script>
子组件(ComponentB.vue)
html
<template>
<div>
<h3>ComponentB</h3>
<!-- 使用<slot>标签定义具名插槽header -->
<slot name="header"></slot>
<hr>
<!-- 使用<slot>标签定义具名插槽main -->
<slot name="main"></slot>
</div>
</template>
<script>
export default {
// ComponentB不需要额外的逻辑,只需定义具名插槽
}
</script>
在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时 将一部分数据提供给插槽 我们也确实有办法这么做!可以像对组件传递 props 那样,向一个 插槽的出口上传递 attributes
父组件(ComponentA.vue)
html
<template>
<div>
<h3>ComponentA</h3>
<!-- 使用ComponentB,并通过具名插槽slotProps传递数据 -->
<componentB v-slot="slotProps">
<h3>{
{ message }}-{
{ slotProps.text }}</h3>
</componentB>
</div>
</template>
<script>
// 导入子组件ComponentB
import ComponentB from "./ComponentB.vue";
export default {
data() {
return {
// 定义message数据属性,用于存储要传递给子组件的内容
message: "message在父级"
}
},
components: {
// 注册子组件ComponentB
ComponentB
}
}
</script>
子组件(ComponentB.vue)
html
<template>
<div>
<h3>ComponentB</h3>
<!-- 使用<slot>标签定义具名插槽,并传递message数据 -->
<slot :text="message"></slot>
</div>
</template>
<script>
export default {
data() {
return {
// 定义message数据属性,用于存储子组件的数据
message: "ComponentB中的数据"
}
}
}
</script>
代码解释
父组件(ComponentA.vue):
在模板中,使用
<componentB>
标签引入子组件,并为slotProps
插槽传递内容。使用
<template v-slot="slotProps">
来指定内容应该填充到哪个插槽中,并从子组件接收数据。
message
数据属性用于存储要传递给子组件的内容。
ComponentB
组件被注册在父组件的components
对象中,使其可以在父组件的模板中使用。子组件(ComponentB.vue):
在模板中,使用
<slot :text="message"></slot>
来定义具名插槽。这个插槽用于接收从父组件传递的内容。
message
数据属性用于存储子组件的数据,并通过插槽传递给父组件。
具名插槽传递数据
html
<template>
<!-- 显示ComponentA的标题 -->
<h3>ComponentA</h3>
<!-- 使用ComponentB组件,并通过名为header的slot传递slotProps -->
<ComponentB #header="slotProps">
<!-- 显示从父组件传递的消息和slotProps.text -->
<h3>{
{ message }}-{
{ slotProps.text }}</h3>
</ComponentB>
</template>
<script>
// 导入ComponentB组件
import ComponentB from "./ComponentB.vue"
export default {
data() {
return {
// 定义一个消息变量,用于在模板中显示
message: "message在父级"
}
},
// 定义组件对象,用于注册子组件
components: {
ComponentB
}
}
</script>
html
<template>
<!-- 显示ComponentB的标题 -->
<h3>ComponentB</h3>
<!-- 使用slot,允许父组件通过名为header的slot传递内容 -->
<slot name="header" :text="message"></slot>
</template>
<script>
export default {
data() {
return {
// 定义一个消息变量,用于在模板中显示
message: "ComponentB中的数据"
}
}
}
</script>
22、组件生命周期
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如 设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变 时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函 数,让开发者有机会在特定阶段运行自己的代码。
html
<template>
<!-- 显示组件生命周期的标题 -->
<h3>组件生命周期</h3>
<!-- 显示message数据 -->
<p>{
{ message }}</p>
<!-- 一个按钮,点击时调用updateHandle方法更新数据 -->
<button @click="updateHandle">更新数据</button>
</template>
<script>
export default {
// data函数返回一个对象,其中包含一个message属性,初始值为"老数据"
data() {
return {
message: "老数据"
}
},
// methods对象包含组件的方法
methods: {
// updateHandle方法更新message属性的值为"新数据"
updateHandle() {
this.message = "新数据"
}
},
// 生命周期钩子函数
beforeCreate() {
// 在组件实例初始化之后调用,此时不能访问data中的属性
console.log("组件创建之前");
},
created() {
// 在实例创建完成后被立即调用,此时可以访问data中的属性
console.log("组件创建之后");
},
beforeMount() {
// 在挂载开始之前被调用,相关的render函数首次被调用
console.log("组件渲染之前");
},
mounted() {
// el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子
console.log("组件挂载之后");
},
beforeUpdate() {
// 数据更新之前调用,可以访问data中的属性,但是视图尚未更新
console.log("数据更新之前");
},
updated() {
// 数据更新之后调用,可以访问data中的属性,视图已经更新
console.log("数据更新之后");
},
beforeUnmount() {
// 实例销毁之前调用,此时可以访问data中的属性,但是视图已经销毁
console.log("组件卸载之前");
},
unmounted() {
// 实例销毁后调用,此时不能访问data中的属性
console.log("组件卸载之后");
}
}
</script>
beforeCreate
:在组件实例初始化之后调用,此时不能访问data
中的属性。
created
:在实例创建完成后被立即调用,此时可以访问data
中的属性。
beforeMount
:在挂载开始之前被调用,相关的render函数首次被调用。
mounted
:el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
beforeUpdate
:数据更新之前调用,可以访问data
中的属性,但是视图尚未更新。
updated
:数据更新之后调用,可以访问data
中的属性,视图已经更新。
beforeUnmount
:实例销毁之前调用,此时可以访问data
中的属性,但是视图已经销毁。
unmounted
:实例销毁后调用,此时不能访问data
中的属性。
生命周期应用
通过ref
获取元素DOM结构
html
<template>
<!-- 模板部分 -->
<h3>组件生命周期应用</h3>
<!-- 一个段落元素,通过ref属性设置引用名为"name" -->
<p ref="name">哈哈哈</p>
</template>
<script>
export default {
// beforeMount生命周期钩子
beforeMount() {
// 在这个阶段,DOM尚未渲染,因此$refs.name是undefined
console.log(this.$refs.name); // 输出:undefined
},
// mounted生命周期钩子
mounted() {
// 在这个阶段,DOM已经渲染完成,可以通过$refs访问模板中的DOM元素
console.log(this.$refs.name); // 输出:<p ref="name">哈哈哈</p>的DOM元素
}
}
</script>
beforeMount
生命周期钩子:在这个阶段,Vue 已经完成了模板的编译,但是还没有挂载 DOM 元素,因此this.$refs.name
是undefined
。
mounted
生命周期钩子:在这个阶段,Vue 已经完成了模板的挂载,DOM 元素已经创建并添加到页面中,因此可以通过this.$refs.name
访问到<p>
元素的 DOM 对象。
模拟网络请求渲染数据,页面加载后初始化数据
html
<template>
<!-- 模板部分 -->
<h3>组件生命周期应用</h3>
<!-- 使用v-for指令循环渲染banner数组中的每一项 -->
<ul>
<li v-for="(item, index) in banner" :key="index">
<!-- 显示每一项的标题 -->
<h3>{
{ item.title }}</h3>
<!-- 显示每一项的内容 -->
<p>{
{ item.content }}</p>
</li>
</ul>
</template>
<script>
export default {
// data函数返回一个对象,其中包含一个banner数组,初始为空
data() {
return {
banner: []
}
},
// mounted生命周期钩子
mounted() {
// 在组件挂载到DOM后,初始化banner数组
this.banner = [
{
"title": "我在爱尔兰",
"content": "爱尔兰(爱尔兰语:Poblacht na hÉireann;英语:Republic of Ireland), 是一个西欧的议会共和制国家,西临大西洋,东靠爱尔兰海,与英国隔海相望,是北美通向欧洲的通道爱尔兰自然",
},
{
"title": "一个人的东京",
"content": "东京(Tokyo)是日本国的首都,是亚洲第一大城市,世界第二大城市。全球最大的经济中心之一。东京的著名观光景点有东京铁塔、皇居、国会议事堂、浅草寺、浜离宫、上野公园与动物园",
},
{
"title": "普罗旺斯的梦",
"content": "普罗旺斯(Provence)位于法国东南部,毗邻地中海和意大利,从地中海沿岸延伸到内陆的丘陵地区,中间有大河"Rhone"流过。自古就以靓丽的阳光和蔚蓝的天空,迷人的地中海和心醉",
},
{
"title": "相约夏威夷之夏",
"content": "夏威夷州位于北太平洋中,距离美国本土3,700公里,总面积16,633平方公里,属于太平洋沿岸地区。首府为檀香山。在1778至1898年间,夏威夷也被称为"三明治群岛"(Sandwich Islands)",
}
]
}
}
</script>
23、动态组件
有些场景会需要在两个组件间来回切换,比如 Tab 界面
html
<template>
<h3>ComponentA</h3>
</template>
html
<template>
<h3>ComponentB</h3>
</template>
html
<template>
<!-- 模板部分 -->
<h3>组件切换</h3>
<!-- 使用 :is 绑定来动态切换组件 -->
<component :is="currentTab"></component>
<!-- 一个按钮,点击时调用 changeComponentHandle 方法切换组件 -->
<button @click="changeComponentHandle">切换</button>
</template>
<script>
// 导入ComponentA和ComponentB组件
import ComponentA from "./components/ComponentA.vue"
import ComponentB from "./components/ComponentB.vue"
export default {
// 注册ComponentA和ComponentB为本地组件
components: {
ComponentA,
ComponentB
},
// data函数返回一个对象,其中包含一个currentTab属性,用于控制显示哪个组件
data() {
return {
currentTab: "ComponentA", // 默认显示ComponentA
}
},
// methods对象包含组件的方法
methods: {
// changeComponentHandle方法用于切换currentTab的值,从而切换组件
changeComponentHandle() {
this.currentTab = this.currentTab === "ComponentA" ? "ComponentB" : "ComponentA";
}
}
}
</script>
:is
是一个属性绑定,它用于动态地绑定组件的名称或路径。就相当于是<componentA/>
24、组件保持存活
当使用 <component :is="...">
来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过<keep-alive>
组件强制被切换掉的组件仍然保持"存活"的状态,下一次使用该组件的时候需要重新渲染,浪费资源。
就是加个标签的事儿
html
<template>
<!-- 模板部分 -->
<h3>组件切换</h3>
<!-- 使用 <keep-alive> 包裹动态组件,保持切换组件的状态 -->
<keep-alive>
<!-- 使用 :is 绑定来动态切换组件 -->
<!-- 当 currentTab 变化时,Vue 将根据 currentTab 的值渲染对应的组件 -->
<component :is="currentTab"></component>
</keep-alive>
<!-- 一个按钮,点击时调用 changeComponentHandle 方法切换组件 -->
<!-- 这将改变 currentTab 的值,从而触发组件的切换 -->
<button @click="changeComponentHandle">切换</button>
</template>
<script>
// 导入ComponentA和ComponentB组件
import ComponentA from "./components/ComponentA.vue"
import ComponentB from "./components/ComponentB.vue"
export default {
// 注册ComponentA和ComponentB为本地组件,使其在模板中可用
components: {
ComponentA,
ComponentB
},
// data函数返回一个对象,其中包含一个currentTab属性,用于控制显示哪个组件
data() {
return {
currentTab: "ComponentA", // 默认显示ComponentA组件
}
},
// methods对象包含组件的方法
methods: {
// changeComponentHandle方法用于切换currentTab的值,从而切换组件
// 当按钮被点击时,这个方法会被调用
changeComponentHandle() {
// 使用三元运算符来切换currentTab的值
// 如果当前是ComponentA,则切换到ComponentB,反之亦然
this.currentTab = this.currentTab === "ComponentA" ? "ComponentB" : "ComponentA";
}
}
}
</script>
<keep-alive>
标签用于包裹动态组件<component :is="currentTab"></component>
。它的作用是保持组件的状态,即使组件被切换,之前的状态也不会丢失,这样可以提高性能,因为避免了不必要的组件销毁和重建。
25、异步组件
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent
方法来实现此功能
html
<template>
<!-- 模板部分 -->
<h3>组件切换</h3>
<!-- 使用 <keep-alive> 包裹动态组件,保持切换组件的状态 -->
<keep-alive>
<!-- 使用 :is 绑定来动态切换组件 -->
<!-- 当 currentTab 变化时,Vue 将根据 currentTab 的值渲染对应的组件 -->
<component :is="currentTab"></component>
</keep-alive>
<!-- 一个按钮,点击时调用 changeComponentHandle 方法切换组件 -->
<!-- 这将改变 currentTab 的值,从而触发组件的切换 -->
<button @click="changeComponentHandle">切换</button>
</template>
<script>
// 从 'vue' 导入 defineAsyncComponent 方法,用于定义异步组件
import { defineAsyncComponent } from 'vue'
// 导入 ComponentA 组件
import ComponentA from "./components/ComponentA.vue"
// 使用 defineAsyncComponent 定义异步加载的 ComponentB 组件
const AsyncComponentB = defineAsyncComponent(() =>
import('./components/ComponentB.vue')
)
export default {
// 注册 ComponentA 和 AsyncComponentB 为本地组件,使其在模板中可用
components: {
ComponentA,
AsyncComponentB
},
// data 函数返回一个对象,其中包含一个 currentTab 属性,用于控制显示哪个组件
data() {
return {
currentTab: "ComponentA", // 默认显示 ComponentA 组件
}
},
// methods 对象包含组件的方法
methods: {
// changeComponentHandle 方法用于切换 currentTab 的值,从而切换组件
// 当按钮被点击时,这个方法会被调用
changeComponentHandle() {
// 使用条件运算符来切换 currentTab 的值
// 如果当前是 ComponentA,则切换到 AsyncComponentB,反之亦然
this.currentTab = this.currentTab == "ComponentA" ? "AsyncComponentB" : "ComponentA";
}
}
}
</script>
异步组件:
使用
defineAsyncComponent
方法定义了一个异步组件AsyncComponentB
。这允许 Vue 延迟加载ComponentB.vue
文件,直到实际需要渲染该组件时才加载。异步组件在大型应用中非常有用,可以减少初始加载时间,提高性能。
26、依赖注入
通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props
。想象一下这样的结构:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦 。
这一问题被称为"prop 逐级透传"
provide
和 inject
可以帮助我们解决这一问题。 一个父组件相对于其所有的后代组件,会作为依赖提供者 。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖
html
<template>
<!-- 模板部分 -->
<h3>祖宗</h3> <!-- 显示"祖宗"标题 -->
<Parent /> <!-- 使用子组件 Parent -->
</template>
<script>
// 导入子组件 Parent
import Parent from "./components/Parent.vue"
export default {
// 使用 provide 提供一个 message,可以在子孙组件中通过 inject 访问
provide: {
message: "爷爷的财产"
},
// 注册子组件 Parent,使其在模板中可用
components: {
Parent
}
}
</script>
html
<template>
<!-- 模板部分 -->
<h3>Parent</h3> <!-- 显示"Parent"标题 -->
<Child /> <!-- 使用子组件 Child -->
</template>
<script>
// 导入子组件 Child
import Child from "./Child.vue"
export default {
// 注册子组件 Child,使其在模板中可用
components: {
Child // 注册的组件名称与模板中使用的标签名称一致
}
}
</script>
html
<template>
<!-- 模板部分 -->
<h3>Child</h3> <!-- 显示"Child"标题 -->
<p>{
{ message }}</p> <!-- 显示从祖先组件注入的消息 -->
</template>
<script>
export default {
// 使用 inject 选项来接收名为 message 的数据
inject: ["message"]
}
</script>
provide/inject:这是 Vue 提供的一种跨组件通信方式,允许一个祖先组件向其所有子孙组件提供数据,而不必通过每个中间组件逐级传递 props。
provide
和inject
只能由上到下的传递,不能反向传递
javascript
// 导入 Vue 的 createApp 函数
import { createApp } from 'vue'
// 导入根组件 App
import App from './App.vue'
// 创建一个 Vue 应用实例
const app = createApp(App)
// 使用 app.provide 提供全局数据
// 这里提供一个名为 "golabData" 的数据,值是 "我是全局数据"
app.provide("golabData", "我是全局数据")
// 将 Vue 应用挂载到 id 为 'app' 的 DOM 元素上
app.mount('#app')
- 使用
app.provide("golabData", "我是全局数据")
提供一个全局数据。这里golabData
是一个键,"我是全局数据"
是这个键对应的值。任何组件都可以通过inject
选项访问这个全局数据。
27、Vue应用
vue从哪开始执行的?
每个 Vue 应用都是通过 createApp
函数创建一个新的 应用实例
javascript
// 从 'vue' 导入 createApp 函数,用于创建 Vue 应用实例
import { createApp } from 'vue'
// 导入根组件 App
import App from './App.vue'
// 创建 Vue 应用实例
// 在一个 Vue 项目中,通常只有一个 Vue 应用实例
const app = createApp(App)
// 将 Vue 应用挂载到页面中 id 为 'app' 的元素上
app.mount('#app')
我们传入 createApp
的对象实际上是一个组件,每个应用都需要一个"根组件",其他组件将作为其子组件。
javascript
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'
const app = createApp(App)
应用实例必须在调用了 .mount()
方法后才会渲染出来。该方法接收一个"容器"参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串.页面挂载到index.html文件上的id=app元素上。
javascript
app.mount('#app')
html
<div id="app"></div>
在src
目录下的assets
文件夹的作用就是存放公共资源,例如:图片、公共CSS或者字体图标等.