Vue2与Vue3区别

发布时间:2024年01月06日

Vue2与Vue3区别

1.性能优化

  • Vue3: Vue3 在性能方面有很大的改进。它引入了响应式系统的重写,使用 Proxy 代替 Object.defineProperty,从而提高了性能。另外,编译器优化、懒加载等新特性也有助于提高应用程序性能。
  • Vue2: Vue2 使用 Object.defineProperty 来实现响应式数据,相比 Vue3,它的性能稍显滞后。

2.组件的创建方式(组合式API)

  • Vue3: Vue3 支持组合式API,这是一种新的组件设计方式,可以更灵活地组织代码,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。
  • Vue2: Vue2 使用选项式 API,较为传统,有时在大型应用中难以维护。一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。
// Vue3 中的组件
<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    return {
      count,
      increment: () => { count.value++ },
    };
  },
};
</script>
// Vue2 中的组件
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      this.count++;
    },
  },
};
</script>

3.生命周期

Vue3中取消了beforeCreated和created,添加了setup函数,其负责设置组件的初始状态、响应式数据和执行其他逻辑。它在组件实例被创建之前被调用,并且接收两个参数:propscontext

  • props 参数表示组件接收到的属性(props),并且是只读的。
  • context 参数包含了一些常用的属性和方法,如 attrsslotsemit 等。

由于setup就是初始化时执行的函数,所以可以替代beforeCreated和created的效用

常用生命周期对比:

vue2vue3
beforeCreate
created
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted

并且再Vue3中提供了onServerPrefetch钩子,若在其中返回了一个Promise,服务端就可以等待Promise完成后再对该组件进行渲染

4.全局 API 的变化

  • Vue3: Vue3 中一些全局 API 发生了变化,例如 Vue.component 变成了 app.component
  • Vue2: 在 Vue2 中,这些全局 API 使用 Vue 实例进行访问。
// Vue3 中的全局组件注册
const app = createApp(App);
app.component('my-component', MyComponent);
app.mount('#app');
// Vue2 中的全局组件注册
Vue.component('my-component', MyComponent);
new Vue({
  el: '#app',
  render: h => h(App),
});

5.Teleport(传送门)的引入

传统的 Vue 组件会被渲染到其父组件的 DOM 结构内,但是有时我们希望将组件渲染到 DOM 结构的不同位置,而不需要改变其父组件的布局。这就是 Teleport 的作用所在。

  • Vue3: Vue3 引入了 Teleport,用于在组件内部的任何地方渲染内容到 DOM 中的其他位置,这在处理模态框等场景时非常有用。
  • Vue2: Vue2 中没有 Teleport 的概念。
<!-- Vue3 中的 Teleport -->
<teleport to="body">
 <Modal />
</teleport>

Teleport 组件将 Modal 组件渲染到了页面的 body 元素下。无论在组件内部的哪个位置使用 Teleport,最终 Modal 组件都会被渲染到 body 元素下,而不是组件的父容器内部。

通过使用 Teleport,你可以将组件渲染到任何合法的 DOM 元素位置,而不局限于其父组件的 DOM 结构。这对于实现全局的弹窗、模态框或通知等功能非常有用。

需要注意的是,Teleport 需要配合 to 属性来指定组件渲染的目标位置,该位置可以是一个有效的 CSS 选择器,或者是特殊的预定义值,如 "body""#app" 等。

另外,需要确保目标位置在组件被挂载的时候是存在的,否则 Teleport 会抛出警告并在渲染期间失效。

总结来说,Teleport 组件使你能够非常灵活地将组件渲染到任意位置,而不受其父组件的限制。这为处理全局弹窗、模态框等提供了方便的解决方案。

6.多个根节点的支持

  • Vue3: Vue3 支持组件有多个根节点。在vue3中支持了多个根组件的写法,原理是在编译阶段新增了判断,如果当前组件不只一个根元素,就添加一个 fragment 组件把这个多根组件的结构给包起来,相当于这个组件还是只有一个根节点
  • Vue2: Vue2 中组件只能有一个根节点。vue2模板最后会被转换为VDOM,而VDOM是一个树形结构,所以需要唯一的根元素
<!-- Vue3 中的多个根节点 -->
<template>
  <div></div>
  <div></div>
</template>
<!-- Vue2 中只能有一个根节点 -->
<template>
  <div>
    <p></p>
  </div>
</template>

7.异步组件加载(Suspense 组件)

  • Vue3: Vue3 引入了 Suspense 组件,用于处理异步组件的加载状态,使得在异步操作完成之前可以展示备用内容。
  • Vue2: Vue2 中没有专门处理异步组件加载状态的机制。

