含 Vue 3 核心知识点的几个页面示例

发布日期: 2025-10-02
标签: Vue3, 教程, CompositionAPI

我们将使用 <script setup> 格式来构建一个包含三个主要页面的小型应用,每个页面都侧重于 Vue 的核心知识点。

为了避免引入复杂的路由库,本示例在 App.vue 中实现了简单的状态管理和条件渲染来模拟页面切换,让你可以专注于 Vue 3 本身的特性。

我们将创建以下五个文件,并展示其核心代码:

  1. App.vue: 应用程序的入口和导航。
  2. DataBinding.vue: 演示响应式数据(ref, reactive, computed, watch)。
  3. ChildComponent.vue: 一个可重用组件,用于演示组件通信。
  4. ComponentsAndSlots.vue: 父组件,演示组件通信(props, emits)和插槽(slots)。
  5. LifecycleAndDirectives.vue: 演示生命周期钩子(onMounted)和内置指令(v-if, v-for, v-bind, v-model)。

1. App.vue: 应用入口和动态组件

这是应用程序的根组件,负责导航和使用 <component :is="..."> 动态加载其他“页面”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<script setup>
import DataBinding from './DataBinding.vue';
import ComponentsAndSlots from './ComponentsAndSlots.vue';
import LifecycleAndDirectives from './LifecycleAndDirectives.vue';

import { ref } from 'vue';

// 页面切换的状态
const currentPage = ref('DataBinding');

// 存储所有组件的映射
const views = {
DataBinding,
ComponentsAndSlots,
LifecycleAndDirectives
};

// 简单的行内样式 (为保持简洁)
const navStyle = { /* ... 样式定义 ... */ };
const linkStyle = { /* ... 样式定义 ... */ };
const activeLinkStyle = { /* ... 样式定义 ... */ };
</script>

<template>
<div style="font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px;">
<nav :style="navStyle">
<div
:style="[linkStyle, currentPage === 'DataBinding' && activeLinkStyle]"
@click="currentPage = 'DataBinding'"
>
1. 响应式与计算属性
</div>
<div
:style="[linkStyle, currentPage === 'ComponentsAndSlots' && activeLinkStyle]"
@click="currentPage = 'ComponentsAndSlots'"
>
2. 组件通信与插槽
</div>
<div
:style="[linkStyle, currentPage === 'LifecycleAndDirectives' && activeLinkStyle]"
@click="currentPage = 'LifecycleAndDirectives'"
>
3. 生命周期与指令
</div>
</nav>

<div style="margin-top: 20px; border: 1px solid #ccc; padding: 20px; border-radius: 8px;">
<component :is="views[currentPage]" />
</div>
</div>
</template>

2. DataBinding.vue: 响应式数据与计算属性

这个组件演示了 Vue 3 的核心响应式 API:refreactivecomputedwatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<script setup>
import { ref, reactive, computed, watch } from 'vue';

// --- 1. ref (处理原始值) ---
const count = ref(0);
function increment() {
count.value++; // 访问 ref 时需要 .value
}

// --- 2. reactive (处理对象) ---
const product = reactive({
item: 'T恤',
price: 50,
quantity: 2
});

// --- 3. computed (计算属性) ---
const totalPrice = computed(() => {
return product.price * product.quantity; // 自动依赖 product.price 和 product.quantity
});

// --- 4. watch (监听器) ---
const message = ref('');
// 监听 count 的变化
watch(count, (newCount, oldCount) => {
message.value = `计数从 ${oldCount} 变更为 ${newCount}。`;
});
</script>

<template>
<div>
<h2>1. 响应式数据与计算属性</h2>

<div>
<h3>ref 演示</h3>
<p>当前的计数是: <strong>{{ count }}</strong></p>
<button @click="increment">增加计数</button>
<p>**Watch 监听消息:** {{ message }}</p>
</div>

<div>
<h3>reactive 与 computed 演示</h3>
<p>数量: <strong>{{ product.quantity }}</strong></p>
<button @click="product.quantity++">增加数量</button>
<h4>总价格 (Computed): ${{ totalPrice }}</h4>
</div>
</div>
</template>

3. ChildComponent.vue:可重用子组件

这是一个子组件,用于演示 defineProps(接收数据)、defineEmits(发送事件)和 <slot>(内容注入)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<script setup>
import { ref } from 'vue';

// --- 1. 定义 Props ---
const props = defineProps({
initialMessage: { type: String, required: true },
userName: String
});

