参考答案:
Vue 2 使用 Object.defineProperty,Vue 3 使用 Proxy。
// Vue 2 Object.defineProperty
functiondefineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
return val;
},
set(newVal) {
val = newVal;
update(); // 触发更新
}
});
}
// Vue 3 Proxy
functionreactive(obj) {
returnnewProxy(obj, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, value) {
target[key] = value;
trigger(target, key);
}
});
}
参考答案:
Vue 2:
Vue 3:
// Vue 3
import { onMounted, onUpdated, onUnmounted } from'vue';
exportdefault {
setup() {
onMounted(() => {
console.log('挂载完成');
});
onUpdated(() => {
console.log('更新完成');
});
onUnmounted(() => {
console.log('卸载完成');
});
}
}
参考答案:
// computed - 有缓存
const double = computed(() => count.value * 2);
// watch - 监听数据变化
watch(count, (newVal, oldVal) => {
console.log('变化了', newVal, oldVal);
});
// 监听多个属性
watch([a, b], ([newA, newB]) => {
console.log(newA, newB);
});
// 深度监听
watch(obj, () => {}, { deep: true });
参考答案:
// provide / inject
// 祖先
provide('theme', 'dark');
// 后代
const theme = inject('theme');
// $attrs
// 父组件
<Childname="Tom"age="25" />
// 子组件
props: ['name']
console.log(this.$attrs) // { age: '25' }
参考答案:
<!-- v-if -->
<divv-if="show">内容</div>
<!-- v-show -->
<divv-show="show">内容</div>
参考答案:
Composition API 是 Vue 3 新增的 API,用函数方式组织组件逻辑。
import { ref, reactive, computed, watch, onMounted } from'vue';
exportdefault {
setup() {
// 响应式数据
const count = ref(0);
const state = reactive({ name: 'Tom' });
// 计算属性
const double = computed(() => count.value * 2);
// 监听
watch(count, (newVal) => {
console.log(newVal);
});
// 生命周期
onMounted(() => {
console.log('mounted');
});
return { count, state, double };
}
}
参考答案:
// ref - 基础类型
const count = ref(0);
count.value = 1; // 访问值需要 .value
// reactive - 对象类型
const state = reactive({
name: 'Tom',
age: 25
});
state.name = 'Jerry'; // 直接访问
// ref 也支持对象,内部会调用 reactive
const obj = ref({ name: 'Tom' });
obj.value.name = 'Jerry'; // 内部转为 reactive
参考答案:
keep-alive 是缓存组件,避免重复渲染。
<keep-alive:include="['Home', 'About']":exclude="['Login']">
<component:is="currentComponent" />
</keep-alive>
<!-- 路由缓存 -->
<router-viewv-slot="{ Component }">
<keep-alive>
<component:is="Component" />
</keep-alive>
</router-view>
生命周期变化:
参考答案:
nextTick 等待 DOM 更新后执行回调。
// 修改数据
this.msg = 'Hello';
// 此时 DOM 还未更新
console.log(this.$refs.text); // 旧内容
// 使用 nextTick
this.$nextTick(() => {
console.log(this.$refs.text); // 新内容
});
// async/await 方式
asyncfunctionupdate() {
this.msg = 'Hello';
awaitthis.$nextTick();
console.log(this.$refs.text);
}
参考答案:
Mixin 是复用组件逻辑的方式。
// myMixin.js
exportdefault {
data() {
return {
name: 'Tom'
}
},
methods: {
sayHello() {
console.log('Hello');
}
}
}
// 使用
import myMixin from'./myMixin';
exportdefault {
mixins: [myMixin],
mounted() {
this.sayHello(); // Hello
}
}
参考答案:
Vue 2 使用 Object.defineProperty 劫持 getter/setter。
functionobserve(obj) {
if (typeof obj !== 'object' || obj === null) return;
Object.keys(obj).forEach(key => {
let value = obj[key];
defineReactive(obj, key, value);
});
}
functiondefineReactive(obj, key, value) {
observe(value); // 递归监听
Object.defineProperty(obj, key, {
get() {
return value;
},
set(newVal) {
if (newVal !== value) {
value = newVal;
observe(newVal);
update(); // 通知更新
}
}
});
}
Vue 3 使用 Proxy,性能更好。
参考答案:
Vue 的 diff 算法是同层比较,时间复杂度 O(n)。
// patch 函数
functionpatch(oldVnode, newVnode) {
if (sameVnode(oldVnode, newVnode)) {
patchVnode(oldVnode, newVnode);
} else {
replaceNode(oldVnode, newVnode);
}
}
functionsameVnode(a, b) {
return a.key === b.key && a.tag === b.tag;
}
优化:
参考答案:
State → Getter → Component
↑ ↓
└──── Mutation ← Action
// state
state: { count: 0 }
// mutation
mutations: {
increment(state) {
state.count++;
}
}
// action
actions: {
increment({ commit }) {
commit('increment');
}
}
// getter
getters: {
double: state => state.count * 2
}
参考答案:
setup 是 Composition API 的入口。
exportdefault {
setup(props, { attrs, slots, emit, expose }) {
// props 需要定义
const name = ref('Tom');
// 暴露给模板
return { name };
// 暴露给父组件
expose({ name });
}
}
参考答案:
参考答案:
// 1. Props / $emit
// 父组件
<Child :value="msg" @change="handleChange" />
// 子组件
props: { value: String }
this.$emit('change', 'new value')
// 2. Provide / Inject
// 父组件
provide: { name: 'Tom' }
// 子组件
inject: ['name']
// 3. Event Bus
// bus.js
const bus = newVue();
exportdefault bus;
// 使用
bus.$emit('event', data);
bus.$on('event', callback);
// 4. Vuex / Pinia
// 5. ref / defineExpose
参考答案:
const router = newVueRouter({
routes: [
{
path: '/user',
component: User,
beforeEnter: (to, from, next) => {
next();
}
}
]
});
// 全局守卫
router.beforeEach((to, from, next) => {
const isAuth = localStorage.getItem('token');
if (isAuth || to.path === '/login') {
next();
} else {
next('/login');
}
});
// 组件内守卫
beforeRouteEnter(to, from, next) {},
beforeRouteUpdate(to, from, next) {},
beforeRouteLeave(to, from, next) {}
参考答案:
// 全局指令
Vue.directive('focus', {
inserted(el) {
el.focus();
}
});
// 组件指令
directives: {
focus: {
inserted(el) {
el.focus();
}
}
}
// 使用
<input v-focus />
参考答案:
// 定义过滤器
filters: {
currency(value) {
return'¥' + value.toFixed(2);
}
}
// 使用
{{ price | currency }}
// 链式
{{ price | currency | lowercase }}
参考答案:
<!-- is 属性 -->
<component :is="currentComponent" />
<!-- keep-alive 缓存 -->
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
参考答案:
// 方式1: defineAsyncComponent
import { defineAsyncComponent } from'vue';
constAsyncComp = defineAsyncComponent(() =>
import('./AsyncComp.vue')
);
// 方式2: 组件定义
exportdefault {
components: {
AsyncComp: () =>import('./AsyncComp.vue')
}
}
参考答案:
// mixin.js
exportdefault {
data() {
return { name: 'Tom' }
},
methods: {
hello() {
console.log(this.name);
}
}
}
// 使用
import myMixin from'./mixin';
exportdefault {
mixins: [myMixin]
}
参考答案:
// 创建组件构造器
constProfile = Vue.extend({
template: '<p>{{ firstName }} {{ lastName }}</p>',
data() {
return { firstName: 'Tom', lastName: 'Jack' }
}
});
// 创建实例
newProfile().$mount('#app');
参考答案:
methods: {
asyncupdate() {
this.msg = 'Hello';
// DOM 更新后执行
this.$nextTick(() => {
console.log(this.$refs.input.value);
});
// async/await 写法
awaitthis.$nextTick();
console.log(this.$refs.input.value);
}
}
参考答案:
Vue 2 使用 Object.defineProperty,Vue 3 使用 Proxy。
// Vue 2
functiondefineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
update();
}
}
});
}
// Vue 3
const handler = {
get(target, key) {
track(target, key);
returnReflect.get(target, key);
},
set(target, key, value) {
Reflect.set(target, key, value);
trigger(target, key);
}
};
参考答案:
computed: {
fullName() {
returnthis.firstName + ' ' + this.lastName;
},
// setter
fullName: {
get() {
returnthis.firstName + ' ' + this.lastName;
},
set(val) {
const [first, last] = val.split(' ');
this.firstName = first;
this.lastName = last;
}
}
}
watch: {
msg(newVal, oldVal) {
console.log(newVal, oldVal);
},
// 深度监听
obj: {
handler(newVal) {},
deep: true
},
// 立即执行
msg: {
handler() {},
immediate: true
}
}
参考答案:
<!-- 父组件 -->
<Child>
<template v-slot:header>
<h1>标题</h1>
</template>
<p>默认内容</p>
<template v-slot:footer>
<p>底部</p>
</template>
</Child>
<!-- 子组件 -->
<div>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
参考答案:
<!-- v-if / v-else-if / v-else -->
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>C</div>
<!-- v-show -->
<div v-show="show">显示/隐藏</div>
<!-- 区别:v-if 真正渲染,v-show 始终渲染并使用 display -->
参考答案:
<!-- 数组 -->
<li v-for="(item, index) in items" :key="index">
{{ index }} - {{ item.name }}
</li>
<!-- 对象 -->
<li v-for="(value, key, index) in obj" :key="key">
{{ key }}: {{ value }}
</li>
<!-- 组件 -->
<my-component
v-for="item in items"
:key="item.id"
:item="item"
/>
参考答案:
<!-- 子组件 -->
<script>
export default {
model: {
prop: 'value',
event: 'change'
},
props: {
value: String
},
methods: {
update(val) {
this.$emit('change', val);
}
}
}
</script>
<!-- 使用 -->
<input :value="value" @input="update($event.target.value)" />
📌 面试重点:双向绑定原理、生命周期、Composition API、响应式原理是高频考点。