代码仓库

gitee

创建项目

首先保证安装了node, 然后使用vite创建项目

vite

1
2
3
npm create vite vue-learn
cd vue-learn
npm i

进入项目, 把hello组件删了

然后清空app.vue的内容, 改为

1
2
3
4

<template>
<div>hello world</div>
</template>

运行

1
npm run dev

Option 选项式风格

vue2和vue3两个版本有比较大的变化, 一般来说, vue2用的是选项式风格, vue3用的是组合式风格

data 响应式数据

先看非响应式数据

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

<template>
<div>
<div id="counter">count</div>
<button id="btn">+1</button>
</div>
</template>
<script>
export default {
// 这个是生命周期函数, 现在先不管, 这里用到是因为在mounted中可以拿到dom元素
mounted() {
// 非响应式数据
let count = 0;
// 将count渲染
const counter = document.getElementById("counter");
counter.innerHTML = count
// 改变count的方法, 注意改变后页面是否变化
const btn = document.getElementById("btn")
btn.addEventListener("click", () => {
count += 1
console.log(count)
})
}
}
</script>

此时, 当你点击按钮, 页面不会变化, 但是可以看到count是在增加的

为了让页面和数据同步变化, 我们需要用到响应式数据, 可以通过data来定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<template>
<div>
<div id="counter">{{ count }}</div>
<button id="btn" @click="count += 1">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
}
</script>

此时再打开页面, 点击按钮, 会发现count的值会同步增加

模板语法

插值

1
{{}}

是vue的插值语法, 可以将数据渲染到页面中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<template>
<div>{{ text }}</div>
</template>

<script>
export default {
name: "O1_template-grammar",
data() {
return {
text: "插值语法"
}
}
}
</script>

指令

内容渲染

v-text v-html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<template>
<div>
<p v-text="textContent"></p>
<p v-html="htmlContent"></p>
</div>
</template>

<script>
export default {
data() {
return {
textContent: "这是v-text渲染的内容",
htmlContent: "<strong>这是v-html渲染的<strong>HTML</strong>内容</strong>"
}
}
}
</script>

属性绑定

v-bind :attrName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<template>
<div>
<img v-bind:src="imageUrl" alt="图片">
<a :href="linkUrl">点击跳转</a>
</div>
</template>

<script>
export default {
data() {
return {
imageUrl: "https://raw.githubusercontent.com/malred/malcode-site-photos/master/img/getqrcode.png",
linkUrl: "https://malcode-site.github.io/",
}
}
}
</script>

事件绑定

v-on @eventName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<template>
<div>
<button v-on:click="()=>{console.log('v-on')}">点击(v-on)</button>
<button @click="()=>{console.log('@')}">点击(@简写)</button>
</div>
</template>

<script>
export default {
data() {
return {
message: ""
}
}
}
</script>

表单双向绑定

v-model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<template>
<div>
<input type="text" v-model="inputValue">
<p>输入框的值是:{{ inputValue }}</p>
</div>
</template>

<script>
export default {
data() {
return {
inputValue: ""
}
}
}
</script>

条件渲染

v-if v-else v-else-if v-show

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<template>
<div>
<p v-if="isVisible">内容显示了(v-if)</p>
<p v-show="isVisible">内容显示了(v-show)</p>
<button @click="()=>this.isVisible = !this.isVisible">切换可见性</button>
</div>
</template>

<script>
export default {
data() {
return {
isVisible: true
}
},
}
</script>

列表渲染

v-for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<template>
<div>
<ul>
<li v-for="(item, index) in items" :key="index">
{{ item }}
</li>
</ul>
</div>
</template>

<script>
export default {
data() {
return {
items: ["苹果", "香蕉", "橙子"]
}
}
}
</script>

computed 计算属性

  • 说明
    computed 是 Vue 提供的用于声明 依赖响应式数据 的计算属性。它会基于其依赖缓存结果,只有当依赖的数据发生变化时才会重新计算。
  • 应用场景
    • 处理模板中复杂的逻辑表达式(如拼接字符串、过滤、格式化等)。
    • 需要根据多个响应式数据派生出新值,且希望避免重复计算。
    • 模板中不适合直接写复杂逻辑,可以使用 computed 将处理后的值返回给模板。