在 Vue 3 中,异步组件加载可以使用 Suspense 组件和 async setup 功能来实现。Suspense 组件可以用于处理异步组件的加载和显示占位符内容,而 async setup 可以在异步组件加载完成后执行相关逻辑。

下面是使用 Suspenseasync setup 的示例:

首先,在父组件中使用 Suspense 包裹需要异步加载的子组件,并设置一个占位符(fallback)内容:

<template>
  <Suspense>
    <template #default>
      <!-- 异步加载的子组件 -->
      <AsyncComponent />
    </template>
    <template #fallback>
      <!-- 加载时的占位符内容 -->
      <div>Loading...</div>
    </template>
  </Suspense>
</template>

然后,在异步加载的子组件中使用 async setup 来定义组件的逻辑:

export default {
  async setup() {
    // 异步加载资源或执行其他异步操作
    await someAsyncOperation();

    // 返回组件的响应式数据和方法
    return {
      data: reactive({}),
      method() {
        // ...
      }
    };
  }
};
</script>

当父组件渲染时,Suspense 组件会检测到子组件的异步加载,并显示占位符内容(“Loading…”)。一旦异步加载完成,Suspense 组件会自动切换到子组件的内容,并显示子组件的内容。

通过使用 Suspenseasync setup,你可以更方便地处理异步组件加载过程中的状态,并在加载完成后执行相关逻辑。

8.全局状态管理

  • Vue3: Vue3 中引入了 reactiveref 函数,使得在应用程序中管理全局状态变得更加简单。新的响应式系统使得 Vuex(Vue 的状态管理库)的需求降低。
  • Vue2: 在 Vue2 中,通常需要使用 Vuex 来管理全局状态,但在较小的应用中可能会显得繁琐。
// Vue3 中的全局状态管理
import { reactive } from 'vue';

const state = reactive({
  count: 0,
});

9.自定义指令的变化

  • Vue3: Vue3 中自定义指令的 API 发生了变化,使用 beforeMount 替代了 Vue2 中的 bindinserted 钩子。
  • Vue2: Vue2 中的自定义指令有 bindinserted 两个钩子。
// Vue3 中的自定义指令
app.directive('自定义指令', {
  beforeMount(el, binding) {
    // ...
  },
});
// Vue2 中的自定义指令
Vue.directive('自定义指令', {
  bind(el, binding) {
    // ...
  },
  inserted(el, binding) {
    // ...
  },
});

10.编译器的变化

  • Vue3: Vue3 的编译器进行了改进,支持更好的 Tree-shaking,减小了应用的体积。
  • Vue2: Vue2 中的编译器不够灵活,难以实现有效的 Tree-shaking。

11.Typescript 支持

  • Vue3: Vue3 对 Typescript 提供了更好的支持,使得在项目中使用 Typescript 更加方便。
  • Vue2: Vue2 也支持 Typescript,但在一些方面可能不如 Vue3 灵活。
// Vue3 中的组件使用 Typescript
<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  data() {
    return {
      count: ref(0),
    };
  },
});
</script>

12.事件处理的变化

  • Vue3: Vue3 中事件处理的参数变得更加一致,例如,事件参数现在总是作为第一个参数传递给处理函数。
  • Vue2: 在 Vue2 中,事件处理的参数可能会有所不同,有时是事件对象,有时是自定义参数。
// Vue3 中的事件处理
<template>
  <button @click="handleClick">Click me</button>
</template>

<script>
export default {
  methods: {
    handleClick(event,other) {
      // event 是事件对象
    },
  },
};
</script>
// Vue2 中的事件处理
<template>
  <button @click="handleClick">Click me</button>
</template>

<script>
export default {
  methods: {
    handleClick(customArg, event) {
      // event 是事件对象
    },
  },
};
</script>

13.TSX 支持

  • Vue3: Vue3 对 TSX(TypeScript JSX)提供了更好的支持,允许你使用 JSX 语法编写组件。
  • Vue2: Vue2 不太友好地支持 TSX,使用的时候可能会遇到一些问题。
// Vue3 中的 TSX 支持
<script lang="tsx">
import { defineComponent } from 'vue';

export default defineComponent({
  render() {
    return <div>Hello, TSX!</div>;
  },
});
</script>

14.Emits 选项

  • Vue3: Vue3 引入了 emits 选项,用于声明组件可以触发的事件,提高了事件的类型安全性。
  • Vue2: Vue2 中并没有显式的方式来声明组件可以触发的事件。
// Vue3 中的 emits 选项
<script>
export default {
  emits: ['custom-event'],
  methods: {
    handleClick() {
      this.$emit('custom-event', /* data */);
    },
  },
};
</script>