// --- 2. 定义 Emits ---
const emit = defineEmits(['updateName']);

const newNameInput = ref('');

function updateParentName() {
if (newNameInput.value) {
// 触发事件并传递数据
emit('updateName', newNameInput.value);
newNameInput.value = '';
}
}
</script>

<template>
<div>
<h4>子组件 (ChildComponent)</h4>

<p>父组件传递的消息 (Props): <strong>{{ props.initialMessage }}</strong></p>

<div>
<h5>默认插槽内容:</h5>
<slot></slot>
</div>

<div>
<h5>命名插槽 (footer):</h5>
<slot name="footer"></slot>
</div>

<div>
<input v-model="newNameInput" placeholder="输入新名称">
<button @click="updateParentName">发送新名称给父组件 (Emit)</button>
</div>
</div>
</template>

4. ComponentsAndSlots.vue:组件通信父组件

这个父组件展示了如何使用 ChildComponent.vue,实现 Props 传递监听 Emits注入 Slots 的完整流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

// 父组件的状态
const parentMessage = ref('这是父组件传递给子组件的问候。');
const currentUserName = ref('张三');

// --- 接收子组件事件 (Emits) ---
function handleNameUpdate(newName) {
currentUserName.value = newName;
console.log(`父组件已接收到新名称: ${newName}`);
}
</script>

<template>
<div>
<h2>2. 组件通信 (Props, Emits) 与 插槽 (Slots)</h2>

<div>
<h3>父组件区域</h3>
<p>父组件的用户名状态: <strong>{{ currentUserName }}</strong></p>

<ChildComponent
:initial-message="parentMessage"
:user-name="currentUserName"
@update-name="handleNameUpdate"
>
<p style="color: green;">
**这是通过默认插槽注入的内容。**
</p>

<template #footer>
<small style="color: blue;">
**这是通过 #footer 插槽注入的脚注。**
</small>
</template>

</ChildComponent>
</div>
</div>
</template>

5. LifecycleAndDirectives.vue:生命周期与内置指令

这个组件演示了常用的内置指令和 onMounted 生命周期的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<script setup>
import { ref, onMounted } from 'vue';

// --- 1. 生命周期钩子 (onMounted) ---
onMounted(() => {
console.log('--- LifecycleAndDirectives 组件已挂载到 DOM ---');
});

// --- 2. v-if ---
const showBox = ref(true);

// --- 3. v-for ---
const items = ref([
{ id: 1, text: '学习指令' },
{ id: 2, text: '理解 Props/Emits' },
]);

// --- 4. v-model (双向绑定) ---
const userInput = ref('初始绑定文本');

// --- 5. v-bind (动态属性) ---
const isDisabled = ref(false);
</script>

<template>
<div>
<h2>3. 生命周期钩子与内置指令</h2>

<div>
<h3>生命周期钩子: onMounted</h3>
<p>请查看**控制台**的日志消息。</p>
</div>

<div>
<h3>条件渲染: v-if</h3>
<button @click="showBox = !showBox">
{{ showBox ? '隐藏 (v-if)' : '显示 (v-if)' }}
</button>
<p v-if="showBox">
✅ 我是 v-if 渲染的内容。
</p>
<p v-else>
❌ 我是 v-else 渲染的内容。
</p>
</div>

<div>
<h3>列表渲染: v-for</h3>
<ul>
<li v-for="item in items" :key="item.id">
ID: {{ item.id }} - 任务: <strong>{{ item.text }}</strong>
</li>
</ul>
</div>

<div>
<h3>双向绑定: v-model</h3>
<input type="text" v-model="userInput">
<p>你输入的内容是: <strong>{{ userInput }}</strong></p>
</div>

<div>
<h3>属性绑定: v-bind (简写为 :)</h3>
<button :disabled="isDisabled" @click="isDisabled = !isDisabled">
{{ isDisabled ? '已禁用' : '点击禁用' }}
</button>
</div>
</div>
</template>

这些文件共同构成了一个基础的 Vue 3 应用,清晰地展示了 响应式系统、组件通信和常用指令/钩子。你可以在 App.vue 中点击导航链接,切换查看不同知识点的演示效果。


这个整理后的版本删除了开头不相关的错误信息,并且使用 Markdown 的标题和代码块更好地组织了内容,让读者可以更清晰地理解每个文件对应的知识点。

你觉得这个格式可以吗?接下来你打算发布这篇文章还是需要进一步修改?