vue2和vue3的区别
前言:很多小伙伴在面试的时候会遇到面试官会问vue框架的版本更替,以及vue2向vue3推进时是一个巨大的改变,会问到vue2和vue3的区别,以下是我个人整理的一些主要的区别,如有不对还希望各位大佬指出,我这边及时更正。
1.响应式对比
1.1 vue2中使用Object.defineProperty
// Vue 2 - 响应式系统(Object.defineProperty)
new Vue({
el: '#app',
data() {
return {
state:{
message: 'Hello Vue 2'
}
};
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('');
}
}
});
1.2 vue3中使用Proxy
// Vue 3 - 响应式系统(Proxy)
const { createApp, reactive, computed } = Vue;
const app = createApp({
setup() {
const state = reactive({
message: 'Hello Vue 3'
});
const reversedMessage = computed(() => state.message.split('').reverse().join(''));
return { state, reversedMessage };
}
});
app.mount('#app');
1.3 vue2不能监听到对象中的属性的新增、删除需要用到this. s e t 。 v u e 3 中删除 set。vue3中删除 set。vue3中删除set,属性删除和新增和响应式
在 Vue2中,如果你直接给一个对象新增或删除属性,Vue 的响应式系统无法检测到这种变动。这是因为 JavaScript 对象的属性在新增或删除时,Vue 2 的响应式系统无法自动劫持这些操作。为了解决这个问题,Vue 2 提供了 this.$set 方法,来保证新增属性是响应式的。
// Vue 2
this.$set(this.state, 'newProperty', 'value');
由于 Vue3使用 Proxy 进行响应式处理,直接修改对象的属性(包括新增)会自动触发视图更新,而且是响应式的。
state.newProperty=value
2. API差异
2.1 vue2使用 Options API
// Vue 2 Options API 示例
new Vue({
el: '#app',
data() {
return {
count: 0
};
},
computed: {
doubledCount() {
return this.count * 2;
}
},
methods: {
increment() {
this.count++;
}
}
});
2.2 Vue 3 使用 Composition API
写法1:
// Vue 3 Composition API 示例
const { createApp, ref, computed } = Vue;
const app = createApp({
setup() {
// 定义响应式变量
const count = ref(0);
// 定义计算属性
const doubledCount = computed(() => count.value * 2);
// 定义方法
const increment = () => {
count.value++;
};
return {
count,
doubledCount,
increment
};
}
});
app.mount('#app');
3.v-model 用法
3.1 Vue 2 中v-model默认是绑定 value 属性
<!-- Vue 2 v-model 示例 -->
<template>
<div>
<input v-model="message" />
<p>{{ message }}</p>
</div>
</template>
<script>
new Vue({
el: '#app',
data() {
return {
message: 'Hello Vue 2'
};
}
});
</script>
3.2 Vue 3中v-model支持多个绑定属性和事件自定义
<!-- Vue 3 多个 v-model 示例 -->
<template>
<child-component v-model:title="title" v-model:content="content" />
</template>
<script>
const { defineComponent, ref } = Vue;
const ChildComponent = defineComponent({
props: {
title: String,
content: String
},
emits: ['update:title', 'update:content'],
template: `
<div>
<input :value="title" @input="$emit('update:title', $event)" />
<textarea :value="content" @input="$emit('update:content', $event)"></textarea>
</div>
`
});
const app = createApp({
components: { ChildComponent },
setup() {
const title = ref('Title');
const content = ref('Content');
return { title, content };
}
});
app.mount('#app');
</script>
4.Fragments(多个根元素)
4.1 Vue 2 每个组件必须有一个根元素
<!-- Vue 2 组件必须有一个根元素 -->
<template>
<div>
<p>Message: {{ message }}</p>
</div>
</template>
<script>
new Vue({
el: '#app',
data() {
return {
message: 'Hello Vue 2'
};
}
});
</script>
4.2 Vue 3 支持多个根元素(Fragments)
<!-- Vue 3 组件可以有多个根元素 -->
<template>
<p>Message: {{ message }}</p>
<button @click="increment">Increment</button>
</template>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const message = ref('Hello Vue 3');
const increment = () => {
message.value += '!';
};
return { message, increment };
}
});
app.mount('#app');
</script>
5.生命周期
5.1 vue2生命周期
beforeCreate:实例刚被创建,数据和事件还未初始化。
created:实例创建完成,数据已响应,事件/Watcher 已设置。
beforeMount:在挂载开始之前调用,el 和 template 还没有被渲染。
mounted:挂载完成,DOM 已经渲染,适合做网络请求或操作 DOM。
beforeUpdate:数据变化导致重新渲染之前调用。
updated:组件更新并重新渲染后调用。
beforeDestroy:组件销毁前调用,可以在此做清理操作(如移除事件监听)。
destroyed:组件销毁后调用。
new Vue({
el: '#app',
data() {
return {
message: 'Hello Vue 2'
};
},
created() {
console.log('created');
},
mounted() {
console.log('mounted');
},
beforeUpdate() {
console.log('beforeUpdate');
},
updated() {
console.log('updated');
},
beforeDestroy() {
console.log('beforeDestroy');
},
destroyed() {
console.log('destroyed');
},
methods: {
someMethod() {
console.log('Some method');
}
}
});
5.2 vue3生命周期
setup:实例创建完成,数据已响应,事件/Watcher 已设置。
onBeforeMount:在挂载开始之前调用,el 和 template 还没有被渲染。
onMounted:挂载完成,DOM 已经渲染,适合做网络请求或操作 DOM。
onBeforeUpdate:数据变化导致重新渲染之前调用。
onUpdated:组件更新并重新渲染后调用。
onBeforeUnmount:组件销毁前调用,可以在此做清理操作(如移除事件监听)。
onUnmounted:组件销毁后调用。
import { createApp, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';
const app = createApp({
setup() {
// 生命周期钩子在 setup 中调用
onBeforeMount(() => {
console.log('beforeMount');
});
onMounted(() => {
console.log('mounted');
});
onBeforeUpdate(() => {
console.log('beforeUpdate');
});
onUpdated(() => {
console.log('updated');
});
onBeforeUnmount(() => {
console.log('beforeUnmount');
});
onUnmounted(() => {
console.log('unmounted');
});
}
});
app.mount('#app');
6.组件传值
6.1 vue2
父传子
<!-- 父组件 -->
<template>
<div>
<Child :message="parentMessage" />
</div>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
data() {
return {
parentMessage: 'Hello from parent',
};
},
};
</script>
<!-- 子组件 -->
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
props: {
message: String,
},
};
</script>
子向父
<!-- 父 -->
<template>
<div>
<Child @sendMessage="receiveMessage" />
</div>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
methods: {
receiveMessage(message) {
console.log('Received from child:', message);
},
},
};
</script>
<!-- 子 -->
<template>
<button @click="sendMessage">Send Message to Parent</button>
</template>
<script>
export default {
methods: {
sendMessage() {
this.$emit('sendMessage', 'Hello from child');
},
},
};
</script>
6.2 vue3
父传子
<!-- 父组件 -->
<template>
<div>
<Child :message="parentMessage" />
</div>
</template>
<script setup>
import Child from './Child.vue';
import { ref } 'vue';
let parentMessage = ref("parentMessage")
</script>
<!-- 子组件 -->
<template>
<div>
<p>{{ props.message }}</p>
</div>
</template>
<script setup>
import { ref } 'vue';
const props = defineProps({
message: {
type: String,
required: true,
},
});
</script>
子传父
<!-- 父 -->
<template>
<div>
<Child @customEvent="customEvent" />
</div>
</template>
<script>
import Child from './Child.vue';
const customEvent =(e)=>{
console.log('Received from child:', e);
}
</script>
<!-- 子 -->
<template>
<button @click="sendMessage">Send Message to Parent</button>
</template>
<script setup>
// 使用 defineEmits() 来声明事件
const emit = defineEmits();
// 触发事件
const sendMessage = () => {
emit('customEvent', 'Hello from Child');
};
</script>
7.v-if和v-for的优先级
7.1 vue2
在vue2中v-for的优先级高于v-if,可以放在一起使用,但是不建议这么做,会带来性能上的浪费
7.2 vue3
在vue3中v-if的优先级高于v-for,一起使用会报错。可以通过在外部添加一个标签,将v-for移到外层
8.diff算法不同
8.1 vue2
遍历每一个虚拟节点,进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方。用patch记录的消息去更新dom
缺点:比较每一个节点,而对于一些不参与更新的元素,进行比较是有点消耗性能的。特点:特别要提一下Vue的patch是即时的,并不是打包所有修改最后一起操作DOM,也就是在vue中边记录变更新。(React则是将更新放入队列后集中处理)。
8.3 vue3
在初始化的时候会给每一个虚拟节点添加一个patchFlags,是一种优化的标识。只会比较patchFlags发生变化的节点,进行识图更新。而对于patchFlags没有变化的元素作静态标记,在渲染的时候直接复用。