15.v-model 的改进

  • Vue3: Vue3 对 v-model 进行了改进,允许你自定义属性和事件,使得在使用 v-model 时更加灵活。
  • Vue2: 在 Vue2 中,v-model 的定制性相对较低。
<!-- Vue3 中的 v-model 定制 -->
<script>
export default {
  model: {
    prop: 'checkedValue',
    event: 'change',
  },
  props: {
    checkedValue: Boolean,
  },
};
</script>

16.打包优化

Tree-shaking:模块打包 webpack、rollup 等中的概念。移除 JavaScript 上下文中未引用的代码。主要依赖于 import 和 export 语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。

以 nextTick 为例子,在 Vue2 中,全局API暴露在Vue实例上,即使未使用,也无法通过 tree-shaking 进行消除。

import Vue from 'vue';
 
Vue.nextTick(() => {
  // 一些和DOM有关的东西
});

vue3:

import { nextTick } from 'vue';   // 显式导入
 
nextTick(() => {
  // 一些和DOM有关的东西
});

通过这一更改,只要模块绑定器支持 tree-shaking,则Vue应用程序中未使用的 api 将从最终的捆绑包中消除,获得最佳文件大小。

受此更改影响的全局API如下所示。

  • Vue.nextTick
  • Vue.observable (用 Vue.reactive 替换)
  • Vue.version
  • Vue.compile (仅全构建)
  • Vue.set (仅兼容构建)
  • Vue.delete (仅兼容构建)

内部API也有诸如 transition、v-model 等标签或者指令被命名导出。只有在程序真正使用才会被捆绑打包。Vue3 将所有运行功能打包也只有约22.5kb,比 Vue2 轻量很多。

17.动态注入(Provide/Inject)的改进

  • Vue3: Vue3 改进了动态注入的性能,并提供了更好的 TypeScript 支持。
  • Vue2: 在 Vue2 中,动态注入可能会在性能方面有一些问题,而且在 TypeScript 中的类型推断相对较弱。
// Vue3 中的动态注入
// Parent.vue
<script>
import { provide } from 'vue';

export default {
  setup() {
    provide('message', 'Hello from parent!');
  },
};
</script>

// Child.vue
<script>
import { inject } from 'vue';

export default {
  setup() {
    const message = inject('message');
    // ...
  },
};
</script>

18.弃用和移除的功能

  • Vue3: Vue3 引入了一些新的 API 并弃用了一些在 Vue2 中存在的功能,例如 $on, $off, $once 等方法被弃用,使用 on, off, once 替代。
  • Vue2: 在 Vue2 中这些弃用的方法仍然是可用的。
// Vue3 中的事件处理
const emitter = createEmitter();
const off = emitter.on('event', () => {
  // ...
});

// 取消监听事件
off();

19.自定义渲染器 API 的改进

  • Vue3: Vue3 的自定义渲染器 API 更加灵活,并引入了 createRenderer 函数,使得可以更方便地在不同平台上进行渲染。
  • Vue2: Vue2 中的自定义渲染器相对较为有限。
// Vue3 中的自定义渲染器 API
import { createRenderer } from 'vue';

const renderer = createRenderer({
  // ...
});

20.新增patchFlag字段

patchFlag 字段是一个位掩码,通过对不同位进行组合来表示不同的变化类型。它可以帮助 Vue 3 在进行 diff 运算时更高效地定位和处理需要更新的部分,避免不必要的比较和操作,从而提升性能。

具体作用如下:

  1. 快速判断节点类型变化:patchFlag 可以告诉 Vue 3 虚拟 DOM 的变化类型,包括节点的类型、文本内容、元素的动态属性等。通过检查 patchFlag 的不同位,可以快速判断节点类型是否发生了变化,避免进行不必要的深度比较。
  2. 提高 diff 粒度:通过细分不同的 patchFlag 值,可以将节点的比较和更新粒度调整得更加准确。例如,如果只有属性发生了变化,可以仅针对属性进行比较,而无需再对子节点进行深度比较。
  3. 优化响应式更新:通过 patchFlag 标记节点的变化,Vue 3 可以针对性地处理不同类型的更新。例如,当一个组件的 props 发生变化时,只需针对 props 进行更新,而无需重新渲染整个组件。

类型列举:

