SoFunction
Updated on 2025-03-10

What are the differences between vue2 and vue3 in front-end interview

1. Detailed description of the difference between Vue3 and Vue2

1. Life cycle

For the life cycle, the overall change is not much, but most life cycle hooks have + "on" in the name, which is similar in function. However, one thing to note is that Vue3 needs to be introduced first when using lifecycle hooks in the Composition API (Composition API, expanded below), while Vue2 can directly call lifecycle hooks in the Options API (Options API), as shown below.

// vue3
<script setup>     
import { onMounted } from 'vue';   // Lifecycle hooks need to be introduced before useonMounted(() => {
  // ...
});
// Different logic can be split into multiple onMounted, and still executed in order, and will not be overwritten.onMounted(() => {
  // ...
});
</script>
// vue2
<script>     
export default {           mounted() {   // Directly call the life cycle hook    // ...         
  },           }
</script>

Commonly used life cycle pairs are shown in the table below.

vue2 vue3
beforeCreate
created
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted

Tips: setup runs around beforeCreate and created life cycle hooks, so it does not need to be explicitly defined.

2. Multiple nodes

Friends who are familiar with Vue2 should be clear that if multiple root nodes are used in the template, an error will be reported, as shown below.

// There are multiple root nodes in template in vue2 and will report an error<template>
  <header></header>
  <main></main>
  <footer></footer>
</template>
// There can only be one root node, and it needs to be wrapped with a <div>&lt;template&gt;
  &lt;div&gt;
    &lt;header&gt;&lt;/header&gt;
    &lt;main&gt;&lt;/main&gt;
    &lt;footer&gt;&lt;/footer&gt;
  &lt;/div&gt;
&lt;/template&gt;

However, Vue3 supports multiple root nodes, that is, fragments. That is, the following multiple nodes are written to be allowed.

<template>
  <header></header>
  <main></main>
  <footer></footer>
</template>

3. Composition API

Vue2 is an option API (Options API). A logic will be scattered in different locations of the file (data, props, computed, watch, life cycle hook, etc.), causing the code to be poorly readable. When a certain logic needs to be modified, it is necessary to jump up and down to jump back to the file location.

Vue3 Composition API (Composition API) solves this problem well, and can write the contents of the same logic together, enhancing the readability and cohesion of the code, and also provides a relatively perfect logical reusability solution.

4. Asynchronous Components (Suspense)

Vue3 provides Suspense component, allowing programs to render the content that is guaranteed before waiting for the asynchronous component to be loaded, making the user's experience smoother. Using it, it needs to be declared in the template and includes two named slots: default and fallback. Suspense ensures that the default slot is displayed when asynchronous content is loaded and uses the fallback slot as the loading state.

<tempalte>
  <suspense>
    <template #default>
      <List />
    </template>
    <template #fallback>
      <div>
        Loading...      </div>
    </template>
  </suspense>
</template>

Before the List component (which may be an asynchronous component, or the component's internal processing logic or too much search operation caused by too slow loading, etc.) is loaded, display Loading... (i.e. fallback slot content), and display itself (i.e. default slot content) when loading is completed.

5. Teleport

Vue3 provides Teleport components to move some DOMs outside of Vue apps. For example, the common Dialog pop-up window in the project. refer to:Detailed answers to front-end vue interview questions

&lt;button @click="dialogVisible = true"&gt;Show pop-up window&lt;/button&gt;
&lt;teleport to="body"&gt;
  &lt;div class="dialog" v-if="dialogVisible"&gt;
    I'm a pop-up window,I moved directly tobodyUnder the tag  &lt;/div&gt;
&lt;/teleport&gt;

6. Responsive Principle

The basis of Vue2's responsive principle is; the basis of Vue3's responsive principle is Proxy.


  • Basic usage: Define new properties directly on an object or modify existing properties and return the object.
let obj = {};
let name = 'leo';
(obj, 'name', {
  enumerable: true,   // Enumerable (whether it can be accessed through for...in or ())  configurable: true,   // Configurable (can be deleted with delete, can the properties be set again)  // value: '', // Value of any type, undefined by default  // writable: true, // rewritable  get() {
    return name;
  },
  set(value) {
    name = value;
  }
});

 Tips: writableandvalueandgetterandsetterNo coexistence.

