vue2与vue3的区别

140 阅读9分钟

一、生命周期的区别

vue2:

  1. beforeCreate

    • 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。此时组件的选项对象还未被处理,也就是说不能访问到组件的 data、computed、methods 和 watch 上的方法和数据。
  2. created

    • 在实例创建完成后被立即调用。在这一步,实例已完成数据观测(data observer)、属性和方法的运算、watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
  3. beforeMount

    • 在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。此时模板编译/挂载过程还没有开始,但是 render 函数已经执行过了,$el 属性还没有被新创建的 vm.el 替换,因此你仍然看到的是旧元素。
  4. mounted

    • el 被新创建的 vm.el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当mounted被调用时vm.el 也在文档内。该钩子在服务器端渲染期间不被调用。
  5. beforeUpdate

    • 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
  6. updated

    • 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用这个钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。但是要避免更改状态,因为这可能会导致无限循环的更新。
  7. beforeDestroy

    • 实例销毁之前调用。在这一步,实例仍然完全可用。
  8. destroyed

    • Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也都会被销毁。

vue3:

  1. setup() :

    • setup() 函数是组件中使用 Composition API 的入口点。
    • 在组件的beforeCreatecreated生命周期钩子之间被调用。但是,请注意setup()函数中并不直接创建datamethods,而是通过返回的对象来提供组件的状态(响应式引用)和方法。
    • setup()函数中,你不能直接访问组件实例(即this),因为它还没有被创建。但是,你可以通过setup()函数的第二个参数context来访问attrsslotsemit等。
  2. onBeforeMount() :

    • 这是一个在组件挂载到DOM之前被调用的生命周期钩子。
    • 在服务器端渲染期间不会被调用。
    • 此时,模板编译/挂载过程还没有开始,但相关的render函数已经被调用(如果组件有render函数的话)。
  3. onMounted() :

    • 这是一个在组件挂载到DOM后调用的生命周期钩子。
    • 此时,组件的$el已经被新创建的DOM替换,并挂载到了实例上。
    • 这个钩子适合执行依赖于DOM的操作,比如通过DOM API来直接操作页面元素。
  4. onBeforeUpdate() :

    • 这是一个在组件的响应式状态更新之前调用的钩子。
    • 它发生在虚拟DOM打补丁之前,允许你访问更新前的DOM状态。
    • 这可以用于执行一些在数据更新前的清理工作,比如移除事件监听器等。
  5. onUpdated() :

    • 这是一个在组件的响应式状态更新之后调用的钩子。
    • 此时,组件的DOM已经根据新的状态被更新。
    • 这个钩子适合执行依赖于更新后DOM的操作。
  6. onBeforeUnmount() :

    • 这是一个在组件的卸载过程开始之前调用的钩子。
    • 它允许你执行一些清理工作,比如移除全局的事件监听器或定时器等。
    • 这个钩子在组件被销毁之前调用,但此时组件仍然完全可用。
  7. onUnmounted() :

    • 这是一个在组件的卸载过程完成后调用的钩子。
    • 此时,组件的实例以及所有的子组件实例都已经被销毁,所有的事件监听器和定时器都被移除,所有的指令也都已经被解绑。
    • 这个钩子通常用于执行一些最终的清理工作,确保不会有内存泄漏等问题。

二、代码组织方式

  • 选项式API(Vue2)

    • 使用Vue实例的选项对象来组织代码,如datamethodscomputedwatch等,这些选项被定义在Vue实例的根对象上。
    • 这种方式将组件的逻辑分散在多个选项中,对于大型组件来说,可能导致代码难以理解和维护。
  • 组合式API(Vue3)

    • 引入了一个新的setup函数,该函数作为组件中所有组合式API的入口点。
    • setup函数中,你可以使用Vue提供的组合式API(如refreactivecomputedwatch等)来定义响应式状态、计算属性和侦听器。
    • 这种方式允许你将组件的逻辑按照功能进行分组,使得代码更加模块化和可重用。

三、根节点的区别

Vue2的单根节点

  1. 限制:在Vue2中,每个组件的<template>标签内只允许存在一个根节点。这是因为Vue2的模板编译器在编译模板时,会将模板转换成一个render函数,而这个render函数需要返回一个单一的根节点。如果组件有多个根节点,则无法满足这个要求,会导致编译错误。
  2. 影响:这种限制要求开发者在编写Vue2组件时,必须确保每个组件的模板只有一个根元素。如果需要多个元素并列显示,必须使用一个父元素(如<div>)将它们包裹起来。