案例:根据姓氏和名字拼接全名

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

<template>
<div>
<input v-model="firstName" placeholder="输入姓">
<input v-model="lastName" placeholder="输入名">
<p>全名: {{ fullName }}</p>
</div>
</template>

<script>
export default {
data() {
return {
firstName: "",
lastName: ""
}
},
computed: {
fullName() {
return this.firstName + " " + this.lastName;
}
}
}
</script>

watch 监听

  • 说明
    watch 允许你监听某个响应式数据的变化,并在数据变化时执行副作用操作(如异步请求、数据清洗等)。
  • 应用场景
    • 当数据变化时需要执行异步或开销较大的操作(如调用 API 获取数据)。
    • 监听特定字段的变化并做出相应处理。
    • 深度监听对象/数组内部值的变化(配合 deep: true)。
    • 监听路由变化时更新页面内容。

案例: 搜索框输入时触发 API 请求

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

<template>
<div>
<input v-model="searchQuery" placeholder="输入关键词搜索">
<p>正在搜索: {{ searchQuery }}</p>
</div>
</template>

<script>
export default {
data() {
return {
searchQuery: ""
}
},
watch: {
searchQuery(newVal) {
if (newVal) {
console.log("调用API搜索", newVal);
// 这里可以替换为真实的请求
// fetch(`/api/search?q=${newVal}`)
}
}
}
}
</script>

methods 方法

  • 说明
    methods 用于定义可以在模板中通过事件绑定或者直接调用的方法。这些方法通常用于处理用户交互、数据转换等操作。
  • 应用场景
    • 绑定按钮点击等事件触发的动作。
    • 数据处理、格式化输出等非响应式计算。
    • 与用户行为相关的函数调用。

案例: 点击按钮计数并显示次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

<template>
<div>
<button @click="increment">+1</button>
<p>点击次数: {{ count }}</p>
</div>
</template>

<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++;
}
}
}
</script>

lifecycle 生命周期

  • 说明
    Vue 实例从创建到销毁的过程称为生命周期。Vue 提供了多个钩子函数,允许你在不同阶段执行特定逻辑。
  • 常见生命周期钩子及应用场景
钩子函数 触发时机 应用场景示例
beforeCreate 实例初始化之后,数据观测之前 很少使用
created 实例已经创建完成,数据已准备就绪 初始化数据、调用接口获取数据
beforeMount 挂载开始之前 几乎不用
mounted 挂载完成后 操作 DOM、第三方库初始化、获取元素尺寸
beforeUpdate 数据更新前 优化性能、记录状态
updated 数据更新后 DOM 更新后的操作
beforeUnmount 实例销毁前 清除定时器、解绑事件
unmounted 实例销毁后 资源释放
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

<template>
<div>
<h2>生命周期演示组件</h2>
<p>组件加载时间:{{ loadedTime }} 秒</p>
</div>
</template>

<script>
export default {
data() {
return {
loadedTime: 0,
showContent: true,
timer: null
};
},
beforeCreate() {
console.log("🚀 beforeCreate: 实例初始化完成,但数据和方法还未初始化");
},
created() {
console.log("✅ created: 数据已加载,可以开始调用接口或初始化数据");
// 模拟异步请求
setTimeout(() => {
console.log("模拟 API 请求完成");
}, 1000);
},
beforeMount() {
console.log("🧭 beforeMount: 模板编译完成,但尚未挂载到 DOM");
},
mounted() {
console.log("🎉 mounted: 组件已渲染到页面,可以操作 DOM 或启动定时器");
this.timer = setInterval(() => {
this.loadedTime += 1;
}, 1000);
},
beforeUpdate() {
console.log("🔄 beforeUpdate: 数据即将更新,DOM 还未更新");
},
updated() {
console.log("✅ updated: 数据和 DOM 已同步更新");
},
beforeUnmount() {
console.log("🛑 beforeUnmount: 组件即将销毁,清理定时器、事件监听等资源");
clearInterval(this.timer);
},
unmounted() {
console.log("👋 unmounted: 组件已销毁,所有绑定解除");
}
};
</script>