Move the Vue2 core source code and slightly deleted.

function defineReactive(obj, key, val) {
  // A key one dep  const dep = new Dep()
  // Get the attribute descriptor of the key and find that it is an unconfigurable object and directly return it  const property = (obj, key)
  if (property &amp;&amp;  === false) { return }
  // Get getter and setter and get val value  const getter = property &amp;&amp; 
  const setter = property &amp;&amp; 
  if((!getter || setter) &amp;&amp;  === 2) { val = obj[key] }
  // Recursive processing to ensure that all keys in the object are observed  let childOb = observe(val)
  (obj, key, {
    enumerable: true,
    configurable: true,
    // get hijacking obj[key] for dependency collection    get: function reactiveGetter() {
      const value = getter ? (obj) : val
      if() {
        // Dependency collection        ()
        if(childOb) {
          // For nested objects, dependency collection          ()
          // Trigger array responsiveness          if((value)) {
            dependArray(value)
          }
        }
      }
    }
    return value
  })
  // set distribute update obj[key]  set: function reactiveSetter(newVal) {
    ...
    if(setter) {
      (obj, newVal)
    } else {
      val = newVal
    }
    // Set the responsive form for new values    childOb = observe(val)
    // Dependency notification update    ()
  }
}

Then why did Vue3 abandon it? That must be because of some limitations.

Main reason: Unable to listen for newly added or deleted elements of objects or arrays.

Vue2 corresponding solution: hacked for common array prototype methods push, pop, shift, unshift, splice, sort, and reverse; new attributes added to the listening object/array are provided. The new/delete response of the object can also be new new objects. If the new one is added, the new attributes and the old object will be merged; if the deleted attributes are deleted, the object after the deleted attributes will be deeply copied to the new object.

  • Proxy
    Proxy is a new feature of ES6, which intercepts the behavior of the target object through the second parameter handler. Compared to providing full-range responsiveness in languages, it eliminates limitations.

limitation:

(1) Add or delete objects/arrays

(2) Monitoring .length modification

(3) Support for Map, Set, WeakMap, WeakSet

Basic usage: Create an agent for the object to implement the interception and customization of basic operations.

let handler = {
  get(obj, prop) {
    return prop in obj ? obj[prop] : '';
  },
  set() {
    // ...
  },
  ...
};

Move vue3's source code file.

function createReactiveObject(target, isReadOnly, baseHandlers, collectionHandlers, proxyMap) {
  ...
  // collectionHandlers: handle Map, Set, WeakMap, WeakSet  // baseHandlers: handle arrays and objects  const proxy = new Proxy(
    target,
    targetType ===  ? collectionHandlers : baseHandlers
  )
  (target, proxy)
  return proxy
}

7. Virtual DOM

Vue3 Compared with Vue2, patchFlag field is added on the virtual DOM. We use the helpVue3 Template ExplorerCome and see。

&lt;div &gt;
  &lt;h1&gt;vue3virtualDOMexplain&lt;/h1&gt;
  &lt;p&gt;The weather is really good today&lt;/p&gt;
  &lt;div&gt;{{name}}&lt;/div&gt;
&lt;/div&gt;

The rendering function is shown below.

import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from vue
const _withScopeId = n =&gt; (_pushScopeId(scope-id),n=n(),_popScopeId(),n)
const _hoisted_1 = { id: app }
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(h1, null, vue3 virtual DOM explanation, -1 /* HOISTED */))const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, the weather is really good today, -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 */)
  ]))
}

Note that the fourth parameter of the third _createElementVNode is the patchFlag field type.

