1. The Ref array in v-forNot compatible with

In Vue 2, the ref attribute used in v-for populates the corresponding $refs property with the ref array. When there are nested V-For-s, this behavior becomes ambiguous and inefficient.

In Vue 3, such usage will no longer automatically create arrays in $ref. To get multiple refs from a single binding, bind the ref to a more flexible function (this is a new feature) :

2. X syntax

<template>
  <div>
    <div v-for="item in 10" :key="item" ref="liRef"></div>
  </div></template> <script> export default { name: 'Vue2Demo', data() {return {}}, mounted() {console.log('refs', this.refs.liref)) Refs (10) [div, div, div, div, div, div, div, div, div, div]Copy the code

3. X syntax

  • Combined with the option API:
<template>
  <div>
    <div v-for="item in 10" :key="item" :ref="setItemRef">
      {{item}}
    </div>
  </div>
</template>
<script>
 export default {
  data() {
    return {
      itemRefs: []}},methods: {
    setItemRef(el) {
      if (el) {
        this.itemRefs.push(el)
      }
    }
  },
  mounted() {
    console.log('Combine option API:'.this.itemRefs)
  }
}
</script>
Copy the code
  • Combined composite APIS:
<template>
  <div v-for="item in 10" :ref="setItemRef">
      {{item}}
  </div>
</template>

<script>
import { defineComponent,onMounted} from 'vue'
export default defineComponent({
  setup() { 
    let itemRefs = [];
    const setItemRef = el= > {
      if (el) {
        itemRefs.push(el)
      }
    } 
    onMounted(() = > {
        console.log('Combine composite APIS :',itemRefs);
    })
    return {
        setItemRef
    }
  }
})
</script>
Copy the code

Note:

  • ItemRefs need not be an array: it can also be an object whose ref is set by the key of the iteration.
  • If desired, itemRef can also be responsive and can be listened on.

2. Asynchronous componentsnew

Overview of the changes:

  • New defineAsyncComponent helper method for explicitly defining asynchronous components
  • Component option to rename to Loader
  • The Loader function itself no longer accepts the resolve and reject arguments and must return a Promise

2. X syntax

Asynchronous components are created by defining the component as a function that returns a Promise

 
const AsyncComponent = () = > import('@/components/AsyncComponent')

Copy the code

Or, for higher-order component syntax with options:

const AsyncComponent = () = > ({
  component: () = > import('@/components/AsyncComponent'),
  delay: 200.timeout: 300.error: ErrorComponent,
  loading: LoadingComponent
})
Copy the code

3. X syntax

In Vue 3, since functional components are defined as pure functions, the definition of an asynchronous component needs to be explicitly defined by wrapping it in the new defineAsyncComponent helper method:

import { defineAsyncComponent } from 'vue'
import ErrorComponent from '@/components/ErrorComponent';
import LoadingComponent from '@/components/LoadingComponent';

// An asynchronous component with no options
const  AsyncComponent = defineAsyncComponent(() = > import('@/components/AsyncComponent'))

// Asynchronous components with options
const AsyncComponentWithOptions = defineAsyncComponent({
  // The Component option in 2.x is changed to Loader to accurately convey information that cannot be directly provided by the component definition. The Loader function no longer accepts the resolve and reject arguments and must always return a Promise.
  loader: () = > import('@/components/AsyncComponent'),
  delay: 200.timeout: 3000.errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})
Copy the code

3. Attribute mandatory behaviorNot compatible with

Pre-knowledge:

  • HTML attributes
  • The difference between Boolean and enumeration properties
  • The difference between content properties and IDL properties

Boolean attribute

  • Boolean attributes have one characteristic: when declared, the value is true; If not declared, the value is false.
  • HTML5 defines the allowed values for Boolean attributes: If the attribute exists, its value must be either an empty string (that is, an unassigned value for the attribute) or a case-insensitive ASCII string that is exactly the same as the attribute name, with no Spaces before or after it. — from MDN: Web Development Techniques >HTML (HyperText Markup Language) >HTML Attribute Reference > Boolean Attribute
<div itemscope> This is valid HTML but invalid XML. </div>
<div itemscope=itemscope> This is also valid HTML but invalid XML. </div>
<div itemscope=""> This is valid HTML and also valid XML. </div>
<div itemscope="itemscope"> This is also valid HTML and XML, but perhaps a bit verbose. </div>
Copy the code

All four of these are equivalent. Therefore, Boolean properties cannot be “true” and “false”. If you want to represent false, the Boolean attribute needs to be completely ignored.

Enumerated attribute

  • An enumeration attribute, as its name suggests, is an enumeration set of keywords. For example, the autoComplete property of the input element can be username, email, country, tel, URL, and so on.
  • Note that some enumeration properties accept only two enumeration values: true and false. Also, an empty string or no attribute assignment is equal to true. Both of these represent true
<div contenteditable>An editable item</div>
<div contenteditable="">An editable item</div>
<div contenteditable="true">An editable item</div>
Copy the code

So all of these are false

<div contenteditable="false">An editable item</div>
<div contenteditable="abcdefg">An editable item</div>
<div>An editable item</div>
Copy the code

Other attributes

Except for the above two categories of attributes, the rest of the attributes can be classified as general attributes.

Content attributes and IDL (Interface Description Language) attributes: In HTML, attributes are also called content attributes and IDL attributes. Note that these two attributes are not a division of tag attributes. They are just different descriptions and ways of writing in different places. The value received by the content property is a string. When writing HTML, content attributes are written directly in tags. In addition, content attributes can also be set with the JS setAttribute(). <div contenteditable>An editable item</div> input.setAttribute('type'.'text');
input.getAttribute('type'); The IDL property is a JavaScript property, which is a real property provided by the DOM to JS. Set with the. Operator, and only values of the correct type are accepted. If the received value is of an incorrect type, it is automatically converted to the correct type. input.type ='password';
Copy the code

MDN: Web Development Techniques >HTML (Hypertext Markup Language) >HTML attribute Reference

Overview of the changes:

  • Remove the internal concepts of enumeration attributes and treat these attributes as normal non-boolean attributes
  • Significant change: Attribute False is no longer deleted if the value is a Boolean. Instead, it is set to attr= “false”. To remove an attribute, use either null or undefined.

2. X syntax

  • For some attribute/element pairs, Vue takes the form of IDL attributes: for example, value of<input>.<select>.<progress>, and so on.
  • For Boolean properties and xLinks, Vue determines whether to add or remove attributes by determining whether they are falsy(undefined, null, false).
  • For enumeration attributes, Vue forces the conversion to a string.
  • For other (normal non-boolean) properties, delete if the value passed is falsy, add it otherwise

The following table describes how Vue forces an “enumeration attribute” using a normal non-boolean attribute:

V – bind expression Normal non-Boolean property: foo Enumeration property: draggable
:attr=”null” / draggable=”false”
:attr=”undefined” / /
:attr=”true” foo=”true” draggable=”true”
:attr=”false” / draggable=”false”
:attr=”0″ foo=”0″ draggable=”true”
attr=”” foo=”” draggable=”true”
attr=”foo” foo=”foo” draggable=”true”
attr foo=”” draggable=”true”

Remove / :

As can be seen from the comparison table above, the performance of the two is inconsistent. This will cause inconvenience when using.

3. X syntax

In Vue 3.x, the concept of enumeration properties was removed and they are treated as normal non-boolean properties. Benefits:

  • Eliminates inconsistencies in the representation of common non-boolean and enumerated attributes (in other words, in Vue 3.x, only non-boolean and Boolean attributes exist)
  • This means that you can use values other than true and false for enumeration properties, even unused keywords.

In addition, for non-boolean attributes, if the value passed is false, Vue will no longer remove the attribute, but will force it to the string ‘false’ instead.

In the table above, the performance in Vue 3.x becomes:

V – bind expression Normal non-Boolean property: foo Enumeration property: draggable
:attr=”null” / /
:attr=”undefined” / /
:attr=”true” foo=”true” draggable=”true”
:attr=”false” foo=”false” draggable=”false”
:attr=”0″ foo=”0″ draggable=”0″
attr=”” foo=”” draggable=””
attr=”foo” foo=”foo” draggable=”foo”
attr foo=”” draggable=””

As you can see, ordinary non-Boolean properties and enumeration properties result in the same result.

For non-Boolean properties, false is forcibly converted to ‘false’ and properties are no longer deleted. So, in Vue 3.x, undefined and NULL should be used to explicitly remove attributes.

Note that the Boolean properties are represented and changed to be consistent with Vue 2.x.

Attribute v-bind value 2.x v-bind value 3.x HTML output
Enumeration properties in Vue 2.x, such as contenteditable, draggable and spellcheck. undefined, false undefined, null removed
true, ‘true’, ”, 1, ‘foo’ true, ‘true’ “true”
null, ‘false’ false, ‘false’ “false”
Common non-Boolean attributes in Vue 2.x, such as: ARIa-Checked, tabindex, Alt, etc. undefined, null, false undefined, null removed
‘false’ false, ‘false’ “false”
Boolean attributes: Required, Disabled, and readonly False, NULL, and undefined False, NULL, and undefined removed

Actual code tests

<div style="width: 500px"> non-enumerating non-boolean attributes:true:<input type="text" :foo="true" />Non-enumeration non-Boolean attributes:false:<input type="text" :foo="false" />Non-enumeration non-Boolean attributes:undefined:<input type="text" :foo="undefined" />Non-enumeration non-Boolean attributes:null:<input type="text" :foo="null" />Non-enumeration non-Boolean attributes:0:<input type="text" :foo="0" />

  <hr />Enumeration properties:true:<input type="text" :spellcheck="true" />Enumeration properties:false:<input type="text" :spellcheck="false" />Enumeration properties:undefined:<input type="text" :spellcheck="undefined" />Enumeration properties:null:<input type="text" :spellcheck="null" />Enumeration properties:0:<input type="text" :spellcheck="0" />

  <hr />Boolean attribute required:true:<input type="text" :required="true" />Boolean attribute required:false:<input type="text" :required="false" />Boolean attribute required:undefined:<input type="text" :required="undefined" />Boolean attribute required:null:<input type="text" :required="null" />Boolean attribute required:0:<input type="text" :required="0" />
</div>
Copy the code

Results:

4. $attrs includes class&styleNot compatible with

Overview of the changes:

  • $attrs now contains all the attributes passed to the component, including class and style.

2. X syntax

There is some special handling of class and style attributes in Vue 2’s virtual DOM implementation. Therefore, they are not included in $attrs, where all other attributes are.

Using inheritAttrs: false has a side effect:

  • Attributes in $attrs are no longer automatically added to the root element; instead, it is up to the developer to decide where to add them.
  • But class and style are not part of $attrs and are still applied to the component’s root element:
<template>
  <label>
    <input type="text" v-bind="$attrs" />
  </label>
</template>
<script>
export default {
  inheritAttrs: false
}
</script>
Copy the code

When used like this:

<my-component id="my-id" class="my-class"></my-component>
Copy the code

… The following HTML will be generated:

<label class="my-class">
  <input type="text" id="my-id" />
</label>
Copy the code

3. X syntax

$attrs contains all the attributes, which makes it easier to apply them all to another element. The above example generates the following HTML:

<label>
  <input type="text" id="my-id" class="my-class" />
</label>
Copy the code

5.$children remove

Overview of the changes:

  • The $children instance property has been removed from Vue 3.0 and is no longer supported.

2. X syntax

In 2.x, developers can use this.$children to directly access the children of the current instance:

<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png">
    <my-button>Change logo</my-button>
  </div>
</template>

<script>
import MyButton from './MyButton'

export default {
  components: {
    MyButton
  },
  mounted() {
    console.log(this.$children) // [VueComponent]}}</script>
Copy the code

3. X syntax

In 3.x, $children property has been removed and is no longer supported. If you need to access child component instances, we recommend using $refs.

6. Customize instructionsNot compatible with

Overview of the changes:

  • The directive’s hook function has been renamed to better align with the component’s lifecycle.

2. X syntax

In Vue 2, custom directives are created by using the hooks listed below, which are optional

  • Bind-occurs after the directive binds to an element. It only happens once.
  • Inserted – occurs after an element is inserted into the parent DOM.
  • Update – This hook is called when an element has been updated, but the child elements have not been updated.
  • ComponentUpdated – This hook is called once the component and its children are updated.
  • Unbind – this hook is called once the directive is removed. It’s only called once.

Here’s an example:

<p v-highlight="'yellow'"> Highlight this text in bright yellow </p>Copy the code
Vue.directive('highlight', {
  bind(el, binding, vnode) {
    el.style.background = binding.value
  }
})
Copy the code

Here, in the initial setting of the element, the directive binds the style by passing a value that can be updated to a different value by the application.

3. X syntax

In Vue 3, however, we created a more cohesive API for custom directives. As you can see, they are quite different from our component lifecycle approach, and even though we are working with similar event hooks, we now unify them:

  • Created – New! Called before the element’s attribute or event listener is applied.
  • The bind – beforeMount
  • He inserted – mounted
  • BeforeUpdate: New! This is called before the element itself is updated, much like a component lifecycle hook.
  • Update → Remove! There are too many similarities to update, so this is redundant, please use updated instead.
  • ComponentUpdated – updated
  • BeforeUnmount: New! Like the component lifecycle hook, it is called before the element is unloaded.
  • unbind -> unmounted

The final API is as follows:

const MyDirective = { beforeMount(el, binding, vnode, prevVnode) {}, mounted() {}, beforeUpdate() {}, / / the new updated () {}, beforeUnmount () {}, / / new unmounted () {}}Copy the code

The generated API can be used as follows, as in the previous example:

<p v-highlight="'yellow'"> Highlight this text in bright yellow </p>Copy the code
const app = Vue.createApp({})

app.directive('highlight', {
  beforeMount(el, binding, vnode) {
    el.style.background = binding.value
  }
})
Copy the code

7. Custom element interactionsNot compatible with

Overview of the changes:

  • Non-compatibility: Custom element whitelists are now executed during template compilation and should be configured through compiler options rather than runtime configuration.
  • Incompatible: Specific is Prop usage is limited to reserved < Component > tags.
  • New: There are new V-IS directives to support the 2.x use case where V-IS is used on native elements to handle native HTML parsing restrictions.

Custom elements

If we want to add a custom element defined outside of Vue (for example, using a Web component API), we need to “instruct” Vue to treat it as a custom element. Let’s take the following template as an example.

<plastic-button></plastic-button>
Copy the code

2. X syntax

In Vue 2 x, will be marked as custom yuan white list is through the Vue. Config. IgnoredElements:

// This causes Vue to ignore custom elements defined outside Vue
// (for example, using the Web Components API)

Vue.config.ignoredElements = ['plastic-button']
Copy the code

3. X syntax

In Vue 3.0, this check, performed during template compilation, instructs the compiler to treat < plastics-button > as a custom element:

  • Pass the isCustomElement to the Vue template compiler using the generate step, or to the Vue-Loader compilerOptions:
// WebPack configuration
rules: [{test: /\.vue$/,
    use: 'vue-loader'.options: {
      compilerOptions: {
        isCustomElement: tag= > tag === 'plastic-button'}}}// ...
]
Copy the code
  • If you are using a dynamic template compilation, please use app. Config. IsCustomElement transfer:
const app = Vue.createApp({})
app.config.isCustomElement = tag= > tag === 'plastic-button'
Copy the code

It is important to note that runtime configuration only affects runtime template compilation — it does not affect precompiled templates.

Custom built-in elements

The Custom element specification provides a way to use custom elements as custom built-in templates by adding the is attribute to the built-in element:

<button is="plastic-button"> Click me! </button>Copy the code

Vue’s use of is special Prop is to emulate the role of Native Attribute before it became commonly available in browsers. However, in 2.x, it is interpreted as rendering a Vue component called a plastic-button, which would prevent native use of the custom built-in elements mentioned above.

In 3.0, we limited Vue’s special handling of is properties only to the < Component > tag.

  • When used on the reserved < Component > tag, its behavior will be exactly the same as in 2.x;

  • When used on a normal component, it will behave like a normal prop:

    <foo is="bar" />
    Copy the code
    • 2. X behavior: Render the bar component.
    • 3. X behavior: Render the Foo component with is Prop.
  • When used on normal elements, it is passed to the createElement call as an IS option and rendered as a native Attribute, which enables the use of custom built-in elements.

    <button is="plastic-button"> Click me! </button>Copy the code
    • 2. X behavior: render the plastic-button component.

    • 3. X behavior: Rendering native buttons via callbacks.

      document.createElement('button', { is: 'plastic-button' })
      Copy the code

V-is is used for template parsing solutions within the DOM

Tip: This section only affects the case where the Vue template is written directly into the HTML of the page. When used in a DOM template, the template is bound by native HTML parsing rules. Some HTML elements such as <ul>, <ol>, <table>, and <select> have limits on what elements can appear within them, and some elements like <li>, <tr>, and <option> can only appear within certain other elements.

2 x syntax

In Vue 2, we propose to address these limitations by using is Prop on native tags:

<table>
  <tr is="blog-post-row"></tr>
</table>
Copy the code

3. X syntax

As the behavior of IS changes, we have introduced a new instruction, V-IS, to address these situations:

<table>
  <tr v-is="'blog-post-row'"></tr>
</table>
Copy the code

The V-is function acts like a dynamic 2.x :is binding — therefore, to render the component with its registered name, its value should be JavaScript string text:

<! -- Incorrect, will not render anything --><tr v-is="blog-post-row"></tr><! - right - ><tr v-is="'blog-post-row'"></tr>
Copy the code

8. The Data optionsNot compatible with

Overview of the changes:

  • Incompatible: The Data component option declaration no longer accepts pure JavaScript objects and requires a function declaration instead.

When multiple data return values from mixin or extend are merged, shallow merge is now done instead of deep merge (only root attributes are merged).

2 x syntax

In 2.x, developers can define the data option to be object or function.

<! --ObjectStatement - ><script>
  const app = new Vue({
    data: {
      apiKey: 'a1b2c3'}})</script><! --FunctionStatement - ><script>
  const app = new Vue({
    data() {
      return {
        apiKey: 'a1b2c3'}}})</script>
Copy the code

While this provides some convenience for root instances with shared state, it leads to confusion because this is only possible on the root instance.

3 x syntax

In 3.x, the data option has been normalized to accept only functions that return object.

Using the above example, there is only one possible implementation of the code:

<script>
  import { createApp } from 'vue'

  createApp({
    data() {
      return {
        apiKey: 'a1b2c3'
      }
    }
  }).mount('#app')
</script>
Copy the code

Mixin merge behavior changed

In addition, when data() from a component and its mixin or extends base class are merged, the merge is now performed at the shallow level:

const Mixin = {
  data() {
    return {
      user: {
        name: 'Jack'.id: 1}}}}const CompA = {
  mixins: [Mixin],
  data() {
    return {
      user: {
        id: 2}}}}Copy the code

In Vue 2.x, the generated $data is:

{
  user: {
    id: 2.name: 'Jack'}}Copy the code

In 3.0, the result would be:

{
  user: {
    id: 2}}Copy the code

9.emits Option new

Changes overview: Vue 3 now offers a emits option, similar to the existing props option. This option can be used to define events that a component can send to its parent component.

2 x syntax

In Vue 2, you can define props for a component to receive, but you can’t declare what events it can emit:

<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text']}</script>
Copy the code

3 x syntax

Similar to props, events emitted by components can now be defined with the emits option:

<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text'].emits: ['accepted']}</script>
Copy the code

This option also accepts an object that allows the developer to define a validator for the parameters passed with the fired event, similar to the validator in the props definition. see

10. Event apisNot compatible with

Overview of the changes:

  • The $on, $OFF, and $once instance methods have been removed, and the application instance no longer implements the event-triggered interface.

2. X syntax

In 2.x, Vue instances can be used to trigger handlers (on, on, on, off, and $once) that are added by the event-firing API in a directive manner. This creates the Event Hub, which is used to create global event listeners available throughout the application:

// eventHub.js

const eventHub = new Vue()

export default eventHub

// ChildComponent.vue
import eventHub from './eventHub'

export default {
  mounted() {
    // Add the eventHub listener
    eventHub.$on('custom-event'.() = > {
      console.log('Custom event triggered! ')})},beforeDestroy() {
    // Remove the eventHub listener
    eventHub.$off('custom-event')}}// ParentComponent.vue
import eventHub from './eventHub'

export default {
  methods: {
    callGlobalCustomEvent() {
      eventHub.$emit('custom-event') // When ChildComponent is mounted, a message is displayed in the console}}}Copy the code

3. X syntax

We completely remove the on, ON, on, off, and once methods from the instance. Once method. Once method. Emit is still included in the existing API because it is used to trigger event handlers that are added declaratively by the parent component.

11. The filterremove

Overview of the changes:

  • As of Vue 3.0, the filter has been removed and is no longer supported.

2. X syntax

In 2.x, developers can use filters to handle common text formats.

<template>
  <h1>Bank Account Balance</h1>
  <p>{{ accountBalance | currencyUSD }}</p>
</template>

<script>
  export default {
    props: {
      accountBalance: {
        type: Number.required: true}},filters: {
      currencyUSD(value) {
        return '$' + value
      }
    }
  }
</script>
Copy the code

While this may seem convenient, it requires a custom syntax that breaks the assumption that expressions inside braces are “just JavaScript,” which is not only costly to learn, but also costly to implement.

3. X syntax

In 3.x, the filter has been removed and is no longer supported. Instead, we suggest replacing them with method calls or computed properties.

Using the example above, here is an example of how to do it.

<template>
  <h1>Bank Account Balance</h1>
  <p>{{ accountInUSD }}</p>
</template>

<script>
  export default {
    props: {
      accountBalance: {
        type: Number.required: true}},computed: {
      accountInUSD() {
        return '$' + this.accountBalance
      }
    }
  }
</script>
Copy the code

Instead of using filters, it is recommended to evaluate attributes or methods instead of filters

Global filter

If filters are registered globally in your application, it may not be convenient to replace them with computed properties or method calls in each component.

Instead, you can use it in all components via global properties:

// main.js
const app = createApp(App)

app.config.globalProperties.$filters = {
  currencyUSD(value) {
    return '$' + value
  }
}
Copy the code

You can then modify all templates using the $filters object, as follows:

<template>
  <h1>Bank Account Balance</h1>
  <p>{{ $filters.currencyUSD(accountBalance) }}</p>
</template>
Copy the code

Note that this approach can only be used in methods and not in computed properties, which only make sense when defined in the context of a single component.

Segments of 12.new

Overview of the changes:

  • Vue 3 now officially supports multi-root components known as fragments!

2. X syntax

In 2.x, because there is no support for multiple root node components, a warning will be issued when developers accidentally create one. To fix this, many components are wrapped in a <div>.

<! -- Layout.vue --><template>
  <div>
    <header>.</header>
    <main>.</main>
    <footer>.</footer>
  </div>
</template>
Copy the code

3. X syntax

In 3.x, a component can contain multiple root nodes! However, this requires the developer to explicitly define where attributes should be distributed.

<! -- Layout.vue --><template>
  <header>.</header>
  <main v-bind="$attrs">.</main>
  <footer>.</footer>
</template>
Copy the code

13. Functional componentsNot compatible with

Overview of the changes:

  • In 3.x, the performance improvement for the functional component 2.x is negligible, so we recommend using only stateful components
  • Functional components can only be created using the normal functions that receive props and context (that is, slots, attrs, emit).
  • Incompatible change: Functional attribute in the single-file component (SFC)
  • Incompatible change: the {functional: true} option on creating components through functions has been removed

Introduction:

There are two main application scenarios for functional components in Vue 2:

  • As a performance optimization, because they initialize much faster than stateful components
  • Returns multiple root nodes

However, in Vue 3, the performance of stateful components has improved to a negligible level. In addition, stateful components now include the ability to return multiple root nodes.

Therefore, the only applications left for functional components are simple components, such as those that create dynamic titles. Otherwise, it is recommended that you use stateful components as usual.

2. X syntax

Using components, it is responsible for providing the appropriate headings (i.e., H1, H2, H3, etc.), which in 2.x May be written as a single file component:

// Vue 2 functional component example
export default {
  functional: true.props: ['level'].render(h, { props, data, children }) {
    return h(`h${props.level}`, data, children)
  }
}
Copy the code

Or, for users who prefer to use <template> in a single file component:

<! -- Vue2Examples of functional components use <template> --><template functional>
  <component
    :is="`h${props.level}`"
    v-bind="attrs"
    v-on="listeners"
  />
</template>

<script>
export default {
  props: ['level']}</script>
Copy the code

3. X syntax

  • Create components through functions

Now in Vue 3, all functional components are created as normal functions; in other words, there is no need to define the {functional: true} component option.

They will take two parameters: props and context. The context parameter is an object that contains the attrs, slots, and Emit Property of the component.

In addition, instead of implicitly supplying H in the Render function, h is now imported globally.

Using the example of the <dynamic-heading> component mentioned earlier, here’s what it looks like now.

import { h } from 'vue'

const DynamicHeading = (props, context) = > {
  return h(`h${props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']

export default DynamicHeading
Copy the code
  • Single file Component (SFC)

In 3.x, the performance difference between stateful and functional components has been greatly reduced and is negligible in most use cases. Therefore, the migration path for developers using Functional on SFCs is to remove the attribute and rename all references to props to $props and attrs to $attrs.

Using the previous <dynamic-heading> example, here’s what it looks like now.

<template>
  <component
    v-bind:is="`h${$props.level}`"
    v-bind="$attrs"
  />
</template>

<script>
export default {
  props: ['level']}</script>
Copy the code

The main differences are:

  • The functional attribute is removed in
  • Listeners are now passed as part of $ATTRs and can be removed

14. The global APINot compatible with

Vue 2.x has a number of global apis and configurations that can change the behavior of Vue globally. For example, to register global components, you can use an API like Vue.com Ponent:

Vue.component('button-counter', {
  data: () = > ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
Copy the code

Similarly, the declaration using global directives is as follows:

Vue.directive('focus', {
  inserted: el= > el.focus()
})
Copy the code

While this declaration is convenient, it can also cause problems. Technically, Vue 2 has no concept of an “app”; the application we define is simply the root Vue instance created with new Vue(). Each root instance created from the same Vue constructor shares the same global configuration, so:

  • During testing, the global configuration can easily accidentally contaminate other test cases. Users need to carefully store the original global configuration and recover it after each test (for example, reset vue.config.errorHandler). Some apis like Vue.use and vue.mixin don’t even have a way to restore the effect, which makes testing involving plug-ins particularly tricky. In fact, vue-test-utils must implement a special API called createLocalVue to handle this problem:
    import { createLocalVue, mount } from '@vue/test-utils'
    
    // Create the extended 'Vue' constructor
    const localVue = createLocalVue()
    
    // Install the plug-in globally on the "local" Vue constructor
    localVue.use(MyPlugin)
    
    // Mount the option via 'localVue'
    mount(Component, { localVue })
    Copy the code
  • The global configuration makes it difficult to share the same Vue copy between multiple “apps” on the same page, but the global configuration is different.
    // This affects both root instances
    Vue.mixin({
      / *... * /
    })
    
    const app1 = new Vue({ el: '#app-1' })
    const app2 = new Vue({ el: '#app-2' })
    Copy the code

To avoid these problems, in Vue 3 we introduce…

A new global API: createApp

Call createApp to return an application instance, which is a new concept in Vue 3:

import { createApp } from 'vue'

const app = createApp({})
Copy the code

If you are using Vue’s CDN build, then createApp is exposed via the global Vue object.

const { createApp } = Vue

const app = createApp({})
Copy the code

Application instances expose a subset of Vue2’s current global apis. The rule of thumb is that any API that globally changes Vue’s behavior will now move to the application instance. Here is a table of the current Vue2 global apis and their corresponding instance apis

2. X global API 3. X Instance API (APP)
Vue.config app.config
Vue.config.productionTip Removed (see below)
Vue.config.ignoredElements App. Config. IsCustomElement (see below)
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use App.use (see below)
Vue.prototype App. Config. GlobalProperties (see below)

All other global apis that do not globally change their behavior are now named exports. See global API Treeshaking for documentation

The config. ProductionTip removed

In Vue 3.x, the “Use production version” prompt is only displayed when using “dev + full build” (a build that includes the runtime compiler and has a warning).

For ES module builds, since they are used with Bundler and, in most cases, the CLI or boilerplate has been correctly configured for the production environment, this tip will no longer occur.

Config.ignoredelements are replaced with config.iscustomElement

This configuration option was introduced to support native custom elements, so renaming can better communicate its functionality. The new option also requires a function that provides more flexibility than the old string/RegExp method:

/ / before
Vue.config.ignoredElements = ['my-el'./^ion-/]

/ / after
const app = createApp({})
app.config.isCustomElement = tag= > tag.startsWith('ion-')
Copy the code

In Vue 3, the check for whether an element is a component has moved to the template compilation phase, so this configuration option is only considered when using the runtime compiler. If you are using the Runtime-only version, the isCustomElement must be replaced at the build step via @vue/compiler-dom — for example, via compilerOptions Option in Vue-loader.

  • If config.isCustomElement is used when a run-time only build is used, a warning is issued instructing the user to pass this option in the build Settings;
  • This will be the new top-level option in the Vue CLI configuration.

Vue.prototype is replaced with config.globalProperties

In Vue 2, vue.prototype is usually used to add properties that are accessible to all components.

Equivalent to Config.globalProperties in Vue 3. These properties will be copied to the application as part of the instantiated component.

// Before - Vue 2
Vue.prototype.$http = () = > {}
Copy the code
// After - Vue 3
const app = createApp({})
app.config.globalProperties.$http = () = > {}
Copy the code

Plug-in User Notice

Plugin developers often use vue.use. For example, how the official Vue-Router plug-in installs itself in the browser environment:

var inBrowser = typeof window! = ='undefined'
/ *... * /
if (inBrowser && window.Vue) {
  window.Vue.use(VueRouter)
}
Copy the code

Since the use global API is no longer used in Vue 3, this method will stop working and stopping calls to vue.use () will now trigger a warning, so the developer must explicitly specify the use of this plug-in on the application instance:

const app = createApp(MyApp)
app.use(VueRouter)
Copy the code

Mount the App instance

App.mount (domTarget) app.mount(domTarget) app.mount(domTarget)

import { createApp } from 'vue'
import MyApp from './MyApp.vue'

const app = createApp(MyApp)
app.mount('#app')
Copy the code

With all these changes, our components and directives at the beginning of the guide will be rewritten as follows:

const app = createApp(MyApp)

app.component('button-counter', {
  data: () = > ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})

app.directive('focus', {
  mounted: el= > el.focus()
})

// All application instances are now mounted and will have the same "button-counter" component and "focus" directive along with their component tree without fouling the global environment
app.mount('#app')
Copy the code

Provide / Inject

Similar to using the provide option on the 2.x root instance, the Vue 3 application instance can also provide dependencies that can be injected by any component within the application:

/ / at the entrance
app.provide('guide'.'Vue 3 Guide')

// In the child component
export default {
  inject: {
    book: {
      from: 'guide'}},template: `<div>{{ book }}</div>`
}
Copy the code

Using Provide is useful when writing plug-ins as an alternative to globalProperties.

Sharing configurations between applications

One way to share configuration (such as components or directives) between applications is to create a factory function like this:

import { createApp } from 'vue'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const createMyApp = options= > {
  const app = createApp(options)
  app.directive('focus'./ *... * /)

  return app
}

createMyApp(Foo).mount('#foo')
createMyApp(Bar).mount('#bar')
Copy the code

The focus directive can now be used in both Foo and Bar instances and their descendants.

15. Global API TreeshakingNot compatible with

2. X syntax

If you’ve ever manually manipulated the DOM in Vue, you’ve probably encountered the following patterns:

import Vue from 'vue'

Vue.nextTick(() = > {
  // Something related to the DOM
})
Copy the code

Or, if you’ve been unit testing an application that involves Async Components, chances are you’ve written something like this:

import { shallowMount } from '@vue/test-utils'
import { MyComponent } from './MyComponent.vue'

test('an async feature'.async() = > {const wrapper = shallowMount(MyComponent)

  // Perform some DOM-related tasks

  await wrapper.vm.$nextTick()

  // Run your assertion
})
Copy the code

Vue.nexttick () is a global API directly exposed to a single Vue object. In fact, the instance method $nextTick() is just a handy wrapper around vue.nexttick (), with the this context of the callback automatically bound to the current instance for ease of use.

But what if you’ve never handled manual DOM manipulation, or used or tested asynchronous components in your application? Or, for whatever reason, do you prefer to use the old window.settimeout () instead? In this case, the code for nextTick() becomes dead code — that is, code that was written but never used. And dead code is hardly a good thing, especially in our client-side context, where every line of code matters.

Module bundles such as WebPack support tree-shaking, which is a fancy term for “dead code elimination.” Unfortunately, because of how the code was written in previous VERSIONS of Vue, the global API vue.nexttick () is immutable and will be included in the final bundle regardless of where they are actually used.

3. X syntax

In Vue 3, both the global and internal apis have been refactored to take tree-shaking support into account. As a result, the global API is now accessible only as a named export built by an ES module. For example, our previous clip should now look like this:

import { nextTick } from 'vue'

nextTick(() = > {
  // Something related to the DOM
})
Copy the code

with

import { shallowMount } from '@vue/test-utils'
import { MyComponent } from './MyComponent.vue'
import { nextTick } from 'vue'

test('an async feature'.async() = > {const wrapper = shallowMount(MyComponent)

  // Perform some DOM-related tasks

  await nextTick()

  // Run your assertion
})
Copy the code

Calling vue.nexttick () directly results in the infamous undefined is not a function error.

With this change, if the module binder supports tree-shaking, the unused global API in the Vue application is removed from the final bundle, resulting in the optimal file size.

Affected apis

These global apis in Vue 2.x are affected by this change:

  • Vue.nextTick
  • Vue.observable (replace vue.reactive)
  • Vue.version
  • Vue.com compile (full build only)
  • Vue.set (compatible build only)
  • Vue.delete (build compatible only)

Internal helper

In addition to the public API, many internal components/helpers are now also exported as named exports, allowing the compiler to import features only if the compiler’s output is these features, such as the following template:

<transition>
  <div v-show="ok">hello</div>
</transition>
Copy the code

Is compiled to something like the following:

import { h, Transition, withDirectives, vShow } from 'vue'

export function render() {
  return h(Transition, [withDirectives(h('div'.'hello'), [[vShow, this.ok]])])
}

Copy the code

This actually means that the Transition component will only be imported if it is actually used by the application. In other words, if the application does not have any Transition components, then the code that supports this functionality will not appear in the final bundle.

With global Tree-shaking, users only “pay” for the functionality they actually use, and even better, knowing that optional features don’t increase the bundle size of applications that don’t use them, frame size is no longer a consideration for other core functionality in the future, if at all.

The above applies only to ES Modules Builds, which are used to support tree-shaking bindings — the UMD build still includes all features and exposes everything on the Vue global variables (the compiler will generate the appropriate output to use the API outside the global instead of importing it).

Usage in plug-ins

If your plugin depends on the affected Vue 2.x global API, for example:

const plugin = {
  install: Vue= > {
    Vue.nextTick(() = > {
      // ...}}})Copy the code

In Vue 3, you must explicitly import:

import { nextTick } from 'vue'

const plugin = {
  install: app= > {
    nextTick(() = > {
      // ...}}})Copy the code

If you use a module bundle like WebPack, this can result in the Vue source code being bundled into the plug-in, and often this is not what you expect. A common way to prevent this from happening is to configure the module binder to exclude Vue from the final binding. For Webpack, you can use the externals configuration option:

// webpack.config.js
module.exports = {
  / *... * /
  externals: {
    vue: 'Vue'}}Copy the code

This tells WebPack to treat the Vue module as an external library, rather than bundling it.

If the module binder you choose happens to be Rollup, you get the same effect for almost nothing, because by default, Rollup will treat absolute module ids (‘vue’ in our example) as external dependencies and not include them in the final bundle. However, during binding, it may throw a “make vue an external dependency” warning, which can be suppressed using the external option:

// rollup.config.js
export default {
  / *... * /
  external: ['vue']}Copy the code

Finally:

This note is mainly based on the official document migration policy summary. If there is any discrepancy, please refer to the official document. It is recommended that you use the official document as the main document and this article as a supplement. This way you can review the reading “for yourself” and not be prejudiced by my views

Share part of their own knowledge point article links

  • Record the interviews with front-end development engineers from the end of March to April

  • Interview high-frequency handwritten JS code

  • A large factory face

  • Webpack covers everything from the light to the deep, so that your resume is really “familiar” (Series 1)

  • Webpack covers everything from the light to the deep, so that your resume is really “familiar” (Series 2)

  • Webpack covers everything from the shallow to the deep, so that your resume really says “familiar” (Series 3)

  • Webpack covers everything from the shallow to the deep, so that your resume is really “familiar” (Series 4)

  • Webpack covers everything from the light to the deep, making your resume truly “familiar” (Series 5)