Vue 3的文章也跟新了不少,相比vue2还是有很多区别的,有许多重要的变化和改进。以下是 Vue 3 相对于 Vue 2 的一些主要区别:
生命周期函数基本和vue2差不多,名字前加了on
;
具体如下:
onBeforeMount
: 在挂载开始之前被调用。onMounted
: 挂载完成后被调用。onBeforeUpdate
: 数据更新前调用。onUpdated
: 数据更新后调用。 onBeforeUnmount
: 在卸载之前被调用,此时组件仍然是可用的。onUnmounted
: 在卸载后被调用,此时组件已经不再可用。由于引入了 Composition API,组件的逻辑可以通过 setup 函数来定义。setup 函数中的代码会在 beforeCreate 和 created 钩子之前执行。这意味着在 setup 函数中可以执行之前在 beforeCreate 和 created 钩子中执行的逻辑,所以还是能使用beforeCreate
和created
// vue3
<script setup>
import { onMounted } from 'vue'; // 使用前需引入生命周期钩子
onMounted(() => {
// ...
});
onMounted(() => {
// ...
});
</script>
</script>
onBeforeRender
: 在渲染开始之前调用,此时组件的状态和属性已经被解析,但尚未生成虚拟 DOM。onRender
: 在渲染函数被调用时调用,返回一个虚拟 DOM 树。onBeforeUpdate
: 数据更新前调用,与 Vue 2 的 beforeUpdate
不同,Vue 3 的 onBeforeUpdate
可以接收一个回调函数,用于在数据更新前执行异步操作。Vue 3 和 Vue 2 在响应式原理上有一些重要的区别,尽管它们的核心思想都是基于数据劫持实现的,但 Vue 3 引入了 Proxy 对象作为数据劫持的主要手段,而 Vue 2 使用的是 Object.defineProperty
,无法监听对象或数组新增、删除的元素。
Proxy 对象:
Reactive 函数:
reactive
函数将一个普通对象转换为响应式对象。这个函数会使用 Proxy 对象来包装原始对象,实现对对象属性的代理。import { reactive } from 'vue';
const state = reactive({ count: 0 });
Ref 函数:
ref
函数,用于创建一个包含响应式数据的引用对象。这个函数返回一个具有 .value
属性的对象,通过对这个对象进行访问和修改来触发响应式更新。import { ref } from 'vue';
const count = ref(0);
Object.defineProperty:
Object.defineProperty
来劫持对象的属性,通过在对象属性的读取和写入时触发 getter 和 setter 函数来实现响应式。Observer 类:
getter 和 setter:
// 伪代码,用于说明 Vue 2 的响应式原理
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
// 收集依赖
// ...
return val;
},
set(newVal) {
// 更新视图
// ...
val = newVal;
}
});
}
Vue 3 中的 Composition API 是一种新的组织组件逻辑的方式,它提供了更灵活和强大的工具来组织和重用组件代码。Composition API 的引入使得代码更易于理解、维护,同时也更好地支持 TypeScript。以下是 Composition API 的一些核心概念和特性:
setup
函数中。这个函数在组件实例创建之前调用,它接收 props
和 context
参数,允许你在组件创建之前进行一些初始化工作。setup(props, context) {
// 在这里可以进行组件初始化工作
// 可以访问 props 和 context 对象
}
reactive
和 ref
函数来创建响应式的数据。reactive
用于创建包含响应式对象,而 ref
用于创建包含基本类型值的引用对象。import { reactive, ref } from 'vue';
setup() {
const state = reactive({ count: 0 });
const count = ref(0);
return { state, count };
}
watch
和 watchEffect
函数来进行数据的监听。watchEffect
在其依赖的响应式数据变化时会自动运行,而 watch
则更加灵活,可以监听特定的数据变化,并执行相应的操作。import { watch, watchEffect } from 'vue';
setup() {
const state = reactive({ count: 0 });
watchEffect(() => {
console.log(state.count);
});
watch(() => state.count, (newValue, oldValue) => {
console.log(`count 从 ${oldValue} 变为 ${newValue}`);
});
}
onXxx
的形式。例如,created
钩子变为 onCreated
。setup() {
onMounted(() => {
// 组件挂载后执行的逻辑
});
onUpdated(() => {
// 组件更新后执行的逻辑
});
onUnmounted(() => {
// 组件卸载前执行的逻辑
});
}
provide
和 inject
函数,用于父子组件之间的传值。这使得跨组件之间的状态管理更加灵活。// 父组件
setup() {
const data = reactive({ message: 'Hello from parent' });
provide('data', data);
}
// 子组件
setup() {
const data = inject('data');
console.log(data.message); // 输出 'Hello from parent'
}
ref
和 reactive
返回的对象在模板中的使用方式变得更加直接,不再需要 .value
。// 在模板中
<template>
<p>{{ count }}</p>
</template>
// 在 setup 函数中
setup() {
const count = ref(0);
return { count };
}
// Custom Hook
export function useCounter() {
const count = ref(0);
function increment() {
count.value++;
}
return { count, increment };
}
// 在组件中使用
setup() {
const { count, increment } = useCounter();
return { count, increment };
}
Composition API 引入了这些新的概念和工具,使得组件的逻辑可以更清晰、更可组织,同时也提高了代码的可维护性。它并不是要取代 Options API,而是提供了另一种更灵活的选择。开发者可以根据项目的需要选择使用 Options API 还是 Composition API。
Teleport 是 Vue 3 中的一个新特性,它允许你在组件树中的任何位置渲染元素,使得在 DOM 中的位置可以脱离组件的父子关系。这对于创建全局弹窗、模态框或者在组件外渲染一些内容非常有用。Teleport 的核心思想是将要渲染的内容传送(teleport)到另一个位置。
<template>
<div>
<button @click="showModal = true">Show Modal</button>
<teleport to="body">
<Modal v-if="showModal" @close="showModal = false">
<!-- Modal 内容 -->
</Modal>
</teleport>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
data() {
return {
showModal: false
};
}
};
</script>
在上面的例子中,<Modal>
组件的内容被传送到 <teleport to="body">
下,也就是挂载到了 body
元素下。这样 Modal 就可以在 DOM 结构中脱离当前组件的位置,使得其可以在整个页面中显示,而不受到组件嵌套的限制。
to: 指定要传送到的目标位置,可以是一个 CSS 选择器字符串、DOM 元素或者一个函数。
<teleport :to="document.getElementById('target')">
<!-- 要传送的内容 -->
</teleport>
disabled: 一个布尔值,用于控制是否启用 Teleport。当为 true
时,Teleport 将不会生效,传送的内容将在组件的父级中渲染。
<teleport :to="document.getElementById('target')" :disabled="shouldDisable">
<!-- 要传送的内容 -->
</teleport>
目标元素的存在性: 在使用 Teleport 时,要确保传送目标存在于 DOM 中。如果目标不存在,Vue 会在控制台给出警告。
Scoped CSS: Teleport 不会破坏 Vue 的样式封装,传送的内容仍然受到组件的样式作用域限制。
事件和过渡: 传送的内容中的事件和过渡仍然可以正常工作,不受 Teleport 影响。
性能考虑: Teleport 的使用应当根据具体情况来考虑性能影响。在某些情况下,可能因为跨越组件边界,导致性能损失。因此,在考虑使用 Teleport 时,建议先进行性能测试。
Vue3 提供 Suspense 组件,在等待异步组件加载完成前渲染默认的内容,如 loading ,使用户的体验更平滑。使用它,需在模板中声明,并包括两个命名插槽:default 和 fallback。Suspense 确保加载完异步内容时显示默认插槽,并将 fallback 插槽用作加载状态。
<template>
<Suspense>
<template #default>
<AsyncComponent />
<AsyncData />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
<script setup>
const AsyncComponent = () => import('./AsyncComponent.vue');
const AsyncData = () => fetchData(); // 异步数据获取函数
</script>
在vue2中,必须有个根元素包裹组件,不然会运行报错。Vue 3 支持 Fragments,即可以在模板中使用多个根元素而不需要包裹它们在一个父元素中。
Tree-shaking 是一种用于剔除未使用代码(dead code)的优化技术,它通过静态分析的方式识别和移除项目中未被引用的模块或代码片段,以减小最终打包体积。Vue 3 被设计为更易于 Tree-shaking,以下是一些关于如何使用 Vue 3 进行 Tree-shaking 的注意事项:
javascript
Copy code
import { reactive } from ‘vue’;
这样可以避免引入整个 Vue 3 库,减小了打包体积。
javascript
Copy code
import { ref, onMounted } from ‘vue’;
export default {
setup() {
const count = ref(0);
onMounted(() => {
console.log('Component is mounted');
});
return { count };
}
};
3. 单文件组件的优化:
在单文件组件中,确保只导入你需要的组件和样式,以避免引入整个文件。同时,避免在模板中使用未使用的组件,因为这也可能影响 Tree-shaking 的效果。
懒加载和异步组件:
使用懒加载和异步组件可以帮助实现按需加载,仅在需要的时候才加载相关代码。这对于 Tree-shaking 是非常有利的。
优化生产构建:
在生产环境中,确保你的构建工具和配置是针对生产环境进行了优化的。例如,使用生产模式构建、开启压缩、移除调试信息等。
检查打包结果:
可以通过检查最终打包结果,查看是否成功剔除了未使用的代码。你可以使用工具或者分析打包后的代码,以确保 Tree-shaking 的效果符合预期。
使用这些方法,结合正确的配置和构建工具,可以使 Vue 3 更好地支持 Tree-shaking,帮助你减小项目的最终打包体积。
Vue 3 在 Virtual DOM 的 diff 算法上进行了一系列的优化,主要目标是提高更新性能、减少不必要的操作、以及更好地利用现代浏览器的特性。以下是一些 Vue 3 中 Virtual DOM diff 算法的优化:
Vue 3 引入了静态树提升,通过标记静态节点,可以在渲染时跳过对这些节点的处理,减少了 Virtual DOM 的操作和对比。这使得在一些场景下,特别是对于大型列表或者频繁更新的组件,能够提高性能。
Vue 3 利用了编译器生成的源码的优势。在运行时,不再需要对模板进行解析,而是直接使用生成的 JavaScript 代码进行渲染。这样避免了一些不必要的运行时开销,提高了渲染性能。
Vue 3 中使用了缓存处理器(cache handlers)来缓存动态组件和事件处理器。这样在多次渲染中,如果组件和事件处理器没有发生变化,可以直接复用之前的结果,减少了重复计算的开销。
Vue 3 在处理事件侦听器时引入了更高效的机制。在处理相同事件的不同侦听器时,Vue 3 会尽量合并它们,以减少事件绑定和解绑时的性能开销。
Vue 3 引入了 patch flag 的概念,用于快速判断两个节点是否相同,从而避免不必要的深度对比。Patch flag 会记录节点的特定信息,例如节点的类型、属性、子节点等,以帮助更快速地进行比较。
Vue 3 支持 Fragments,使得组件的模板中可以有多个根节点。这样在渲染时就不需要额外的根节点包裹,减少了 Virtual DOM 的节点数量。对于一些 slot 的合并也进行了优化,减少了 Virtual DOM 的操作。
Vue 3 引入了块级树遍历(Block Tree Walking)的概念,对于动态组件和模板的动态块,Vue 3 能够更高效地处理。
Vue 3 对 TypeScript 提供了更好的支持,它的代码库和构建工具都是用 TypeScript 编写的,同时也提供了一些特性来优化 TypeScript 开发体验。
自定义模板块的 TypeScript 支持:Vue 3 中对于使用自定义模板块的 TypeScript 支持更加友好,例如对于 defineProps、defineEmits 等。
在 Vue 3 中,v-model
的使用方式相对于 Vue 2 有一些变化,主要是为了提供更灵活的用法和更好的支持。
v-model
的参数化:在 Vue 3 中,v-model
可以接收参数,可以自定义 prop 和 event 的名称。这使得 v-model
在组件内的用法更加灵活,不再限定于默认的 modelValue
和 update:modelValue
。
<template>
<MyComponent v-model:customProp="value" />
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const value = ref('');
return { value };
}
};
</script>
在上面的例子中,v-model
使用了 :customProp
,这样在 MyComponent
内部会使用 customProp
作为 prop 名称和 update:customProp
作为事件名称。
v-model
的修饰符支持:Vue 3 引入了修饰符 API,这使得你可以在 v-model
上使用修饰符,例如 .lazy
和 .trim
。
<template>
<input v-model.lazy="value" />
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const value = ref('');
return { value };
}
};
</script>
在上面的例子中,.lazy
修饰符表示在 input
的 change
事件之后更新数据,而不是在 input
的 input
事件触发时即时更新数据。
v-model
在自定义组件上的使用:在 Vue 3 中,自定义组件上的 v-model
更加灵活。你可以通过 model
选项自定义 prop 和 event 的名称。
<template>
<MyComponent v-model:customProp="value" />
</template>
<script>
import { ref } from 'vue';
export default {
props: {
customProp: String
},
emits: ['update:customProp'],
setup(props) {
const value = ref('');
return {
value,
// 通过手动触发事件来更新父组件的数据
updateValue(newValue) {
props['update:customProp'](newValue);
}
};
}
};
</script>
Vue 3 在性能、可维护性、功能和 TypeScript 支持等方面都有很多的提升,但也需要开发者适应一些新的概念和 API。如果要升级到 Vue 3,建议查看官方迁移指南。