-
生命周期对比
- Vue2生命周期
- beforeCreate:实例刚在内存中被创建出来,此时数据观测(data observer)和事件机制(event/watcher)都未初始化。主要用于一些插件的初始化工作,如在这个阶段可以引入一些全局的工具函数等。
- created:实例已经创建完成,数据观测(data observer)和事件机制(event/watcher)已经初始化。可以进行数据的获取、初始化一些非DOM相关的数据等,比如发起Ajax请求获取数据来填充组件的数据。
- beforeMount:在挂载开始之前被调用,相关的
render
函数首次被调用。可以对挂载前的DOM结构进行最后的调整,或者在挂载前对数据做一些检查和预处理。 - mounted:实例挂载到DOM上,
el
被新创建的vm.$el
替换。这是最常使用的生命周期钩子,用于操作DOM元素,如添加事件监听器、执行基于DOM的动画等。 - beforeUpdate:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。可以在这里比较更新前后的数据,用于优化更新逻辑或者记录数据变化的日志。
- updated:由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。应该避免在这个钩子中修改数据,否则可能会导致无限循环更新。主要用于在DOM更新后执行一些依赖于DOM状态的操作,如获取更新后的DOM元素尺寸等。
- beforeDestroy:实例销毁之前调用。在这个阶段可以清除定时器、解绑事件监听器、取消订阅等操作,以防止内存泄漏。
- destroyed:实例销毁后调用,此时所有的事件监听器和子组件都已经被移除。
- Vue3生命周期
- setup:这是Vue3新增的函数,在组件创建之前执行,用于组合式API的入口。在这里可以定义响应式数据、计算属性、方法等,返回的对象属性会暴露给模板和其他组件选项。
- onBeforeMount:和Vue2的
beforeMount
类似,在组件挂载到DOM之前调用。 - onMounted:和Vue2的
mounted
类似,组件挂载到DOM后调用。 - onBeforeUpdate:和Vue2的
beforeUpdate
类似,在组件更新之前调用。 - onUpdated:和Vue2的
updated
类似,在组件更新之后调用。 - onBeforeUnmount:和Vue2的
beforeDestroy
类似,在组件卸载之前调用。 - onUnmounted:和Vue2的
destroyed
类似,在组件卸载之后调用。
- 主要区别
- Vue3使用组合式API,将相关逻辑通过
setup
函数集中管理,生命周期钩子变成了基于函数的导入方式,使得代码逻辑更清晰,更易于理解和维护。而Vue2是通过选项对象的方式定义生命周期钩子。
- Vue3使用组合式API,将相关逻辑通过
- Vue2生命周期
-
全局钩子函数与组件钩子函数及应用场景
- Vue2
- 全局钩子函数:例如
Vue.mixin
可以定义全局混入,它会影响到所有组件。应用场景包括全局的错误处理、全局的数据预处理等。 - 组件钩子函数:就是前面提到的各个生命周期钩子函数,应用场景主要是围绕组件自身的初始化、更新和销毁过程中的各种操作。
- 全局钩子函数:例如
- Vue3
- 全局钩子函数:可以通过
app.config.globalProperties
来添加全局属性和方法,这些属性和方法可以在任何组件中访问。应用场景如添加全局的工具函数、全局状态管理等。 - 组件钩子函数:通过导入
onMounted
等生命周期钩子函数在setup
函数中使用,应用场景和Vue2类似,用于组件特定阶段的操作。
- 全局钩子函数:可以通过
- Vue2
-
自定义指令用法
- Vue2
- 定义:通过
Vue.directive('directiveName', { inserted: function (el, binding, vnode) {... } })
来定义全局自定义指令,其中inserted
是指令插入到DOM时触发的钩子函数。可以在对象中定义bind
(指令绑定到元素时调用)、update
(组件更新且指令所在的模板更新时调用)等钩子。 - 应用:在模板中使用
v - directiveName
来应用指令。 - 场景:用于操作DOM元素,如实现权限控制下的元素显示隐藏、实现拖拽功能等。
- 定义:通过
- Vue3
- 定义:通过
const myDirective = { mounted(el, binding, vnode) {... }, updated(el, binding, vnode) {... } }
定义指令对象,然后使用app.directive('directiveName', myDirective)
来注册指令(app
是应用实例)。 - 应用:和Vue2类似,在模板中使用
v - directiveName
。 - 场景:同样用于操作DOM,如自定义表单验证的指令等。
- 定义:通过
- Vue2
-
混入与组合式API的区别及Vue3中的混入
- 区别
- 混入(Vue2和Vue3):是一种分发Vue组件中可复用功能的方式。它通过将一个对象的选项(如
data
、methods
、mounted
等)合并到组件选项中来实现复用。但是可能会导致命名冲突,并且逻辑分散,难以理解组件真正的行为来源。 - 组合式API(Vue3):将组件的逻辑关注点(如响应式数据、计算属性、方法等)通过
setup
函数组合在一起。使得代码结构更加清晰,逻辑更易追踪,避免了混入的命名冲突问题。
- 混入(Vue2和Vue3):是一种分发Vue组件中可复用功能的方式。它通过将一个对象的选项(如
- Vue3中的混入:Vue3仍然支持混入,但是在新的项目中推荐使用组合式API。混入的用法和Vue2类似,通过
Vue.mixin
(全局)或者在组件选项中定义mixins
属性(局部)来使用。
- 区别
-
Slot插槽用法对比
- Vue2
- 默认插槽:在子组件中使用
<slot></slot>
定义插槽位置,在父组件使用子组件标签内的内容填充。例如:
<!-- 子组件 --> <template> <div> <slot></slot> </div> </template> <!-- 父组件 --> <template> <child - component> <p>这是插槽内容</p> </child - component> </template>
- 具名插槽:在子组件中使用
<slot name="slotName"></slot>
定义具名插槽,在父组件通过v - slot:slotName
或者#slotName
(缩写)来指定内容填充。
- 默认插槽:在子组件中使用
- Vue3
- 默认插槽:基本用法和Vue2类似,不过在父组件使用
v - slot
的缩写#
更推荐,例如<child - component> #default { <p>这是插槽内容</p> } </child - component>
。 - 具名插槽:和Vue2类似,但是缩写形式
#slotName
使用更广泛,并且语法更加简洁统一。同时,Vue3支持动态插槽名,通过v - slot:[dynamicSlotName]
或者#[dynamicSlotName]
来实现。
- 默认插槽:基本用法和Vue2类似,不过在父组件使用
- 改进:Vue3的插槽语法更加简洁明了,特别是缩写形式的广泛使用,使得模板代码更易读。动态插槽名的支持也增加了灵活性。
- Vue2
-
全局组件注册与局部组件注册用法
- Vue2
- 全局组件注册:使用
Vue.component('component - name', Component)
来注册全局组件,其中Component
是组件的构造函数或者对象。注册后可以在任何组件的模板中使用<component - name></component - name>
。 - 局部组件注册:在组件选项中定义
components
属性,例如components: { 'component - name': Component }
,然后在该组件的模板中使用。
- 全局组件注册:使用
- Vue3
- 全局组件注册:通过
app.component('component - name', Component)
来注册全局组件(app
是应用实例),使用方式和Vue2类似。 - 局部组件注册:和Vue2类似,在
setup
函数或者组件选项的components
属性中定义组件。
- 全局组件注册:通过
- Vue2
-
数据的传递方式
- Vue2
- 父子组件数据传递:父组件通过
props
向子组件传递数据,子组件通过$emit
触发事件来向父组件传递数据。 - 兄弟组件数据传递:可以通过共同的父组件作为中间人,子组件A通过
$emit
向父组件传递数据,父组件再通过props
将数据传递给子组件B。 - 跨层级数据传递(非Vuex):可以使用
provide
和inject
,父组件通过provide
提供数据,子孙组件通过inject
获取数据。
- 父子组件数据传递:父组件通过
- Vue3
- 父子组件数据传递:和Vue2类似,通过
props
和emit
。不过emit
的使用方式在setup
函数中有一些语法上的变化,需要通过const emit = defineEmits(['event - name'])
来获取emit
函数。 - 兄弟组件数据传递:同样可以借助共同的父组件或者使用Vuex等状态管理工具。
- 跨层级数据传递(非Vuex):和Vue2类似,通过
provide
和inject
,并且在setup
函数中使用更加方便,const data = inject('data - key')
。
- 父子组件数据传递:和Vue2类似,通过
- Vue2
-
DOM操作的方式以及
forceUpdate
强制更新- Vue2
- DOM操作:在
mounted
等生命周期钩子中可以通过this.$el
来获取组件的根DOM元素,然后使用原生DOM API或者像jQuery
这样的库来操作DOM。 - 强制更新:可以通过
this.$forceUpdate()
来强制组件重新渲染,不过应该谨慎使用,因为这会绕过响应式系统的优化。
- DOM操作:在
- Vue3
- DOM操作:在
onMounted
等生命周期钩子中获取ref
引用的DOM元素来操作,例如const myDom = ref(null);
在模板中通过ref="myDom"
绑定,在onMounted
中可以通过myDom.value
来操作DOM。 - 强制更新:Vue3没有
$forceUpdate
方法。一般情况下,如果数据是响应式的,数据变化会自动触发更新。如果确实需要强制更新,可以通过修改一个响应式数据来触发更新,例如const triggerUpdate = ref(0); triggerUpdate.value++;
。
- DOM操作:在
- Vue2
-
响应式系统
- Vue2
- 使用
Object.defineProperty()
来进行数据劫持,实现响应式。这种方式对于对象新增属性需要使用Vue.set
方法才能保证其响应式,对于数组的一些变异方法(如push
、pop
等)进行了重写来实现响应式更新。
- 使用
- Vue3
- 采用
Proxy
对象来实现响应式。它可以直接代理整个对象,包括新增属性和删除属性都能很好地响应。这使得响应式数据的处理更加直观和灵活,减少了一些特殊情况的处理。例如:
const data = { name: 'John', age: 30 }; const proxyData = new Proxy(data, { get(target, key) { console.log(`Getting ${key}`); return target[key]; }, set(target, key, value) { console.log(`Setting ${key} to ${value}`); target[key] = value; return true; } });
- 改进点:
Proxy
-based的响应式系统解决了Vue2响应式的一些限制,如新增属性需要特殊处理的问题,提供了更完整的对象代理能力,使得响应式数据的操作更加符合直觉。
- 采用
- Vue2
-
生命周期钩子
- Vue2
- 生命周期钩子是通过选项对象的方式定义,如
created
、mounted
等钩子函数是在组件选项中定义。
- 生命周期钩子是通过选项对象的方式定义,如
- Vue3
- 采用组合式API,新增
setup
函数作为组件逻辑的入口,生命周期钩子变成了基于函数的导入方式,如onMounted
、onUpdated
等。这些函数需要从vue
模块中导入并在setup
函数中使用。例如:
import { onMounted } from 'vue'; export default { setup() { onMounted(() => { console.log('Component mounted'); }); return {}; } };
- 改进点:组合式API的生命周期钩子使得代码逻辑更加集中,避免了在选项对象中分散定义生命周期钩子的情况,使得组件的逻辑结构更加清晰,尤其是在复杂组件中更容易理解和维护。
- 采用组合式API,新增
- Vue2
-
模板语法(部分改进)
- Vue2
- 已经有比较成熟的模板语法,如
v - if
、v - for
、v - bind
(缩写为:
)和v - on
(缩写为@
)等指令来实现条件渲染、列表渲染和事件绑定等功能。
- 已经有比较成熟的模板语法,如
- Vue3
- 延续了大部分Vue2的模板语法,但在一些细节上有所改进。例如,在插槽语法上更加简洁,
v - slot
的缩写#
被更广泛地使用,并且支持动态插槽名,像v - slot:[dynamicSlotName]
或者#[dynamicSlotName]
这种形式。 - 改进点:插槽语法的改进使得模板代码更加简洁明了,动态插槽名的支持增加了组件复用的灵活性,让开发者能够更方便地构建可复用的组件。
- 延续了大部分Vue2的模板语法,但在一些细节上有所改进。例如,在插槽语法上更加简洁,
- Vue2
-
组件通信与数据传递
- Vue2
- 父子组件通过
props
和$emit
进行通信,兄弟组件可以借助共同的父组件或者事件总线来传递数据,跨层级组件(非Vuex)可以使用provide
和inject
。
- 父子组件通过
- Vue3
- 基本的组件通信方式和Vue2类似,不过在
setup
函数中emit
的使用方式有所变化,需要通过const emit = defineEmits(['event - name'])
来获取emit
函数。provide
和inject
在setup
函数中使用更加方便,例如const data = inject('data - key')
。 - 改进点:在组合式API下,组件通信的相关操作在
setup
函数中更加统一和规范,使得数据传递的逻辑更加清晰,尤其是在复杂的组件嵌套和通信场景下更容易管理。
- 基本的组件通信方式和Vue2类似,不过在
- Vue2
-
性能优化
- Vue2
- 有一些性能优化的策略,如虚拟DOM的比较和更新。在更新组件时,会对新旧虚拟DOM树进行比较,通过最小化DOM操作来提高性能。
- Vue3
- 进一步优化了虚拟DOM的更新算法,提高了更新性能。同时,由于响应式系统的改进,数据更新和组件重新渲染的效率也有所提升。
- 改进点:通过对虚拟DOM算法的优化和响应式系统的升级,Vue3在性能上有了显著的提升,尤其是在大型应用和频繁数据更新的场景下,能够提供更流畅的用户体验。
- Vue2
-
TypeScript支持
- Vue2
- 对TypeScript的支持相对较弱,需要通过一些额外的工具和配置来更好地使用TypeScript,如
vue - class - component
库来以类组件的方式编写Vue2组件,以方便类型检查。
- 对TypeScript的支持相对较弱,需要通过一些额外的工具和配置来更好地使用TypeScript,如
- Vue3
- 从设计之初就考虑了TypeScript的支持,组合式API与TypeScript配合得很好。在
setup
函数中,可以方便地定义和使用类型,使得代码的类型安全性大大提高。例如:
import { ref } from 'vue'; export default { setup() { const count: Ref<number> = ref(0); return { count }; } };
- 改进点:更好的TypeScript支持使得大型项目的开发更加稳健,减少了类型相关的错误,提高了代码的可维护性和可读性,符合现代前端开发对类型安全的要求。
- 从设计之初就考虑了TypeScript的支持,组合式API与TypeScript配合得很好。在
- Vue2
-
Vue2中的
activated
和deactivated
- 定义
activated
:这是一个组件生命周期钩子函数。当一个被keep - alive
包裹的组件被激活时调用,即从缓存中重新显示时触发。deactivated
:同样是组件生命周期钩子函数。当一个被keep - alive
包裹的组件失活时调用,即组件被缓存起来,从页面上隐藏时触发。
- 应用场景
- 数据缓存与恢复:在
activated
钩子中,可以检查组件的状态是否需要更新或恢复。例如,当一个包含表单的组件被缓存后重新激活,可能需要重新获取数据或者恢复之前的表单输入状态。假设我们有一个用户编辑表单组件:
export default { data() { return { user: { name: '', age: 0 } }; }, activated() { // 重新获取用户数据,更新user对象 this.fetchUser(); }, methods: { fetchUser() { // 模拟获取用户数据的API调用 const userData = api.getUser(); this.user.name = userData.name; this.user.age = userData.age; } } };
- 组件状态管理:用于处理组件内部状态,如定时器、动画状态等。例如,在
activated
中启动一个定时器,在deactivated
中清除定时器,避免内存泄漏。
export default { data() { return { timer: null }; }, activated() { this.timer = setInterval(() => { // 执行定时任务 console.log('Component is active'); }, 1000); }, deactivated() { clearInterval(this.timer); } };
- 数据缓存与恢复:在
- 定义
-
Vue3中的
activated
和deactivated
- 定义
- 从功能上来说和Vue2类似。在Vue3中,这些钩子函数是在
setup
函数内部通过导入来使用的。需要从vue
模块中导入onActivated
和onDeactivated
函数。
- 从功能上来说和Vue2类似。在Vue3中,这些钩子函数是在
- 应用场景
- 与Vue2类似的数据缓存和恢复:例如,对于一个包含大量数据请求的复杂组件,在
onActivated
中可以触发数据的重新加载。假设我们有一个数据展示组件:
import { onActivated, ref } from 'vue'; export default { setup() { const dataList = ref([]); onActivated(() => { // 重新获取数据列表 getDataList().then((response) => { dataList.value = response; }); }); return { dataList }; } };
- 资源管理和性能优化:和Vue2一样,在
onDeactivated
中可以清理一些不必要的资源占用,如停止正在运行的动画、释放网络请求等。例如,在一个具有动画效果的组件中:
import { onDeactivated, ref } from 'vue'; export default { setup() { const animationRunning = ref(true); onDeactivated(() => { animationRunning.value = false; // 清理动画相关资源 clearAnimationResources(); }); return { animationRunning }; } };
- 与Vue2类似的数据缓存和恢复:例如,对于一个包含大量数据请求的复杂组件,在
- 定义