Vue3重点

Vue 3 面试题集

基础概念

1. Vue 3 相比 Vue 2 有哪些重大变化?

答案

  • 性能提升:Vue 3 重写了虚拟 DOM 实现,渲染性能提升约 1.3~2 倍,内存占用减少约 50%
  • Composition API:新增组合式 API,提供更灵活的逻辑组织和复用方式
  • TypeScript 支持:Vue 3 是用 TypeScript 重写的,提供了更好的类型推断
  • Teleport 组件:允许将组件的内容传送到 DOM 的其他位置
  • Fragments:组件可以有多个根节点
  • Suspense:处理异步组件的新特性
  • 响应式系统升级:使用 ES6 的 Proxy 代替 Object.defineProperty,解决了 Vue 2 中的数组和对象响应式问题
  • 全局 API 改为应用实例调用:减少了全局污染
  • 更好的 Tree-shaking 支持:减小打包体积

2. 什么是 Composition API?它解决了什么问题?

答案
Composition API 是 Vue 3 引入的一种新的组件逻辑组织方式,它允许我们按照功能/关注点组织代码,而不是按照选项(data、methods、computed 等)。

解决的问题

  • 逻辑复用:相比于 Vue 2 的 mixins,Composition API 提供了更清晰、更灵活的逻辑复用方式
  • 更好的类型推断:对 TypeScript 的支持更好
  • 代码组织:相关功能的代码可以放在一起,而不是分散在不同的选项中
  • 避免命名冲突:不同功能模块可以在各自的作用域中定义变量,避免了 mixins 中的命名冲突问题
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
// Vue 2 的 Options API
export default {
data() {
return {
user: { name: '', age: 0 },
posts: []
}
},
methods: {
fetchUserData() { /* ... */ },
fetchUserPosts() { /* ... */ }
},
computed: {
userFullName() { /* ... */ }
}
}

// Vue 3 的 Composition API
import { ref, computed, onMounted } from 'vue'

export default {
setup() {
// 用户相关逻辑
const user = ref({ name: '', age: 0 })
const fetchUserData = () => { /* ... */ }
const userFullName = computed(() => { /* ... */ })

// 文章相关逻辑
const posts = ref([])
const fetchUserPosts = () => { /* ... */ }

onMounted(() => {
fetchUserData()
fetchUserPosts()
})

return {
user,
posts,
userFullName,
fetchUserData,
fetchUserPosts
}
}
}

3. ref 和 reactive 有什么区别?什么情况下使用它们?

答案
ref 和 reactive 都是用于创建响应式数据的 API,但它们有以下区别:

ref

  • 可以包装任何类型的值(基本类型和对象类型)
  • 创建一个包含 .value 属性的响应式对象
  • 在模板中使用时会自动解包(不需要 .value
  • 适合处理基本类型值(如字符串、数字、布尔值)

reactive

  • 只能用于对象类型(包括数组和普通对象)
  • 直接返回原始对象的响应式代理
  • 不能用于基本类型值
  • 不能被重新赋值(会破坏响应性)

使用场景

  • 对于基本类型值(如 string、number、boolean),必须使用 ref
  • 对于复杂对象,可以使用 reactive 或 ref
  • 如果需要整体替换一个响应式对象,应该使用 ref
  • 如果只需要修改对象的属性,可以使用 reactive
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
import { ref, reactive } from 'vue'

// 使用 ref
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1

// 使用 reactive
const state = reactive({ count: 0 })
console.log(state.count) // 0
state.count++
console.log(state.count) // 1

// ref 可以包装对象
const user = ref({ name: 'John', age: 30 })
user.value.age++
// 或者整体替换
user.value = { name: 'Jane', age: 25 }

// reactive 不能重新赋值
const user2 = reactive({ name: 'John', age: 30 })
user2.age++
// 下面这样会破坏响应性
// user2 = { name: 'Jane', age: 25 } // 错误用法

4. Vue 3 中的生命周期钩子有哪些变化?

答案
Vue 3 中的生命周期钩子与 Vue 2 相比有以下变化:

  1. 命名变化

    • beforeCreatecreatedsetup() 函数本身替代
    • 其他钩子前缀改为 on,如 mounted 变为 onMounted
  2. 使用方式:在 Composition API 中,生命周期钩子作为函数导入并在 setup() 中调用

  3. 新增钩子

    • onRenderTracked:当组件渲染过程中追踪到响应式依赖时调用
    • onRenderTriggered:当响应式依赖触发组件重新渲染时调用
  4. 移除钩子

    • beforeDestroy 改名为 beforeUnmount
    • destroyed 改名为 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
import { 
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onActivated,
onDeactivated,
onErrorCaptured
} from 'vue'

export default {
setup() {
onBeforeMount(() => {
console.log('组件挂载前')
})

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

// 其他生命周期钩子...
}
}

5. 什么是 Teleport?它解决了什么问题?

答案
Teleport 是 Vue 3 新增的一个内置组件,它可以将组件的一部分 DOM 传送到组件 DOM 树之外的位置。

解决的问题

  • 解决了模态框、弹出菜单、通知等需要打破组件层次结构的 UI 元素的定位问题
  • 避免了 CSS 样式(如 z-index、overflow、position)带来的限制
  • 保持了组件的逻辑封装,同时允许 DOM 结构的灵活布局
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>
<button @click="showModal = true">打开模态框</button>

<!-- 使用 Teleport 将模态框传送到 body 下 -->
<teleport to="body">
<div v-if="showModal" class="modal">
<h2>模态框标题</h2>
<p>模态框内容...</p>
<button @click="showModal = false">关闭</button>
</div>
</teleport>
</div>
</template>

<script>
import { ref } from 'vue'

export default {
setup() {
const showModal = ref(false)
return { showModal }
}
}
</script>

响应式系统

6. Vue 3 的响应式系统是如何工作的?

答案
Vue 3 的响应式系统基于 ES6 的 Proxy 实现,主要工作流程如下:

  1. 创建响应式对象:通过 reactive()ref() 创建响应式对象
  2. 代理拦截:使用 Proxy 拦截对象的属性访问、修改等操作
  3. 依赖追踪:当组件渲染或计算属性计算时,会访问响应式对象的属性,此时系统会记录这些依赖关系
  4. 变更通知:当响应式对象的属性被修改时,Proxy 的 set 处理器会被触发,然后通知所有依赖于该属性的副作用(如组件重新渲染)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 简化版的响应式系统实现原理
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
// 追踪依赖
track(target, key)
const value = Reflect.get(target, key, receiver)
// 如果值是对象,则递归使其响应式
return typeof value === 'object' ? reactive(value) : value
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
// 触发更新
trigger(target, key)
}
return result
}
})
}

7. Vue 3 中如何实现计算属性?

答案
Vue 3 中使用 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
26
27
28
29
30
31
32
import { ref, computed } from 'vue'

export default {
setup() {
const firstName = ref('John')
const lastName = ref('Doe')

// 创建只读计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})

// 创建可写计算属性
const fullName2 = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(newValue) {
const names = newValue.split(' ')
firstName.value = names[0]
lastName.value = names[1] || ''
}
})

return {
firstName,
lastName,
fullName,
fullName2
}
}
}

计算属性具有以下特点:

  • 基于其响应式依赖进行缓存
  • 只有当依赖项变化时才会重新计算
  • 返回一个只读的响应式引用
  • 可以通过提供 get 和 set 函数创建可写的计算属性

8. watchEffect 和 watch 有什么区别?

答案
watchEffectwatch 都用于侦听响应式数据的变化并执行副作用,但它们有以下区别:

watchEffect

  • 立即执行一次回调函数,并自动追踪其中的响应式依赖
  • 当任何依赖项变化时重新执行回调
  • 不需要明确指定要侦听的数据源
  • 无法获取被侦听状态的前一个值

watch

  • 默认情况下,只有在侦听的源数据变化时才执行回调
  • 需要明确指定要侦听的数据源
  • 可以访问被侦听状态的当前值和前一个值
  • 支持侦听多个数据源
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
import { ref, watch, watchEffect } from 'vue'

export default {
setup() {
const count = ref(0)
const name = ref('Vue')

// watchEffect 示例
watchEffect(() => {
console.log(`Count is: ${count.value}, Name is: ${name.value}`)
// 自动追踪 count 和 name 的变化
})

// watch 示例 - 侦听单个数据源
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})

// watch 示例 - 侦听多个数据源
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`Count: ${oldCount} -> ${newCount}, Name: ${oldName} -> ${newName}`)
})

// watch 示例 - 深度侦听
const user = ref({ name: 'John', age: 30 })
watch(user, (newUser, oldUser) => {
console.log('User changed:', newUser, oldUser)
}, { deep: true })

return {
count,
name,
user
}
}
}

组件通信