components 组件

组件传值

父传子

父组件通过 props 向子组件传递数据,是单向数据流。

父组件 Parent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<template>
<div>
<h2>父组件</h2>
<Child :message="parentMsg"/>
</div>
</template>

<script>
import Child from './Child.vue';

export default {
components: {Child},
data() {
return {
parentMsg: '这是来自父组件的消息'
};
}
};
</script>

子组件 Child.vue

1
2
3
4
5
6
7
8
9
10
11
12
13

<template>
<div>
<p>子组件接收到消息:{{ message }}</p>
</div>
</template>

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

子传父

子组件通过 $emit 触发事件,将数据发送给父组件。

父组件 Parent.vue

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

<template>
<div>
<h2>父组件</h2>
<Child @from-child="handleFromChild"/>
<p>接收到来自子组件的消息:{{ childMsg }}</p>
</div>
</template>

<script>
import Child from './Child.vue';

export default {
components: {Child},
data() {
return {
childMsg: ''
};
},
methods: {
handleFromChild(msg) {
this.childMsg = msg;
}
}
};
</script>

子组件 Child.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<template>
<div>
<button @click="sendToParent">点击发送给父组件</button>
</div>
</template>

<script>
export default {
methods: {
sendToParent() {
this.$emit('from-child', '你好,我是子组件');
}
}
};
</script>

兄弟传值

通过一个公共的“事件总线”或 Vuex 状态管理实现非父子组件之间的通信。

创建 event-bus.js (vue2才支持这种写法, vue3的话我在参考代码里实现了简易的方案)

1
2
3
4
5
// event-bus.js
import Vue from 'vue';

export const EventBus = new Vue();

兄弟组件 A - Sender.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<template>
<div>
<button @click="sendMessage">发送消息给兄弟组件</button>
</div>
</template>

<script>
import {EventBus} from '../event-bus.js';

export default {
methods: {
sendMessage() {
EventBus.$emit('brother-msg', '来自组件A的消息');
}
}
};
</script>

兄弟组件 B - Receiver.vue

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

<template>
<div>
<p>接收到兄弟组件的消息:{{ msg }}</p>
</div>
</template>

<script>
import {EventBus} from '../event-bus.js';

export default {
data() {
return {
msg: ''
};
},
mounted() {
EventBus.$on('brother-msg', (msg) => {
this.msg = msg;
});
},
beforeUnmount() {
EventBus.$off('brother-msg');
}
};
</script>

跨级传值

祖先组件通过 provide 提供数据,后代组件通过 inject 接收,无需逐层传递。

祖先组件 GrandParent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

<template>
<div>
<h2>祖组件</h2>
<Parent/>
</div>
</template>

<script>
import Parent from './Parent.vue';

export default {
components: {Parent},
provide() {
return {
theme: 'dark',
appName: 'MyApp'
};
}
};
</script>

中间组件 Parent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<template>
<div>
<h3>中间组件</h3>
<Child/>
</div>
</template>

<script>
import Child from './Child.vue';

export default {
components: {Child}
};
</script>

子组件 Child.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<template>
<div>
<p>从祖组件注入的主题:{{ theme }}</p>
<p>应用名称:{{ appName }}</p>
</div>
</template>

<script>
export default {
inject: ['theme', 'appName']
};
</script>

Composition 组合式风格

Vue 3 引入了 组合式 API(Composition API),它提供了一种更灵活、更可复用的方式来组织逻辑代码,特别适合大型项目和逻辑复用场景。

🧩 基本概念

  • 与 Vue 2 的 Option API 不同,Composition API 更偏向函数式编程。
  • 使用 setup() 函数替代 data、methods、created 等选项。
  • 可以使用 ref、reactive、computed、watch 等函数来管理响应式数据。
  • 支持自定义逻辑封装(即 Custom Hooks)。

📦 setup() 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<script>
import {ref} from 'vue';

export default {
setup() {
const count = ref(0);

function increment() {
count.value++;
}

return {count, increment};
}
}
</script>

