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)
has been removed
- 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)