9. Vue 3 中组件之间有哪些通信方式?

答案
Vue 3 中组件通信的主要方式包括:

  1. Props 和 Events

    • 父组件通过 props 向子组件传递数据
    • 子组件通过 emits 向父组件发送事件
  2. v-model

    • Vue 3 中的 v-model 可以使用自定义的 prop 和事件
    • 可以在同一组件上使用多个 v-model
  3. provide/inject

    • 适用于深层组件嵌套的场景
    • 祖先组件通过 provide 提供数据,后代组件通过 inject 注入数据
  4. Vuex/Pinia

    • 集中式状态管理
    • 适用于复杂应用的全局状态管理
  5. mitt/tiny-emitter

    • 事件总线,用于任意组件间通信
    • Vue 3 移除了 $on, $off 等事件 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Props 和 Events
// 子组件
export default {
props: {
message: String
},
emits: ['update'],
setup(props, { emit }) {
const handleClick = () => {
emit('update', 'New message')
}

return { handleClick }
}
}

// 父组件
<child-component
:message="parentMessage"
@update="parentMessage = $event"
/>

// provide/inject
// 父组件
import { provide, ref } from 'vue'

export default {
setup() {
const theme = ref('light')
provide('theme', theme) // 提供响应式数据

return { theme }
}
}

// 子组件或后代组件
import { inject } from 'vue'

export default {
setup() {
const theme = inject('theme') // 注入数据
return { theme }
}
}

10. Vue 3 中的 v-model 有什么变化?

答案
Vue 3 中的 v-model 相比 Vue 2 有以下变化:

  1. 默认 prop 和事件名称变化

    • Vue 2:prop 为 value,事件为 input
    • Vue 3:prop 为 modelValue,事件为 update:modelValue
  2. 支持多个 v-model

    • Vue 3 允许在同一组件上使用多个 v-model,每个绑定可以有不同的名称
  3. 移除 .sync 修饰符

    • Vue 3 中 v-model 可以替代 Vue 2 中的 .sync 修饰符
  4. 自定义 v-model 修饰符

    • Vue 3 支持自定义 v-model 修饰符
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
<!-- 基本用法 -->
<custom-input v-model="searchText" />

<!-- 等价于 -->
<custom-input
:modelValue="searchText"
@update:modelValue="searchText = $event"
/>

<!-- 多个 v-model -->
<user-form
v-model:name="userName"
v-model:age="userAge"
/>

<!-- 等价于 -->
<user-form
:name="userName"
@update:name="userName = $event"
:age="userAge"
@update:age="userAge = $event"
/>

<!-- 带修饰符的 v-model -->
<custom-input v-model.capitalize="searchText" />

组件实现:

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
// 单个 v-model
export default {
props: {
modelValue: String
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const handleInput = (e) => {
emit('update:modelValue', e.target.value)
}

return { handleInput }
}
}

// 多个 v-model
export default {
props: {
name: String,
age: Number
},
emits: ['update:name', 'update:age'],
setup(props, { emit }) {
const updateName = (name) => {
emit('update:name', name)
}

const updateAge = (age) => {
emit('update:age', age)
}

return { updateName, updateAge }
}
}

// 自定义修饰符
export default {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const handleInput = (e) => {
let value = e.target.value

// 应用修饰符
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}

emit('update:modelValue', value)
}

return { handleInput }
}
}

性能优化

11. Vue 3 中如何优化性能?

答案
Vue 3 中的性能优化方法包括:

  1. 使用 v-memo 减少不必要的重新渲染

    1
    2
    3
    <div v-memo="[item.id === selected]">
    <!-- 只有当 item.id === selected 变化时才会重新渲染 -->
    </div>
  2. 使用 v-once 渲染静态内容

    1
    2
    3
    <div v-once>
    <!-- 只渲染一次,之后不再更新 -->
    </div>
  3. 使用 computed 缓存计算结果

    1
    2
    3
    const filteredItems = computed(() => {
    return items.value.filter(item => item.price > 100)
    })
  4. 使用 shallowRef 和 shallowReactive 减少深层响应

    1
    2
    3
    4
    // 只有 state 的顶层属性是响应式的
    const state = shallowReactive({
    user: { name: 'John', address: { city: 'New York' } }
    })
  5. 使用 defineAsyncComponent 异步加载组件

    1
    2
    3
    const AsyncComponent = defineAsyncComponent(() => 
    import('./components/AsyncComponent.vue')
    )
  6. 使用 KeepAlive 缓存组件实例

    1
    2
    3
    <keep-alive>
    <component :is="currentComponent" />
    </keep-alive>
  7. 使用 Suspense 处理异步依赖

    1
    2
    3
    4
    5
    6
    7
    8
    <suspense>
    <template #default>
    <async-component />
    </template>
    <template #fallback>
    <loading-spinner />
    </template>
    </suspense>
  8. 使用虚拟列表渲染大量数据

    1
    2
    3
    4
    5
    6
    7
    <virtual-list
    :items="items"
    :item-height="50"
    v-slot="{ item }"
    >
    <div>{{ item.name }}</div>
    </virtual-list>