vue3还提供了setup语法糖(Syntactic Sugar),用于更简洁地使用 组合式 API(Composition
API)。它极大地简化了组件的定义方式,让开发者可以更直观、更高效地编写响应式逻辑。

在这个模式下,你无需显式导出 setup() 函数或返回模板中使用的变量。
所有在 setup 中声明的变量和函数都可以直接在template 中使用。

1
2
3
4
5
6
7
8

<script setup>
const count = ref(0);

function increment() {
count.value++;
}
</script>

🔁 ref 和 reactive

类型|用途说明|
ref|创建基本类型响应式数据(如 number、string)|
reactive|创建对象或数组的响应式引用|

1
2
const count = ref(0);
const user = reactive({name: '张三', age: 25});

🧠 computed 计算属性

1
2
3
import {computed} from 'vue';

const fullName = computed(() => firstName.value + ' ' + lastName.value);

👀 watch 监听器

1
2
3
4
5
6
import {watch} from 'vue';

watch(count, (newVal, oldVal) => {
console.log('count changed:', newVal);
});

⏱ 生命周期钩子

Vue 3 的组合式 API 提供了对应的生命周期钩子函数:

选项式钩子 组合式写法
beforeCreate ❌ 不再需要
created ❌ 不再需要
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
1
2
3
4
5
6
import {onMounted} from 'vue';

onMounted(() => {
console.log('组件挂载完成');
});

组件传值

这个部分可以使用pinia/vuex等状态管理库来实现, 了解即可

🧱 一、父组件 → 子组件(props)

使用 defineProps 接收父组件传递的数据。

父组件 Parent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<template>
<div>
<h2>父组件</h2>
<Child :message="parentMsg"/>
</div>
</template>

<script setup>
import {ref} from 'vue';
import Child from './Child.vue';

const parentMsg = ref('这是来自父组件的消息');
</script>

子组件 Child.vue

1
2
3
4
5
6
7
8
9
10
11
12

<template>
<div>
<p>子组件接收到消息:{{ message }}</p>
</div>
</template>

<script setup>
// 定义 props
const props = defineProps(['message']);
</script>

🔁 二、子组件 → 父组件(emit / defineEmits)

使用 defineEmits 触发事件通知父组件。
父组件 Parent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<template>
<div>
<h2>父组件</h2>
<Child @from-child="handleFromChild"/>
<p>接收到来自子组件的消息:{{ childMsg }}</p>
</div>
</template>

<script setup>
import {ref} from 'vue';
import Child from './Child.vue';

const childMsg = ref('');

function handleFromChild(msg) {
childMsg.value = msg;
}
</script>

子组件 Child.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<template>
<div>
<button @click="sendToParent">点击发送给父组件</button>
</div>
</template>

<script setup>
// 定义 emit 事件
const emit = defineEmits(['from-child']);

function sendToParent() {
emit('from-child', '你好,我是子组件');
}
</script>

👯‍♂️ 三、兄弟组件传值(EventBus / mitt)

vue3不支持vue2的eventbus, 可以通过共同的父组件来传递数据。

🌳 四、跨级传值(provide / inject)

使用 provide 向深层组件注入数据,inject 接收。

祖先组件 GrandParent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<template>
<div>
<h2>祖组件</h2>
<Parent/>
</div>
</template>

<script setup>
import {provide} from 'vue';
import Parent from './Parent.vue';

// 提供数据
provide('theme', 'dark');
provide('appName', 'MyApp');
</script>

中间组件 Parent.vue

1
2
3
4
5
6
7
8
9
10
11
12

<template>
<div>
<h3>中间组件</h3>
<Child/>
</div>
</template>

<script setup>
import Child from './Child.vue';
</script>

子组件 Child.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<template>
<div>
<p>从祖组件注入的主题:{{ theme }}</p>
<p>应用名称:{{ appName }}</p>
</div>
</template>

<script setup>
import {inject} from 'vue';

// 注入数据
const theme = inject('theme');
const appName = inject('appName');
</script>

社群

你可以在这些平台联系我:


本站总访问量