Vue
2026/1/15大约 3 分钟
Vue
Vue2 基础
生命周期
beforeCreate → created → beforeMount → mounted
→ beforeUpdate → updated
→ beforeDestroy → destroyedexport default {
created() {
// 实例创建完成,可访问 data/methods
// 常用于:初始化数据、发起请求
},
mounted() {
// DOM 挂载完成
// 常用于:操作 DOM、初始化第三方库
},
beforeDestroy() {
// 实例销毁前
// 常用于:清除定时器、解绑事件
}
}响应式原理
// Vue2 使用 Object.defineProperty
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend(); // 收集依赖
}
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
dep.notify(); // 通知更新
}
}
});
}组件通信
// 1. props / $emit
// 父 → 子
<Child :msg="message" />
// 子 → 父
this.$emit('update', data);
// 2. $refs
<Child ref="child" />
this.$refs.child.method();
// 3. provide / inject
// 祖先组件
provide() {
return { theme: this.theme };
}
// 后代组件
inject: ['theme']
// 4. EventBus
const bus = new Vue();
bus.$emit('event', data);
bus.$on('event', handler);
// 5. Vuex
this.$store.state.xxx
this.$store.commit('mutation', payload)
this.$store.dispatch('action', payload)computed vs watch
export default {
computed: {
// 计算属性:有缓存,依赖变化才重新计算
fullName() {
return this.firstName + ' ' + this.lastName;
}
},
watch: {
// 侦听器:数据变化时执行副作用
searchQuery: {
handler(newVal) {
this.fetchResults(newVal);
},
immediate: true, // 立即执行
deep: true // 深度监听
}
}
}Vue3 新特性
Composition API
import { ref, reactive, computed, watch, onMounted } from 'vue';
export default {
setup() {
// 响应式数据
const count = ref(0);
const state = reactive({ name: 'Vue3' });
// 计算属性
const double = computed(() => count.value * 2);
// 侦听器
watch(count, (newVal, oldVal) => {
console.log(newVal, oldVal);
});
// 生命周期
onMounted(() => {
console.log('mounted');
});
// 方法
const increment = () => count.value++;
return { count, state, double, increment };
}
}setup 语法糖
<script setup>
import { ref, computed } from 'vue';
const count = ref(0);
const double = computed(() => count.value * 2);
const increment = () => count.value++;
// 定义 props
const props = defineProps({
msg: String
});
// 定义 emit
const emit = defineEmits(['update']);
</script>响应式原理(Vue3)
// Vue3 使用 Proxy
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key); // 收集依赖
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return result;
}
});
}ref vs reactive
// ref:基本类型,需要 .value 访问
const count = ref(0);
count.value++;
// reactive:对象类型,直接访问
const state = reactive({ count: 0 });
state.count++;
// 解构会丢失响应性,使用 toRefs
const { count } = toRefs(state);虚拟 DOM 与 Diff
虚拟 DOM
// 虚拟 DOM 结构
const vnode = {
tag: 'div',
props: { id: 'app' },
children: [
{ tag: 'span', children: 'Hello' }
]
};Diff 算法
Vue2 双端比较:
1. 新头 vs 旧头
2. 新尾 vs 旧尾
3. 新头 vs 旧尾
4. 新尾 vs 旧头
5. 遍历查找
Vue3 最长递增子序列:
1. 预处理相同前缀/后缀
2. 新增/删除节点
3. 移动节点(最长递增子序列优化)Vue Router
// 路由配置
const routes = [
{ path: '/', component: Home },
{ path: '/user/:id', component: User, props: true },
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true },
children: [
{ path: 'dashboard', component: Dashboard }
]
}
];
// 导航守卫
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login');
} else {
next();
}
});
// 路由懒加载
const User = () => import('./views/User.vue');Vuex / Pinia
Vuex
const store = new Vuex.Store({
state: { count: 0 },
getters: {
double: state => state.count * 2
},
mutations: {
increment(state, payload) {
state.count += payload;
}
},
actions: {
async fetchData({ commit }) {
const data = await api.getData();
commit('setData', data);
}
}
});Pinia
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2
},
actions: {
increment() {
this.count++;
}
}
});
// 使用
const store = useCounterStore();
store.count;
store.increment();性能优化
// 1. v-if vs v-show
// v-if:条件渲染,切换开销大
// v-show:display 切换,初始渲染开销大
// 2. v-for 使用 key
<li v-for="item in list" :key="item.id">
// 3. 组件懒加载
const AsyncComponent = defineAsyncComponent(() => import('./Async.vue'));
// 4. keep-alive 缓存
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
// 5. 虚拟滚动
// 使用 vue-virtual-scroller
// 6. 避免不必要的响应式
const rawData = markRaw(largeObject);