12. Vue 3 中如何实现自定义指令?

答案
Vue 3 中自定义指令的实现方式与 Vue 2 有所不同,主要变化在于钩子函数的名称与组件生命周期保持一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 全局注册
const app = createApp({})

app.directive('focus', {
mounted(el) {
el.focus()
}
})

// 局部注册
export default {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}

Vue 3 中自定义指令的钩子函数包括:

  • created:在绑定元素的 attribute 或事件监听器被应用之前调用
  • beforeMount:在元素被插入到 DOM 之前调用
  • mounted:在绑定元素的父组件被挂载后调用
  • beforeUpdate:在包含组件的 VNode 更新之前调用
  • updated:在包含组件的 VNode 及其子组件的 VNode 更新之后调用
  • beforeUnmount:在绑定元素的父组件卸载之前调用
  • unmounted:当指令与元素解除绑定且父组件已卸载时调用

自定义指令的参数:

1
2
3
4
5
6
7
8
9
app.directive('my-directive', (el, binding, vnode, prevVnode) => {
// binding 包含以下属性:
// value:传递给指令的值
// oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用
// arg:传递给指令的参数,如 v-my-directive:arg
// modifiers:包含修饰符的对象,如 v-my-directive.foo.bar 中,modifiers 为 { foo: true, bar: true }
// instance:使用该指令的组件实例
// dir:指令定义对象
})

工程化与生态

13. Vue 3 中如何使用 TypeScript?

答案
Vue 3 对 TypeScript 有很好的支持,主要使用方式包括:

  1. 使用 defineComponent 包装组件选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { defineComponent } from 'vue'

    export default defineComponent({
    props: {
    message: {
    type: String,
    required: true
    }
    },
    data() {
    return {
    count: 0
    }
    }
    })
  2. 在 setup 函数中使用类型注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { defineComponent, ref, Ref } from 'vue'

    interface User {
    name: string;
    age: number;
    }

    export default defineComponent({
    setup() {
    const count: Ref<number> = ref(0)
    const user: Ref<User> = ref({ name: 'John', age: 30 })

    return { count, user }
    }
    })
  3. 使用 defineProps 和 defineEmits

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <script setup lang="ts">
    // 使用运行时声明
    const props = defineProps<{
    name: string;
    age?: number;
    }>()

    const emit = defineEmits<{
    (e: 'update', id: number): void;
    (e: 'delete'): void;
    }>()

    // 使用默认值
    withDefaults(defineProps<{
    name: string;
    age?: number;
    }>(), {
    age: 18
    })
    </script>
  4. 为 ref 和 reactive 提供类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import { ref, reactive } from 'vue'

    interface User {
    name: string;
    age: number;
    }

    // 为 ref 提供类型
    const user = ref<User>({ name: 'John', age: 30 })

    // 为 reactive 提供类型
    const state = reactive<{
    count: number;
    users: User[];
    }>({
    count: 0,
    users: []
    })
  5. 使用 PropType 定义复杂 prop 类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import { defineComponent, PropType } from 'vue'

    interface User {
    name: string;
    age: number;
    }

    export default defineComponent({
    props: {
    user: {
    type: Object as PropType<User>,
    required: true
    },
    callback: {
    type: Function as PropType<(id: number) => void>,
    required: true
    }
    }
    })

14. Vue 3 中如何使用 Pinia 进行状态管理?