Field type situation: 1 represents the node as a dynamic text node. In the diff process, you only need to compare the text content without paying attention to class, style, etc. In addition, all static nodes (HOISTED is -1) were found to be saved as a variable for static promotion, and can be directly referenced when re-rendering without re-creating.

// patchFlags field type listexport const enum PatchFlags { 
  TEXT = 1,   // Dynamic text content  CLASS = 1 &lt;&lt; 1,   // Dynamic class name  STYLE = 1 &lt;&lt; 2,   // Dynamic style  PROPS = 1 &lt;&lt; 3,   // Dynamic attributes, not including class names and styles  FULL_PROPS = 1 &lt;&lt; 4,   // Has a dynamic key attribute. When the key changes, a complete diff comparison is required.  HYDRATE_EVENTS = 1 &lt;&lt; 5,   // Node with listening events  STABLE_FRAGMENT = 1 &lt;&lt; 6,   // fragment that will not change the order of child nodes  KEYED_FRAGMENT = 1 &lt;&lt; 7,   // fragment or part of child nodes with key attribute  UNKEYED_FRAGMENT = 1 &lt;&lt; 8,   // The child node does not have a fragment with key  NEED_PATCH = 1 &lt;&lt; 9,   // Only non-props comparisons will be performed  DYNAMIC_SLOTS = 1 &lt;&lt; 10,   // Dynamic slots  HOISTED = -1,   // Static node, the diff stage ignores its child nodes  BAIL = -2   // means diff should end}

8. Event Cache

Vue3'scacheHandlerOur events can be cached after the first rendering. Compared to Vue2, there is no need to pass a new function every time it renders. Add a click event.

&lt;div &gt;
  &lt;h1&gt;vue3Event Cache Explanation&lt;/h1&gt;
  &lt;p&gt;The weather is really good today&lt;/p&gt;
  &lt;div&gt;{{name}}&lt;/div&gt;
  &lt;span onCLick=() =&gt; {}&gt;&lt;span&gt;
&lt;/div&gt;

The rendering function is shown below.