Vue3的多根节点

  1. 支持:Vue3引入了Fragment(片段)的概念,允许组件的模板中存在多个根节点。这是通过Vue3的模板编译器和渲染机制的改进实现的,不再需要将模板转换成单一的render函数。
  2. 灵活性:Vue3的这一改进提高了组件模板的灵活性。开发者可以更加自由地组织模板结构,无需担心单根节点的限制。这有助于编写更清晰、更易于维护的组件代码。
  3. 使用方式:在Vue3中,可以使用<template>标签或Fragment组件来包裹多个根元素。例如,<template><div>...</div><div>...</div></template>在Vue3中是有效的,而在Vue2中则会报错。
  4. 总结:Vue2和Vue3在组件模板的根节点方面存在显著差异。Vue2要求每个组件模板只能有一个根节点,而Vue3则支持多个根节点,这主要得益于Vue3在模板编译器和渲染机制上的改进。这一变化提高了Vue3组件的灵活性和可维护性,使得开发者可以更加自由地组织模板结构。

四、响应式实现原理

1. 数据劫持方法

Vue2

  • Vue2的响应式系统是基于Object.defineProperty方法实现的。这种方法允许Vue在对象属性被访问或修改时执行特定的操作(getter/setter)。
  • 当Vue实例创建时,它会遍历data选项中的所有属性,并使用Object.defineProperty将它们转换成getter/setter。这意味着只有在Vue初始化时声明在data中的属性才是响应式的。
  • 局限性:由于Object.defineProperty只能监听属性的值变化,对于对象的新增属性或数组通过索引添加的元素,无法自动使其成为响应式的,除非使用Vue.set或Vue.delete等方法。

Vue3

  • Vue3则采用了ES6中的Proxy对象来实现响应式系统。Proxy能够拦截对象的多种操作,如属性读取、设置、枚举、函数调用等,使得Vue3可以更全面地控制对象属性的变化。
  • Proxy不仅能代理对象的现有属性,还能代理对象本身,从而实现了对新增属性的监听以及对数组通过索引变化的监听。
  • 这种方式相较于Object.defineProperty更加灵活和强大,同时解决了Vue2中一些无法直接监听场景的问题。

2. 监听机制

Vue2

  • Vue2通过Object.defineProperty对data属性进行监听,结合观察者模式(发布-订阅模式),在数据变化时通知所有依赖的视图进行更新。
  • Vue2中使用了DepWatcher类来实现依赖收集和触发更新。每个响应式属性都有一个Dep实例来存储其所有依赖的Watcher。当属性变化时,Dep会通知所有Watcher执行更新操作。

Vue3

  • Vue3同样采用了观察者模式,但在细节上有所优化。其响应式系统通过Proxy对象的handler参数拦截对对象的操作,并结合内部的依赖收集和触发机制来实现数据变化的响应。
  • Vue3还引入了effect函数来简化依赖收集和触发更新的过程。使用effect可以自动将函数的执行结果与其访问的响应式数据关联起来,形成依赖关系。当数据变化时,自动重新执行相关的effect函数以更新视图。

五、vue3内置Teleport组件

  • 在某些特定情境下,需要子组件挂载到父组件以外的地方可以使用vue3的内置组件Teleport,来避免父组件在css样式上带来的干扰。
<button @click="open = true">Open Modal</button>

<Teleport to="body">
    <div v-if="open" class="modal">
        <p>Hello from the modal!</p>
        <button @click="open = false">Close</button>
    </div>
</Teleport>
  • <Teleport> 接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue“把以下模板片段传送到 body 标签下”。

六、mixin和hooks在逻辑复用上的区别

  • 在 Vue 3 中,组合 API (Hooks)提供了更为灵活和高效的方式来复用逻辑,相比于 Vue 2 的 mixins,组合 API 减少了潜在的命名冲突和上下文不明确的问题,使得代码更容易理解和维护。这使得 Vue 3 更适合大型应用的开发和逻辑复用,下面详细比较这两种方式在 Vue 2 和 Vue 3 中的差异。

image.png