本文深入探讨了Vue3的各项新特性和面试中常见的考点,涵盖了Vue3的基础概念、Composition API的使用、响应式系统详解以及面试时可能遇到的高频问题。文中提供了详细的代码示例和应用场景,帮助读者全面掌握Vue3的核心机制和面试技巧,确保在面试中能够从容应对Vue3面试真题。
Vue3作为Vue框架的最新版本,带来了一系列重要的改进和优化。以下是Vue3与Vue2的主要区别:
性能提升:
FSTreeShaking
,只针对变化的部分进行更新,避免了不必要的DOM操作。3.TypeScript支持增强:
<teleport>
组件,允许开发者将DOM节点渲染到DOM树的其他位置。v-model
的双向绑定优化等。Vue3引入了Composition API,这是一个更灵活、强大的API,用于组织组件中的逻辑。通过setup
函数,开发者可以在组件中直接编写逻辑代码,而不是依赖于Options API中的选项。Composition API的优点包括:更好的逻辑复用、更清晰的逻辑划分,以及更易于进行TypeScript类型推断。
<template> <div> <p>Count: {{ count }}</p> <button @click="increment">Increment</button> </div> </template> <script setup> import { ref, watch } from 'vue'; const count = ref(0); function increment() { count.value++; } watch(count, (newValue, oldValue) => { console.log(`Count changed from ${oldValue} to ${newValue}`); }); </script>
ref
和reactive
来管理状态,特别适用于需要频繁更新的复杂状态。setup
函数中的逻辑模块。Vue3的响应式系统基于Proxy
对象,提供了更强大的特性,包括能够监听复杂对象的变化。响应式系统的核心是将数据对象包装成一个Proxy代理对象,使得任何数据的读写操作都会触发相应的更新。
import { reactive } from 'vue'; const state = reactive({ count: 0, users: [ { id: 1, name: 'John' }, { id: 2, name: 'Jane' } ] });
state.count
或state.count = 5
state.users.push({ id: 3, name: 'Doe' })
delete state.users[0]
get
和set
拦截器实现。Proxy
代理可以监听数组的push
、pop
等方法,而无需自定义方法。Proxy
代理还可以监听到属性的添加或删除。Vue3对组件的生命周期钩子进行了一些调整,主要的变化是:
beforeDestroy
变为beforeUnmount
,destroyed
变为unmounted
。onMounted
、onUnmounted
等函数,开发者需要通过setup
函数来调用这些函数。<template> <div> <h1>Life Cycle Hooks</h1> </div> </template> <script setup> import { onMounted, onUnmounted } from 'vue'; onMounted(() => { console.log('Component is mounted'); }); onUnmounted(() => { console.log('Component is unmounted'); }); </script>
ref
用于创建一个可变化的响应式引用,适用于简单类型(如数字、布尔、字符串等)。ref
对象具有一个特殊的.value
属性,用于访问或修改内部值。reactive
用于创建一个可变化的响应式对象,适用于复杂类型(如对象、数组等),可以直接访问对象的任何属性。ref
。import { ref } from 'vue'; const count = ref(0);
reactive
。import { reactive } from 'vue'; const state = reactive({ count: 0, users: [] });
<teleport>
组件允许开发者将DOM节点渲染到DOM树的其他位置。这在需要将模态框或弹窗放置在DOM树根部时非常有用,以避免样式和位置问题。<template> <div> <teleport to="body"> <div class="modal"> <p>Modal Content</p> </div> </teleport> </div> </template> <script setup> </script>
npm install -g @vue/cli vue create my-vue3-app cd my-vue3-app
vue add vue3
main.js
:引入Vue3的Composition API。import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app');
App.vue
:编写简单的组件。<template> <div id="app"> <h1>Hello Vue3</h1> </div> </template> <script setup> import { ref, reactive } from 'vue'; const count = ref(0); const state = reactive({ count: 0, users: [] }); </script>
<template> <div> <h1>{{ count }}</h1> <button @click="increment">Increment</button> </div> </template> <script setup> import { ref } from 'vue'; const count = ref(0); function increment() { count.value++; } </script>
provide
和inject
进行状态共享:<!-- ParentComponent.vue --> <template> <div> <ChildComponent /> </div> </template> <script setup> import { ref, provide } from 'vue'; import ChildComponent from './ChildComponent.vue'; const count = ref(0); provide('count', count); </script> <!-- ChildComponent.vue --> <template> <div> <p>{{ count }}</p> </div> </template> <script setup> import { inject } from 'vue'; const count = inject('count'); </script>
npm install vue-router@next
import { createRouter, createWebHistory } from 'vue-router'; import Home from './components/Home.vue'; import About from './components/About.vue'; const routes = [ { path: '/', component: Home }, { path: '/about', component: About } ]; const router = createRouter({ history: createWebHistory(), routes }); export default router;
npm install pinia@next
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');
// store/index.js import { defineStore } from 'pinia'; export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), actions: { increment() { this.count++; } } });
<template> <div> <h1>{{ $store.counter.count }}</h1> <button @click="increment">Increment</button> </div> </template> <script setup> import { useCounterStore } from '@/store'; const store = useCounterStore(); function increment() { store.increment(); } </script>
<!-- ParentComponent.vue --> <template> <ChildComponent :value="parentValue" @increment="incrementValue" /> </template> <script setup> import ChildComponent from './ChildComponent.vue'; const parentValue = ref(0); function incrementValue() { parentValue.value++; } </script> <!-- ChildComponent.vue --> <template> <div> <p>{{ value }}</p> <button @click="$emit('increment')">Increment</button> </div> </template> <script setup> import { defineProps } from 'vue'; const props = defineProps({ value: Number }); </script>
<!-- ParentComponent.vue --> <template> <ChildComponent /> </template> <script setup> import { provide } from 'vue'; import ChildComponent from './ChildComponent.vue'; provide('value', 0); </script> <!-- ChildComponent.vue --> <template> <div> <p>{{ value }}</p> </div> </template> <script setup> import { inject } from 'vue'; const value = inject('value'); </script>
// store/index.js import { defineStore } from 'pinia'; export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), actions: { increment() { this.count++; } } });
<!-- App.vue --> <template> <div> <p>{{ store.counter.count }}</p> <button @click="increment">Increment</button> </div> </template> <script setup> import { useCounterStore } from '@/store'; import { storeToRefs } from 'pinia'; const store = useCounterStore(); const { count } = storeToRefs(store); function increment() { store.increment(); } </script>
Object.assign
、{...obj}
或JSON.parse(JSON.stringify(obj))
const obj = { a: 1, b: { c: 2 } }; const shallowCopy = { ...obj };
JSON.parse(JSON.stringify(obj))
、lodash
中的_.cloneDeep
或递归实现。function deepCopy(obj) { if (obj === null) return null; const result = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { result[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return result; } const obj = { a: 1, b: { c: 2 } }; const deepCopy = deepCopy(obj);
npm install typescript @types/node @vue/cli-plugin-typescript
vue add typescript
ref
和reactive
类型推断:import { ref, reactive, Ref } from 'vue'; const count: Ref<number> = ref(0); const state: { count: number; users: [] } = reactive({ count: 0, users: [] });
setup
函数中的类型推断:import { ref, reactive } from 'vue'; const count = ref<number>(0); function increment(): void { count.value++; }