import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from vue
const _withScopeId = n =&gt; (_pushScopeId(scope-id),n=n(),_popScopeId(),n)
const _hoisted_1 = { id: app }
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(h1, null, vue3 event cache explanation, -1 /* HOISTED */))const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, the weather is really good today, -1 /* HOISTED */))const _hoisted_4 = /*#__PURE__*/ _withScopeId(() =&gt; /*#__PURE__*/_createElementVNode(span, { onCLick: () =&gt; {} }, [
  /*#__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
  ]))
}

Observe the above rendering function and you will find that the click event node is a static node (HOISTED is -1), which means that you do not need to re-render each time.

9. Diff algorithm optimization

Move the Vue3 patchChildren source code. Combining the above and source code, patchFlag helps diff distinguish between static nodes and different types of dynamic nodes. Reduce the comparison of nodes themselves and their attributes to a certain extent.

function patchChildren(n1, n2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {
  // Get new and old children's nodes  const c1 = n1 &amp;&amp; 
  const c2 = 
  const prevShapeFlag = n1 ?  : 0
  const { patchFlag, shapeFlag } = n2
  // Process patchFlag greater than 0  if(patchFlag &gt; 0) {
    if(patchFlag &amp;&amp; PatchFlags.KEYED_FRAGMENT) {
      // Existence key      patchKeyedChildren()
      return
    } els if(patchFlag &amp;&amp; PatchFlags.UNKEYED_FRAGMENT) {
      // No key      patchUnkeyedChildren()
      return
    }
  }
  // Match is a text node (static): remove old nodes and set text nodes  if(shapeFlag &amp;&amp; ShapeFlags.TEXT_CHILDREN) {
    if (prevShapeFlag &amp; ShapeFlags.ARRAY_CHILDREN) {
      unmountChildren(c1 as VNode[], parentComponent, parentSuspense)
    }
    if (c2 !== c1) {
      hostSetElementText(container, c2 as string)
    }
  } else {
    // Match the old and new Vnodes as arrays, then compare them all; otherwise, remove all current nodes    if (prevShapeFlag &amp; ShapeFlags.ARRAY_CHILDREN) {
      if (shapeFlag &amp; ShapeFlags.ARRAY_CHILDREN) {
        patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense,...)
      } else {
        unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true)
      }
    } else {
      if(prevShapeFlag &amp; ShapeFlags.TEXT_CHILDREN) {
        hostSetElementText(container, '')
      } 
      if (shapeFlag &amp; ShapeFlags.ARRAY_CHILDREN) {
        mountChildren(c2 as VNodeArrayChildren, container,anchor,parentComponent,...)
      }
    }
  }
}

patchUnkeyedChildren The source code is as follows.

function patchUnkeyedChildren(c1, c2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {
  c1 = c1 || EMPTY_ARR
  c2 = c2 || EMPTY_ARR
  const oldLength = 
  const newLength = 
  const commonLength = (oldLength, newLength)
  let i
  for(i = 0; i &lt; commonLength; i++) {
    // If the new Vnode is already mounted, then clone it directly, otherwise create a new node    const nextChild = (c2[i] = optimized ? cloneIfMounted(c2[i] as Vnode)) : normalizeVnode(c2[i])
    patch()
  }
  if(oldLength &gt; newLength) {
    // Remove excess nodes    unmountedChildren()
  } else {
    // Create a new node    mountChildren()
  }
}

10. Packaging optimization

Tree-shaking: Concepts in module packaging webpack, rollup, etc. Removes unreferenced code in the JavaScript context. It mainly relies on import and export statements to detect whether the code module is exported, imported, and used by JavaScript files.

Taking nextTick as an example, in Vue2, the global API is exposed to the Vue instance and cannot be eliminated through tree-shaking even if it is not used.

import Vue from 'vue';
(() =&gt; {
  // Some things related to DOM});

Vue3 has been refactored for global and internal APIs, taking into account tree-shaking support. Therefore, the global API is now accessible only as a named export built by ES modules.

import { nextTick } from 'vue';   // Explicit importnextTick(() =&gt; {
  // Some things related to DOM});

With this change, as long as the module binder supports tree-shaking, unused apis in the Vue application will be eliminated from the final bundle, achieving the best file size.

The global API affected by this change is shown below.

  • (Replace with
  • (Full build only)
  • (Compatible for builds only)
  • (Compatible for builds only)

Internal APIs also have tags or instructions such as transition, v-model, etc. that are named and exported. Only when the program is actually used will it be bundled and packaged. Vue3 packages all running functions only about 22.5kb, which is much lighter than Vue2.

11. TypeScript Support

Vue3 is rewritten by TypeScript and has better TypeScript support compared to Vue2.

  • In the Vue2 Options API, option is a simple object, while TypeScript is a type system with object-oriented syntax and does not have a special match.
  • Vue2 requires vue-class-component to strengthen vue native components, and also requires vue-property-decorator to add more decorators that combine Vue characteristics, which is more cumbersome.

2. Options API and Composition API

Vue components can be written in two different API styles: the Options API and the Composition API.

1. Options API

Using the Options API, we use option objects to define the logic of components, such as data, methods, and mounted. The properties defined by the options are exposed in this internal function, pointing to the component instance, as shown below.

<template>
  <button @click="increment">count is: {{ count }}</button>
</template>
<script>
export default {  data() {    return {      count: 0
    }  },  methods: {    increment() {      ++;    }  },  mounted() {    (`The initial count is ${}.`);  }}
</script>

2. Composition API

Using the Composition API, we use the imported API functions to define the logic of the component. In SFC, the Composition API is usually used

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>
<script setup>
import { ref, onMounted } from 'vue'; 
const count = ref(0); 
function increment() {  ++;} 
onMounted(() => {  (`The initial count is ${}.`);})
</script>

The above is the detailed content of the difference between vue2 and vue3 in front-end interview. For more information on the difference between vue2 and vue3 in front-end interview, please pay attention to my other related articles!