chapter 4(组件间相互作用)
props将数据传给子组件
在 Vue.js 中,Props 是一种用于父组件向子组件传递数据的机制。通过 Props,父组件可以将数据传递给子组件,子组件可以通过定义 Props 来接收和使用这些数据。以下是关于 Props 的详细解释:
基本概念
Props(Properties)是 Vue 组件中用于接收外部数据的接口。父组件可以通过 Props 将数据传递给子组件,子组件通过定义 Props 来接收这些数据并使用它们。
Vue将通过props传递给子组件的数据视为只读和原始数据。单向数据流确保父组件是唯一可以更新数据属性的组件。
使用场景
- 数据传递: 父组件向子组件传递数据。
- 组件复用: 通过 Props 使组件更加通用和可复用。
- 状态管理: 通过 Props 管理组件之间的状态。
基本用法
1. 定义 Props
在子组件中使用 props
选项来定义可以接收的数据。
示例子组件 (ChildComponent.vue):
<template>
<div>
<p>项目名称: {{ projectName }}</p>
<p>项目编号: {{ projectNumber }}</p>
</div>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
projectName: {
type: String,
required: true
},
projectNumber: {
type: Number,
required: true
}
})
</script>
解释:
defineProps
是 Vue 3 的组合式 API 中用于定义 Props 的函数。projectName
和projectNumber
是定义的 Props,分别指定类型为String
和Number
,并且都是必需的。
2. 传递 Props
在父组件中通过属性将数据传递给子组件。
示例父组件 (ParentComponent.vue):
<template>
<div>
<ChildComponent projectName="项目A" :projectNumber="12345" />
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue'
</script>
解释:
<ChildComponent projectName="项目A" :projectNumber="12345" />
: 通过属性传递projectName
和projectNumber
给ChildComponent
。projectName
是字符串,直接传递。projectNumber
是数字,使用:
绑定(即v-bind
简写),确保传递的是数字类型。
Props 选项
在定义 Props 时,可以使用多种选项来控制 Props 的行为和验证。
常用选项
- type: 指定 Prop 的类型。
- required: 指定 Prop 是否必需。
- default: 指定 Prop 的默认值。
- validator: 自定义验证函数,用于验证 Prop 的值。
声明组件的props的另一种方法是使用一个字符串数组,每个字符串表示它接受的props的名称,例如props:[“name”,“price”]
。当您想要快速原型化组件时,这种方法是实用的。但是,我强烈建议您使用props的对象形式,并使用类型声明所有props,这是代码可读性和bug预防的良好实践。
emit将数据传给父组件
在 Vue.js 中,$emit
是一个用于子组件向父组件传递数据或事件的重要机制。通过 $emit
,子组件可以触发自定义事件,并将数据传递给父组件。以下是详细的解释和示例。
基本概念
$emit 是 Vue 组件实例的一个方法,用于触发自定义事件。父组件可以通过监听这些自定义事件来响应子组件的事件,并执行相应的操作。
它返回一个函数实例,我们可以使用它来调用组件中的特定事件:
emits('component-event', [...arguments])
使用场景
- 子组件向父组件传递数据: 例如,用户在子组件中点击按钮,子组件通过
$emit
将数据传递给父组件。 - 触发父组件的方法: 子组件可以触发父组件的方法,从而实现父组件的行为。
- 通知父组件状态变化: 子组件的状态变化可以通过
$emit
通知父组件,父组件可以根据这些变化执行相应的逻辑。
基本用法
1. 子组件触发自定义事件
在子组件中使用 $emit
触发自定义事件,并传递数据。
示例:
子组件 (ChildComponent.vue):
<template>
<div>
<h2>子组件</h2>
<button @click="sendMessage">发送消息</button>
</div>
</template>
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['message-sent'])
const sendMessage = () => {
emit('message-sent', 'Hello from Child')
}
</script>
解释:
- 定义事件: 使用
defineEmits(['message-sent'])
定义一个名为message-sent
的自定义事件。 - 触发事件: 在
sendMessage
方法中使用emit('message-sent', 'Hello from Child')
触发message-sent
事件,并传递消息'Hello from Child'
。
2. 父组件监听自定义事件
在父组件中使用 v-on
或简写的 @
监听子组件触发的自定义事件,并执行相应的操作。
示例:
父组件 (ParentComponent.vue):
<template>
<div>
<h1>父组件</h1>
<ChildComponent @message-sent="handleMessage" />
<p>收到的消息: {{ receivedMessage }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const receivedMessage = ref('')
const handleMessage = (message) => {
receivedMessage.value = message
}
</script>
解释:
- 监听事件: 使用
@message-sent="handleMessage"
监听子组件触发的message-sent
事件。 - 处理事件: 在
handleMessage
方法中接收传递的消息,并更新receivedMessage
。
详细步骤
- 在子组件中定义事件:
- 使用
defineEmits
定义需要触发的自定义事件。
- 在子组件中触发事件:
- 使用
emit
方法触发自定义事件,并传递数据。
- 在父组件中监听事件:
- 使用
v-on
或简写的@
监听子组件触发的自定义事件。 - 定义处理事件的方法,并在方法中处理传递的数据。
provide和inject实现祖先和后代通信
在 Vue.js 中,provide
和 inject
是一种用于在祖先组件和后代组件之间传递数据和方法的机制。这种方式特别适用于需要在多个嵌套层级的组件之间共享数据或方法的场景。以下是详细的解释和示例。
基本概念
provide 和 inject 是 Vue 提供的依赖注入系统,允许祖先组件向其所有后代组件提供数据或方法,而不需要通过 props 逐层传递。
使用场景
- 共享数据: 在多个嵌套层级的组件之间共享数据,避免通过 props 逐层传递。
- 共享方法: 在多个组件之间共享方法,例如全局配置、主题设置等。
- 全局状态管理: 在大型应用中,通过
provide
和inject
管理全局状态,减少组件间的耦合。
基本用法
1. 在祖先组件中提供数据或方法
使用 provide
在祖先组件中提供数据或方法。
示例:
祖先组件 (AncestorComponent.vue):
<template>
<div>
<h1>祖先组件</h1>
<ChildComponent />
</div>
</template>
<script setup>
import { provide, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const sharedData = ref('共享的数据')
const sharedMethod = () => {
console.log('共享的方法被调用了')
}
provide('sharedData', sharedData)
provide('sharedMethod', sharedMethod)
</script>
解释:
- 定义数据和方法: 使用
ref
定义响应式数据sharedData
和普通方法sharedMethod
。 - 提供数据和方法: 使用
provide
将sharedData
和sharedMethod
提供给所有后代组件。
2. 在后代组件中注入数据或方法
使用 inject
在后代组件中注入祖先组件提供的数据或方法。
示例:
后代组件 (ChildComponent.vue):
<template>
<div>
<h2>后代组件</h2>
<p>共享的数据: {{ sharedData }}</p>
<button @click="sharedMethod">调用共享方法</button>
</div>
</template>
<script setup>
import { inject } from 'vue'
const sharedData = inject('sharedData')
const sharedMethod = inject('sharedMethod')
</script>
解释:
- 注入数据和方法: 使用
inject
注入祖先组件提供的sharedData
和sharedMethod
。 - 使用数据和方法: 在模板中使用
sharedData
和sharedMethod
。
详细步骤
- 在祖先组件中提供数据或方法:
- 使用
provide
方法提供数据或方法。
- 在后代组件中注入数据或方法:
- 使用
inject
方法注入祖先组件提供的数据或方法。
使用 provide
和 inject
的注意事项
- 命名冲突: 确保提供的键名是唯一的,避免命名冲突。
- 响应式数据: 提供的数据应该是响应式的(例如使用
ref
或reactive
),以便后代组件能够响应数据的变化。 - 组件层级:
provide
和inject
只能在祖先组件和后代组件之间使用,不能在兄弟组件之间使用。
总结
- 提供数据和方法: 使用
provide
在祖先组件中提供数据或方法。 - 注入数据和方法: 使用
inject
在后代组件中注入祖先组件提供的数据或方法。 - 响应式数据: 提供的数据应该是响应式的,以便后代组件能够响应数据的变化。
- 组件层级:
provide
和inject
只能在祖先组件和后代组件之间使用。
teleport
基本概念
Teleport 允许你将组件的内容“传送”到 DOM 中的另一个位置。这使得你可以在不改变组件的逻辑结构的情况下,将部分内容渲染到页面的其他位置。
使用场景
- 模态框和弹出框: 将模态框渲染到
body
元素中,避免被父组件的样式限制。 - 全局提示信息: 将提示信息渲染到
body
中,确保它们在页面的最上层显示。 - 复杂布局: 将某些内容渲染到特定的 DOM 节点,而不影响组件的嵌套结构。
详细步骤
- 定义 Teleport 目标:
- 在 HTML 中定义一个目标元素,例如
<div id="modal-root"></div>
。
- 使用 Teleport:
- 在组件中使用
<teleport to="#modal-root">
将内容渲染到目标元素中。
- 样式和逻辑:
- 确保模态框的样式和逻辑正确,以便在目标位置正确显示和工作。
基本用法
House.vue
<template>
<div>
This is a house
</div>
<Teleport to="#sky">
<div>Sky and clouds</div>
</Teleport>
</template>
<template>
<section id="sky" />
<section class="wrapper">
<House />
</section>
</template>
结果
chapter5 (响应式函数)
ref
ref
是 Vue 3 中用于创建响应式数据的函数。它允许你将一个普通的 JavaScript 值转换为响应式对象,从而在数据变化时自动更新视图。
1. ref
作用
- 创建响应式数据:将一个普通的 JavaScript 值(如字符串、数字、对象等)转换为响应式对象。
- 响应式更新:当响应式数据发生变化时,Vue 会自动更新视图中依赖该数据的部分。
- 访问和修改:通过
.value
属性访问和修改ref
创建的响应式数据。
2. ref
用法
- 创建响应式数据:
import { ref } from 'vue'
const count = ref(0)
3. ref
的注意事项
- 访问和修改:
- 对于
ref
创建的响应式数据,必须通过.value
属性访问和修改。 - 示例:
console.log(count.value) // 访问 count.value++ // 修改
- 模板中的使用:
- 在模板中可以直接使用
ref
创建的响应式数据,Vue 会自动处理.value
。 - 示例:
<template> <p>计数器: {{ count }}</p> </template>
- 解构赋值:
- 如果需要解构
ref
创建的响应式数据,可以使用toRefs
或toRef
。 - 示例:
import { ref, toRefs } from 'vue' const user = ref({ name: 'Alice', age: 25 }) const { name, age } = toRefs(user)
4.shallowRef
shallowRef
是 Vue 3 中用于创建浅层响应式的引用(reference)。与 ref
不同,shallowRef
只会将引用本身转换为响应式对象,而不会递归地将引用对象的内部属性转换为响应式。这在处理大型对象或性能敏感的场景中非常有用。
1. shallowRef
作用
- 创建浅层响应式数据:将一个普通的 JavaScript 值转换为响应式对象,但不会递归地将对象的内部属性转换为响应式。
- 性能优化:适用于大型对象或不需要深层响应式的场景,可以减少不必要的性能开销。
- 访问和修改:通过
.value
属性访问和修改值。
2. shallowRef
用法
- 创建浅层响应式数据:
import { shallowRef } from 'vue'
const user = shallowRef({
name: 'Alice',
age: 25
})
3. ref
与 shallowRef
的区别
特性 | ref | shallowRef |
---|---|---|
响应式深度 | 递归地将对象的所有属性转换为响应式。 | 只将引用本身转换为响应式,不会递归地将对象的内部属性转换为响应式。 |
适用场景 | 适用于需要深层响应式的对象。 | 适用于大型对象或不需要深层响应式的场景,可以减少不必要的性能开销。 |
访问和修改 | 通过 .value 属性访问和修改值。 | 通过 .value 属性访问和修改值。 |
模板中的使用 | 在模板中可以直接使用 ref 创建的响应式数据,Vue 会自动处理 .value 。 | 在模板中可以直接使用 shallowRef 创建的响应式数据,Vue 会自动处理 .value 。 |
示例:ref
与 shallowRef
的对比
使用 ref
import { ref } from 'vue'
const user = ref({
name: 'Alice',
age: 25
})
const changeName = () => {
user.value.name = 'Bob'
}
const changeAge = () => {
user.value.age = 30
}
使用 shallowRef
import { shallowRef } from 'vue'
const user = shallowRef({
name: 'Alice',
age: 25
})
const changeName = () => {
user.value.name = 'Bob'
}
const changeAge = () => {
user.value.age = 30
}
详细说明
ref
:user
对象的所有属性(name
和age
)都是响应式的。- 当
user.value.name
或user.value.age
发生变化时,Vue 会自动更新视图。 shallowRef
:user
对象本身是响应式的,但其内部属性(name
和age
)不是响应式的。- 当
user.value
被重新赋值时,Vue 会自动更新视图。 - 但直接修改
user.value.name
或user.value.age
不会触发视图更新。
reactive
reactive
是 Vue 3 中用于创建响应式对象的函数。它将一个普通的 JavaScript 对象转换为响应式对象,使得对象的属性在变化时能够自动触发视图更新。
1. reactive
作用
- 创建响应式对象:将一个普通的 JavaScript 对象转换为响应式对象。
- 深层响应式:递归地将对象的所有属性转换为响应式,包括嵌套的对象和数组。
- 访问和修改:直接访问和修改对象的属性,Vue 会自动处理响应式更新。
2. reactive
用法
- 创建响应式对象:
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'Alice',
age: 25
}
})
- 访问响应式对象:
console.log(state.count) // 输出: 0
console.log(state.user.name) // 输出: Alice
3. reactive
与 ref
的区别
ref
:- 用于创建单个响应式数据。
- 可以用于任何类型的值(字符串、数字、对象等)。
- 通过
.value
属性访问和修改值。 reactive
:- 用于创建对象类型的响应式数据。
- 只能用于对象。
- 直接访问和修改对象的属性。
特性 | reactive | ref |
---|---|---|
数据类型 | 仅适用于对象(包括普通对象、数组等)。 | 适用于任何类型的值(字符串、数字、对象、数组等)。 |
访问和修改 | 直接访问和修改对象的属性,无需使用 .value 。 | 通过 .value 属性访问和修改值。 |
响应式深度 | 递归地将对象的所有属性转换为响应式。 | 仅将引用本身转换为响应式,对于对象类型的值,需要通过 .value 访问内部属性。 |
模板中的使用 | 在模板中可以直接使用 reactive 创建的响应式对象的属性。 | 在模板中可以直接使用 ref 创建的响应式数据,Vue 会自动处理 .value 。 |
适用场景 | 适用于需要深层响应式的对象。 | 适用于需要创建单个响应式数据的场景,例如计数器、表单字段等。 |
示例:ref
与 reactive
的对比
使用 ref
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
使用 reactive
import { reactive } from 'vue'
const state = reactive({
count: 0
})
const increment = () => {
state.count++
}
const decrement = () => {
state.count--
}
4. reactive
的注意事项
- 数据类型:
reactive
仅适用于对象(包括普通对象、数组等)。- 对于基本类型(如字符串、数字),应使用
ref
。
- 访问和修改:
- 对于
reactive
创建的响应式对象,直接访问和修改对象的属性。 - 示例:
console.log(state.count) // 访问 state.count++ // 修改
- 模板中的使用:
- 在模板中可以直接使用
reactive
创建的响应式对象的属性。 - 示例:
<template> <p>计数器: {{ state.count }}</p> </template>
- 响应式深度:
reactive
递归地将对象的所有属性转换为响应式。- 如果需要浅层响应式,应使用
shallowReactive
。
组合式API中的生命周期函数
使用Composition API,生命周期钩子是我们需要从vue包中导入的单独函数,然后才能在组件生命周期的特定点使用它们执行逻辑。Composition API的生命周期挂钩与Options API中的生命周期挂钩类似,只是语法现在包含前缀on(例如,在Composition API中,mounted变为onMounted)。表中显示了一些生命周期钩子从选项API到组合API的映射。