// patchFlags 字段类型列举
export const enum PatchFlags { 
  TEXT = 1,   // 动态文本内容
  CLASS = 1 << 1,   // 动态类名
  STYLE = 1 << 2,   // 动态样式
  PROPS = 1 << 3,   // 动态属性,不包含类名和样式
  FULL_PROPS = 1 << 4,   // 具有动态 key 属性,当 key 改变,需要进行完整的 diff 比较
  HYDRATE_EVENTS = 1 << 5,   // 带有监听事件的节点
  STABLE_FRAGMENT = 1 << 6,   // 不会改变子节点顺序的 fragment
  KEYED_FRAGMENT = 1 << 7,   // 带有 key 属性的 fragment 或部分子节点
  UNKEYED_FRAGMENT = 1 << 8,   // 子节点没有 key 的fragment
  NEED_PATCH = 1 << 9,   // 只会进行非 props 的比较
  DYNAMIC_SLOTS = 1 << 10,   // 动态的插槽
  HOISTED = -1,   // 静态节点,diff阶段忽略其子节点
  BAIL = -2   // 代表 diff 应该结束
}

21.错误处理的改进

  • Vue3: Vue3 引入了 errorCaptured 生命周期钩子,用于捕获子组件的错误,并提供了更好的错误处理机制。
  • Vue2: 在 Vue2 中,错误处理相对较为有限。
// Vue3 中的错误处理
<script>
export default {
  errorCaptured(err, vm, info) {
    // 处理错误
  },
};
</script>

22.事件缓存

Vue3 的cacheHandler可在第一次渲染后缓存我们的事件。相比于 Vue2 无需每次渲染都传递一个新函数。

<div id="app">
  <h1>vue3事件缓存</h1>
  <p>这是一个p标签</p>
  <div>{{name}}</div>
  <span onCLick=() => {}><span>
</div>

渲染函数如下所示。

import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from vue
 
const _withScopeId = n => (_pushScopeId(scope-id),n=n(),_popScopeId(),n)
const _hoisted_1 = { id: app }
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(h1, null, vue3事件缓存, -1 /* HOISTED */))
const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, 这是一个p标签, -1 /* HOISTED */))
const _hoisted_4 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(span, { onCLick: () => {} }, [
  /*#__PURE__*/_createElementVNode(span)
], -1 /* HOISTED */))
 
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock(div, _hoisted_1, [
    _hoisted_2,
    _hoisted_3,
    _createElementVNode(div, null, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _hoisted_4
  ]))
}

观察以上渲染函数,你会发现 click 事件节点为静态节点(HOISTED 为 -1),即不需要每次重新渲染。

23.响应式原理

Proxy与Object.defineProperty:

  • Vue 2: Vue 2使用Object.defineProperty来实现响应式。该方法通过劫持对象的getset操作,在数据发生变化时触发视图更新。但这种方式存在一些限制,例如不能监听数组的变化。

  • Vue 3: Vue 3采用了ES6中引入的Proxy对象。Proxy提供了更强大和灵活的拦截器,可以覆盖目标对象的各种操作,包括getsetdelete等。这使得Vue 3的响应式系统更加强大且性能更优。

    Proxy 对象:Vue 3 使用了 ES6 中的 Proxy 对象来实现响应式。Proxy 对象可以拦截 JavaScript 对象上的各种操作,比如读取属性、设置属性、删除属性等。通过使用 Proxy 对象,Vue 3 可以在属性发生变化时捕获到对应的操作,并触发更新。

    依赖追踪:Vue 3 使用了依赖追踪的机制来追踪属性之间的依赖关系。每个响应式对象都有一个关联的依赖追踪器,当访问响应式对象的某个属性时,会将当前的依赖关系添加到依赖追踪器中。这样,在属性发生变化时,可以找到所有依赖于该属性的地方,并触发相应的更新。

更高效的触发更新机制:

  • Vue 2: 在Vue 2中,每个组件实例都有一个Watcher实例,用于追踪数据的变化。当数据变化时,会触发Watcher,进而导致视图更新。
  • Vue 3: Vue 3引入了基于scheduler的新的更新机制。通过scheduler,Vue 3可以更灵活地控制何时执行更新,以提高性能。这种机制使得Vue 3在处理大规模数据变化时更加高效。

组合式API的影响:

  • Vue 2: Vue 2的开发主要基于选项式 API,这在大型组件和复杂逻辑下可能导致代码难以维护和组织。
  • Vue 3: 引入了API,这是一种基于函数的组合式API,允许开发者更灵活地组织组件逻辑。组合式API与Vue 3的响应式系统密切结合,使得组件逻辑更容易复用和组织。

Tree-shakable:

  • Vue 2: Vue 2的体积相对较大,无法很好地支持Tree-shaking,导致在项目中引入整个Vue库,即使只使用了其中的一部分功能。
  • Vue 3: Vue 3采用了模块化设计,使得库更容易被Tree-shaking优化,可以更细粒度地按需引入和使用。
文章来源:https://blog.csdn.net/Robinwang1128/article/details/135408238
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。