答案
Pinia 是 Vue 官方推荐的状态管理库,用于替代 Vuex。使用方法如下:

  1. 安装 Pinia

    1
    npm install pinia
  2. 创建 Pinia 实例并挂载到应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import { createApp } from 'vue'
    import { createPinia } from 'pinia'
    import App from './App.vue'

    const app = createApp(App)
    const pinia = createPinia()

    app.use(pinia)
    app.mount('#app')
  3. 定义 Store

    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
    // stores/counter.js
    import { defineStore } from 'pinia'

    // 第一个参数是应用中 store 的唯一 ID
    export const useCounterStore = defineStore('counter', {
    // state
    state: () => ({
    count: 0,
    name: 'Eduardo'
    }),

    // getters
    getters: {
    doubleCount: (state) => state.count * 2,
    // 使用 this 访问其他 getter
    doubleCountPlusOne() {
    return this.doubleCount + 1
    }
    },

    // actions
    actions: {
    increment() {
    this.count++
    },
    async fetchData() {
    const data = await api.get('...')
    this.count = data.count
    }
    }
    })
  4. 使用 Setup Stores(更简洁的语法):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import { defineStore } from 'pinia'
    import { ref, computed } from 'vue'

    export const useCounterStore = defineStore('counter', () => {
    // state
    const count = ref(0)
    const name = ref('Eduardo')

    // getters
    const doubleCount = computed(() => count.value * 2)

    // actions
    function increment() {
    count.value++
    }

    return { count, name, doubleCount, increment }
    })
  5. 在组件中使用 Store

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <template>
    <div>
    <p>Count: {{ counter.count }}</p>
    <p>Double count: {{ counter.doubleCount }}</p>
    <button @click="counter.increment()">Increment</button>
    <button @click="increment">Increment (extracted)</button>
    </div>
    </template>

    <script setup>
    import { useCounterStore } from '@/stores/counter'
    import { storeToRefs } from 'pinia'

    const counter = useCounterStore()

    // 解构 store 时保持响应性
    const { count, doubleCount } = storeToRefs(counter)
    // 直接解构 actions
    const { increment } = counter
    </script>
  6. 修改 State

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 方式 1:直接修改
    counter.count++

    // 方式 2:使用 $patch 方法
    counter.$patch({
    count: counter.count + 1,
    name: 'Alex'
    })

    // 方式 3:使用 $patch 函数
    counter.$patch((state) => {
    state.count++
    state.name = 'Alex'
    })

    // 方式 4:使用 actions
    counter.increment()
  7. 重置 State

    1
    counter.$reset()

15. Vue 3 中的 script setup 语法有什么优势?

答案
<script setup> 是 Vue 3.2 引入的编译时语法糖,它有以下优势:

  1. 更少的样板代码

    • 不需要返回要暴露给模板的变量
    • 导入的组件自动注册,无需在 components 选项中声明
  2. 更好的性能

    • 编译时优化,减少运行时开销
    • 模板中的变量访问不需要通过代理
  3. 更好的 TypeScript 支持

    • 直接在 <script setup> 中使用 TypeScript
    • 使用 defineProps 和 defineEmits 获得完整的类型推断
  4. 更好的 IDE 支持

    • 更好的自动补全
    • 更准确的类型检查
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
<script setup>
import { ref, computed } from 'vue'
import MyComponent from './MyComponent.vue' // 自动注册

// 声明响应式状态
const count = ref(0)

// 计算属性
const doubleCount = computed(() => count.value * 2)

// 方法
function increment() {
count.value++
}

// 生命周期钩子
onMounted(() => {
console.log('Component mounted')
})

// 直接导入并使用组件,无需注册
</script>

<template>
<div>
<p>Count: {{ count }}</p>
<p>Double count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
<MyComponent />
</div>
</template>

使用 defineProps 和 defineEmits:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script setup>
// 声明 props
const props = defineProps({
title: String,
likes: {
type: Number,
default: 0
}
})

// 声明 emits
const emit = defineEmits(['update', 'delete'])

// 使用 emit
function handleClick() {
emit('update', 123)
}
</script>

使用 TypeScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup lang="ts">
// 使用类型声明 props
const props = defineProps<{
title: string;
likes?: number;
}>()

// 使用类型声明 emits
const emit = defineEmits<{
(e: 'update', id: number): void;
(e: 'delete'): void;
}>()
</script>

适合快速熟悉 Vue 3 的 GitHub 项目

以下是一些优质的 Vue 3 项目,可以帮助你快速熟悉 Vue 3 的各种特性和最佳实践:

  1. Vue 3 官方示例

  2. Hoppscotch

  3. VueUse

  4. Element Plus

  5. Vite

  6. Vue 3 Instagram Clone

  7. Vue 3 Whatsapp Clone

  8. Vue 3 Netflix Clone

  9. Elk

  10. Pinia

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器