PS: Their own records with the official website can be ignored oh
Create an application instance
const app = Vue.createApp({
/ * option * /
})
Copy the code
Register “global” components in your application
const app = Vue.createApp({})
app.component('SearchInput', SearchInputComponent)
app.directive('focus', FocusDirective)
app.use(LocalePlugin)
Copy the code
Most methods exposed by an application instance return that same instance, so chaining is allowed
Vue.createApp({})
.component('SearchInput', SearchInputComponent)
.directive('focus', FocusDirective)
.use(LocalePlugin)
Copy the code
The root component
The options passed to createApp are used to configure the root component. This component is used as a starting point for rendering when we mount the application
To mount a Vue application to
, pass #app:
const RootComponent = {
/ * option * /
}
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')
Copy the code
mount
Instead of returning the application itself, the root component instance is returned
Copy the code
V – once the instructions
Perform the interpolation once and for all. When the data changes, the contents of the interpolation are not updated
This affects other data bindings on the node
Rendering pure HTML elements in Vue is very fast, but sometimes you may have a component that contains a lot of static content. In these cases, you can ensure that the root element is evaluated only once and then cached by adding the V-once instruction to it
<span v-once>This will not change: {{MSG}}</span>
Copy the code
Vue.extend(options) : Create oneVue
Subclass and return the corresponding constructor
options
: Component options
New is required for use
See: vue.extend in the VUE API
And: Three uses of vUE components in the construct component + vue.prototype
Using the base Vue constructor, create a “subclass.” The parameter is an object that contains component options.
The data option is a special case, which must be a function in vue.extend ()
<div id="mount-point"></div>
Copy the code
// Create a constructor
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>'.data: function () {
return {
firstName: 'Walter'.lastName: 'White'.alias: 'Heisenberg'}}})// Create a Profile instance and mount it to an element.
new Profile().$mount('#mount-point')
Copy the code
<! Results - - - >
<! <div id="mount point"></div>
<p>Walter White aka Heisenberg</p>
Copy the code
Build release terminology
1, the full version:
Includes both compiler and runtime versions.
2. Compiler:
Code used to compile a template string into a JavaScript rendering function.
3. Runtime:
Code for creating Vue instances, rendering and manipulating virtual DOM, and so on. Basically everything else is stripped out of the compiler.
If you needCompile templates on the client side
(Such as passing a string totemplate
Option, or mount to an element with itsDOM
The inside of the HTML
As a template), will need to add the compiler, i.e., the full version:
Set runtimeCompiler: true in vue.config.js
// A compiler is required
new Vue({ template: '<div>{{ hi }}</div>' })
// No compiler is required
new Vue({ render (h) { return h('div'.this.hi) } })
Copy the code
instruction
The dynamic parameters
You can use a JavaScript expression enclosed in square brackets as an argument to an instruction
Dynamically bound properties
<! Note that there are constraints on how parameter expressions can be written, as described later in the "Constraints on dynamic parameter Expressions" section. -->
<a v-bind:[attributeName] ="url">.</a>
Copy the code
When attributeName is “href”, this binding is equivalent to v-bind:href.
Dynamic binding method
<a v-on:[eventName] ="doSomething">.</a>
Copy the code
When the value of eventName is “focus”, v-on:[eventName] is equivalent to V-on :focus
Matters needing attention
Spaces and quotes inside HTML attribute names are invalid
<! -- This triggers a compile warning -->
<a v-bind:['foo'+bar] ="value">.</a>
Copy the code
The solution
Use expressions without Spaces or quotes, or replace such complex expressions with computed attributes
Avoid uppercase characters for key names
When using templates in the DOM (writing templates directly in an HTML file), you also need to avoid using uppercase characters for key names, because browsers force all attribute names to lowercase:
<! -- This code is converted to 'V-bind :[someattr]' when using templates in the DOM. The code will not work unless there is a property named "someattr" in the instance. -->
<a v-bind:[someAttr] ="value">.</a>
Copy the code
Calculate attribute
Calculate the property cache vs method
Computed properties are cached based on their reactive dependencies. They are reevaluated only when the associated reactive dependencies change. This means that as long as the Message has not changed, multiple visits to the reversedMessage computed property will immediately return the previous computed result without having to execute the function again
Computational property writing
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
computed: {
// Calculates the getter for the property
reversedMessage: function () {
// 'this' points to the VM instance
return this.message.split(' ').reverse().join(' ')}}Copy the code
Methods of writing
<p>Reversed message: "{{ reversedMessage() }}"</p>
// In the component
methods: {
reversedMessage: function () {
return this.message.split(' ').reverse().join(' ')}}Copy the code
Non-responsive data does not trigger recalculation
The computed properties below will not be updated because date.now () is not a reactive dependency
But if the method is written, the calling method will always execute the function again whenever a re-render is triggered
computed: { now: function () { return Date.now() } }
Copy the code
Compute properties vs. listen properties
Vue provides a more general way to observe and respond to changes in data on Vue instances: listening properties.
It’s easy to abuse Watch when you have some data that needs to change with other data.
However, it is often better to use computed properties rather than imperative Watch callbacks
Evaluate the setter for the property
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]}}}// ...
Copy the code
The listener
This approach is most useful when asynchronous or expensive operations need to be performed when data changes
<div id="watch-example">
<p> Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div><! Because the ecosystem of AJAX libraries and common tools is already quite rich, the Vue core code is not duplicated --> <! Provide these features to keep things lean. This also gives you the freedom to choose the tools you're more familiar with. --><script>
var watchExampleVM = new Vue({
el: '#watch-example'.data: {
question: ' '.answer: 'I cannot give you an answer until you ask a question! '
},
watch: {
// If 'question' changes, the function is run
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing... '
this.debouncedGetAnswer()
}
},
created: function () {
// '_. Debounce' is a function that limits the frequency of operations through Lodash.
// In this case, we want to limit the frequency of access to yesNo. WTF/API
// The AJAX request is not sent until the user has entered. Want to learn more about
// '_. Debounce' functions (and their relatives' _. Throttle '),
// Please refer to https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)},methods: {
getAnswer: function () {
if (this.question.indexOf('? ') = = = -1) {
this.answer = 'Questions usually contain a question mark. ; -) '
return
}
this.answer = 'Thinking... '
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
Copy the code
withkey
Manage reusable elements
Vue renders elements as efficiently as possible, often reusing existing elements rather than rendering them from scratch. This has other benefits besides making Vue very fast
Example: Allow users to switch between different login methods
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
Copy the code
Switching loginType in the code above will not clear what the user has entered. Because both templates use the same element, the will not be replaced — just its placeholder
Vue gives you a way to say “These two elements are completely separate, don’t reuse them”. Just add one that has a unique valuekey
The attribute can be
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
Copy the code
v-if
vs v-show
V-if is “true” conditional rendering because it ensures that event listeners and subcomponents within the conditional block are properly destroyed and rebuilt during the switch.
V-if is also lazy: if the condition is false during initial rendering, nothing is done — the conditional block is not rendered until the condition is true for the first time.
V-show, by contrast, is much simpler — elements are always rendered regardless of initial conditions and simply switch based on CSS.
In general, V-if has a higher switching overhead, while V-show has a higher initial rendering overhead. Therefore, v-show is good if you need to switch very frequently; It is better to use V-if if conditions rarely change at run time.
V – for the key
When Vue is updating a list of elements rendered using V-for, it defaults to the “update in place” policy.
If the order of data items is changed, Vue does not move DOM elements to match the order of data items, but instead updates each element in place and ensures that they are rendered correctly at each index location.
This default mode is efficient, but only for list rendering output that does not depend on child component state or temporary DOM state (for example, form input values)
To give Vue a hint that it can track the identity of each node to reuse and reorder existing elements, you need to provide a unique key for each item
v-for
与 v-if
Used together
It is not recommended to use v-if and V-for on the same element
When they are on the same node, V-for takes precedence over V-IF, which means that V-IF will run separately in each V-for loop
Event modifier
<! -- Prevent the click event from propagating -->
<a v-on:click.stop="doThis"></a>
<! Submit events no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>
<! -- modifiers can be concatenated -->
<a v-on:click.stop.prevent="doThat"></a>
<! -- only modifiers -->
<form v-on:submit.prevent></form>
<! Add event listener with event capture mode
<! Events triggered by an inner element are processed here before they are processed by the inner element.
<div v-on:click.capture="doThis">.</div>
<! Trigger handler only if event.target is the current element itself -->
<! -- that is, events are not triggered from internal elements -->
<div v-on:click.self="doThat">.</div>
Copy the code
.exact
The modifier
Allows you to control events triggered by the exact combination of system modifiers
<! -- Trigger even when Alt or Shift is pressed together --><button @click.ctrl="onClick">A</button><! -- Triggered only when Ctrl is pressed --><button @click.ctrl.exact="onCtrlClick">A</button><! -- triggered when no system modifier is pressed --><button @click.exact="onClick">A</button>
Copy the code
Form input
The modifier
.lazy
inchange
Synchronize after event _
By default, v-Model synchronizes the value of the input box with the data after each input event (except for organizing text as described above). You can add the lazy modifier to synchronize after the change event
<! Update "change" instead of "input" --><input v-model.lazy="msg" />
Copy the code
.number
Converts the user’s input value to a numeric type
<input v-model.number="age" type="number" />
Copy the code
.trim
Automatically filter the first and last blank characters entered by the user
<input v-model.trim="msg" />
Copy the code
component
// Create an application
const app = Vue.createApp({})
// Define component registration
app.component('button-counter', {
data() {
return {
count: 0}},template: ` `
})
// Hang on the instance
app.mount('#components-demo')
Copy the code
<div id="components-demo">// Use this component as a custom element in a root instance<button-counter></button-counter>
</div>
Copy the code
The component registration
app.component
Global registration
const app = Vue.createApp({})
app.component('component-a', {
/ *... * /
})
app.component('component-b', {
/ *... * /
})
app.component('component-c', {
/ *... * /
})
app.mount('#app')
Copy the code
Local registration
The component is defined by a plain JavaScript object:
const ComponentA = {
/ *... * /
}
const ComponentB = {
/ *... * /
}
const ComponentC = {
/ *... * /
}
Copy the code
Then define the components you want to use in the Components option:
const app = Vue.createApp({
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
Copy the code
A locally registered component is not available in its children
If you want ComponentA to be available in ComponentB, you need to write:
const ComponentA = {
/ *... * /
}
const ComponentB = {
components: {
'component-a': ComponentA
}
// ...
}
Copy the code
Use ES2015 modules with Babel and Webpack
import ComponentA from './ComponentA.vue'
export default {
components: {
ComponentA
}
// ...
}
Copy the code
Local registration in the module system
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
ComponentA,
ComponentC
}
// ...
}
Copy the code
Listen for child component events
Parent component listener
<blog-post ... @enlarge-text="PostFontSize + = 0.1"></blog-post>
Copy the code
Child components
<button @click="$emit (0.1) 'enlargeText',">
Enlarge text
</button>
Copy the code
Can be in the componentemits
Options list the events that were thrown
app.component('blog-post', {
props: ['title'].emits: ['enlargeText']})Copy the code
Validates thrown events
If you define emitted events using object syntax instead of array syntax, you can validate it
To add validation, the event is assigned a function that takes the parameters passed to the $emit call and returns a Boolean value indicating whether the event is valid.
app.component('custom-form', {
emits: {
// No validation
click: null.// Verify the Submit event
submit: ({ email, password }) = > {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload! ')
return false}}},methods: {
submitForm(email, password) {
this.$emit('submit', { email, password })
}
}
})
Copy the code
Use v-Models on components
<input v-model="searchText" />
Copy the code
Is equivalent to
<input :value="searchText" @input="searchText = $event.target.value" />
Copy the code
When used with components, the V-Model looks like this:
<custom-input
:model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>
Copy the code
- it
value
Attribute is bound to a name namedmodelValue
On the prop of - In its
input
The event is triggered when a new value is passed through a customupdate:modelValue
Event is thrown
The code looks like this:
app.component('custom-input', {
props: ['modelValue'].emits: ['update:modelValue'].template: ` `
})
Copy the code
The V-Model should now work perfectly on this component:
<custom-input v-model="searchText"></custom-input>
Copy the code
implementationv-model
Another way is to usecomputed
app.component('custom-input', {
props: ['modelValue'].emits: ['update:modelValue'].template: ` `.computed: {
value: {
get() {
return this.modelValue
},
set(value) {
this.$emit('update:modelValue', value)
}
}
}
})
Copy the code
Considerations when parsing DOM templates
Element placement restrictions
Some HTML elements, such as
-
, < OL >,
- ,
table>
<blog-post-row></blog-post-row>
</table>
Copy the code
The custom component
will be promoted externally as invalid content and cause the final render to fail. We can use the special IS attribute as a workaround:
<table>
<tr is="vue:blog-post-row"></tr>
</table>
Copy the code
When it is used for native HTML elements, the value of is must begin with vue: to be interpreted as a VUE component. This is to avoid confusion with native custom elements.
Case insensitive
HTML attribute names are case insensitive, so the browser interprets all uppercase characters as lowercase characters. This means that the camel prop name and event handler parameters need to use their kebab-cased (delimited character) equivalent values when you use them in DOM templates:
// Hump in JavaScript
app.component('blog-post', {
props: ['postTitle'].template: ` {{ postTitle }}
`
})
Copy the code
<! In HTML, it's a line character split --><blog-post post-title="hello!"></blog-post>
Copy the code
slot
Vue implements a content distribution API inspired by the draft Web Components specification that uses
elements as an outlet for hosting distribution content
<todo-button>
Add todo
</todo-button>
Copy the code
Then in the
template, you might have:
<! -- Todo-button component template -->
<button class="btn-primary">
<slot></slot>
</button>
Copy the code
<! -- Render HTML -->
<button class="btn-primary">
Add todo
</button>
Copy the code
Slots can also contain any template code, including HTML\ other components
If the template of a
does not contain a
element, anything between the component’s start tag and end tag is discarded
Render scope
Slot cannot access scope of
(cannot access child component data in parent component)
Everything in the parent template is compiled in the parent scope; Everything in a subtemplate is compiled in a subscope
Alternate content: Rendered when no content is provided
<button type="submit">
<slot>Submit</slot>
</button>
Copy the code
A named slot
You need multiple slots
<div class="container">
<header>
<! -- We want to put the header here -->
</header>
<main>
<! -- We want to put the main content here -->
</main>
<footer>
<! -- We want to put footer here -->
</footer>
</div>
Copy the code
For such cases, the
element has a special attribute: name. This attribute can be used to define additional slots:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
Copy the code
exits without a name have the implied name “default”
When providing content to a named slot, we can use the V-slot directive on a
element and provide its name as an argument to the V-slot
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
Copy the code
The results of
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
Copy the code
Scope slot
Sometimes it is useful to give slot content access to data that is only available in child components.
We have a component that contains a list of todo-items
app.component('todo-list', {
data() {
return {
items: ['Feed a cat'.'Buy milk']}},template: `
- {{ item }}
`
})
Copy the code
You want to replace {{item}} with
for customization on the parent component
The parent of todo-list wants to get the item<todo-list>
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
Copy the code
To make item available to slot content provided by the parent, we can add a
element and bind it as an attribute
Todo list component<ul>
<li v-for="( item, index ) in items">
<slot :item="item"></slot>
</li>
</ul>
Copy the code
Bind as many attributes to slots as you need
<ul>
<li v-for="( item, index ) in items">
<slot :item="item" :index="index" :another-attribute="anotherAttribute"></slot>
</li>
</ul>
Copy the code
Attributes bound to
elements are called slot prop. Now in parent scope, we can define the name of our supplied slot prop using v-slot with a value:
The parent of todo-list wants to get the item<todo-list>
<template v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</template>
</todo-list>
Copy the code
Exclusive default slot abbreviation syntax
In this case, the component’s label can be used as a template for the slot only if the content provided is the default slot. This allows us to use v-slot directly on components:
<todo-list v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</todo-list>
Copy the code
And this could be even simpler. Just as unspecified content is assumed to correspond to the default slot, v-slot without arguments is assumed to correspond to the default slot:
<todo-list v-slot="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</todo-list>
Copy the code
Default slot abbreviation syntaxCan’tUsed with named slots because it results in undefined scope:
<! -- invalid, will result in warning -->
<todo-list v-slot="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</todo-list>
Copy the code
Whenever multiple slots are present, always use the full
based syntax for all slots:
<todo-list>
<template v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</template>
<template v-slot:other="otherSlotProps">.</template>
</todo-list>
Copy the code
Deconstructing slot Prop
The inner workings of a scoped slot are to include the contents of your slot in a function passing in a single argument:
function (slotProps) {
/ /... Slot content...
}
Copy the code
This means that the value of the V-slot can actually be any JavaScript expression that can be used as a parameter in the function definition. You can also use ES2015 deconstruction to pass in a specific slot prop, as shown below
<todo-list v-slot="{ item }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
Copy the code
Rename item to todo:
<todo-list v-slot="{ item: todo }">
<i class="fas fa-check"></i>
<span class="green">{{ todo }}</span>
</todo-list>
Copy the code
Alternate content can even be defined for cases where slot prop is undefined
<todo-list v-slot="{ item = 'Placeholder' }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
Copy the code
Dynamic slot name
Can be used as a dynamic instruction parameter on v-slot to define a dynamic slot name:
<base-layout>
<template v-slot:[dynamicSlotName] >.</template>
</base-layout>
Copy the code
An abbreviation for named slot
Replace everything before the argument (v-slot:) with the character #
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
Copy the code
Provide / Inject
No matter how deep the component hierarchy is, a parent component can act as a dependent provider for all its children. This feature has two parts: the parent component has a provide option to provide data, and the child component has an Inject option to start using that data.
provide
Is a function that returns an object
- The parent component does not need to know which child components use the property it provides
- Child components don’t need to know where inject’s property comes from
app.component('todo-list', {
data() {
return {
todos: ['Feed a cat'.'Buy tickets']}},provide() {
return {
todoLength: this.todos.length
}
},
template: `... `
})
Copy the code
Changed the todos list, this change is not reflected in inject’s todoLength property. This is because provide/ Inject binding is not reactive by default. You can change this behavior by passing a Ref property or Reactive object to provide
Assign a combined API computed Property to the provide todoLength:
app.component('todo-list', {
// ...
provide() {
return {
todoLength: Vue.computed(() = > this.todos.length)
}
}
})
app.component('todo-list-statistics', {
inject: ['todoLength'].created() {
console.log(`Injected property: The ${this.todoLength.value}`) // > Injected property: 5}})Copy the code
Dynamic components & asynchronous components
Used on dynamic componentskeep-alive
When switching between components, keep the state of these components to avoid performance issues caused by repeated rerendering
<! -- Deactivated components will be cached! -->
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
Copy the code
Asynchronous componentsdefineAsyncComponent
Break the application into smaller code blocks and load a module from the server only when needed. To simplify, Vue has a defineAsyncComponent method:
const { createApp, defineAsyncComponent } = Vue
const app = createApp({})
const AsyncComp = defineAsyncComponent(
() = >
new Promise((resolve, reject) = > {
resolve({
template: '
I am async!
'
})
})
)
app.component('async-example', AsyncComp)
Copy the code
This method accepts a factory function that returns a Promise. Once the component definition is retrieved from the server, the Promise’s resolve callback should be invoked. You can also call reject(Reason) to indicate a load failure.
To return a Promise in the factory function, combine WebPack 2 with ES2015 syntax to import dynamically like this:
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() = >
import('./components/AsyncComponent.vue')
)
app.component('async-component', AsyncComp)
Copy the code
It can also be used when registering components locallydefineAsyncComponent
import { createApp, defineAsyncComponent } from 'vue'
createApp({
// ...
components: {
AsyncComponent: defineAsyncComponent(() = >
import('./components/AsyncComponent.vue'))}})Copy the code
Use with Suspense
Asynchronous components are suspended by default. This means that if it has a girl in Suspense> in the parent chain, it will be treated as an asynchronous dependency of that girl. In this case, the load status is controlled by
, and the component’s own load, error, delay, and timeout options are ignored.
Template reference ref
<input ref="input" />
Copy the code
const app = Vue.createApp({})
app.component('base-input', {
template: ` `.methods: {
focusInput() {
this.$refs.input.focus()
}
},
mounted() {
this.focusInput()
}
})
Copy the code
You can also add another REF to the component itself and use it to trigger the focusInput event from the parent component:
<base-input ref="usernameInput"></base-input>
this.$refs.usernameInput.focusInput()
Copy the code
Access should be avoided in templates or computed properties$refs
.
Control update$forceUpdate
Updates need to be enforced in Vue, and 99.99% of the time, you’ve made a mistake somewhere. It may depend on states not tracked by the Vue responsive system, such as data attributes added after component creation.
Transition & Animation Overview
Vue provides abstractions to help with transitions and animations, especially in response to certain changes
- In CSS and JS, use the built-in
<transition>
Components to hook components into and out of the DOM. - Transition mode, so that you can order during transitions.
- Used when processing multiple element position updates
<transition-group>
Component, using FLIP technology to improve performance. - use
watchers
To handle transitions between different states in your application.
Class-based animations and transitions
<div id="demo">
Push this button to do something you shouldn't be doing:<br />
<div :class="{ shake: noActivated }">
<button @click="noActivated = true">Click me</button>
<span v-if="noActivated">Oh no!</span>
</div>
</div>
Copy the code
const Demo = {
data() {
return {
noActivated: false
}
}
}
Vue.createApp(Demo).mount('#demo')
Copy the code
.shake {
animation: shake 0.82 s cubic-bezier(0.36.0.07.0.19.0.97) both;
transform: translate3d(0.0.0);
backface-visibility: hidden;
perspective: 1000px;
}
@keyframes shake {
10%.90% {
transform: translate3d(-1px.0.0);
}
20%.80% {
transform: translate3d(2px.0.0);
}
30%.50%.70% {
transform: translate3d(-4px.0.0);
}
40%.60% {
transform: translate3d(4px.0.0); }}Copy the code
Transitions are bound to Style
<div id="demo">
<div
@mousemove="xCoordinate"
:style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }"
class="movearea"
>
<h3>Move your mouse across the screen...</h3>
<p>x: {{x}}</p>
</div>
</div>
Copy the code
const Demo = {
data() {
return {
x: 0}},methods: {
xCoordinate(e) {
this.x = e.clientX
}
}
}
Vue.createApp(Demo).mount('#demo')
Copy the code
.movearea {
transition: 0.2 s background-color ease;
}
Copy the code
performance
Hardware-accelerated element animation whenever possible, using properties that do not trigger redraw
The Transform and Opacity
Changing transform does not trigger any geometry changes or drawing. This means that the operation may be performed by the synthesizer thread with the help of the GPU.
The opacity property behaves similarly. Therefore, they are ideal for element movement on the Web.
Hardware acceleration
Properties such as Perspective, Backface-visibility, and Transform :translateZ(x) will let the browser know you need hardware acceleration.
If you want to hardware accelerate an element, you can apply any of the following properties (not all of them, just any one will do) :
perspective: 1000px;
backface-visibility: hidden;
transform: translateZ(0);
Copy the code
Between 0.1 seconds and 0.4 seconds of exercise,0.25 sIs a best choice
If you have some elements that need to move more distance, or have more steps or state changes, 0.25s doesn’t work very well, you’ll have to be more purposeful, and the timing needs to be more unique.
Single element/component transitions
In the following cases, entry/exit transitions can be added to any element and component
- Conditional rendering (use
v-if
) - Conditional display (use
v-show
) - Dynamic components
- Component root node
<div id="demo">
<button @click="show = ! show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
Copy the code
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
Copy the code
.fade-enter-active..fade-leave-active {
transition: opacity 0.5 s ease;
}
.fade-enter-from..fade-leave-to {
opacity: 0;
}
Copy the code
When an insert or delete is included intransition
Vue does the following when an element is in a component:
- Automatically sniff out if the target element has CSS transitions or animations applied, and if so, add/remove CSS class names when appropriate.
- If the transition component provides JavaScript hook functions, these hook functions will be called at the appropriate time.
- If the JavaScript hook is not found and CSS transitions/animations are not detected, the DOM operation (insert/delete) is performed immediately in the next frame. Note: this refers to browser frame-by-frame animation, and Vue’s
nextTick
Different concept)
The transition class
There are six class switches in the entry/exit transition.
v-enter-from
: Defines the state at the beginning of the transition. It takes effect before the element is inserted and is removed the next frame after the element is inserted.v-enter-active
: Defines the state when the transition takes effect. Applied throughout the transition phase, before the element is inserted and removed after the transition/animation is complete. This class can be definedThe process time of entering the transition
.delay
andCurve function
.v-enter-to
: Defines the end state of the transition. The next frame takes effect after the element is inserted (at the same timev-enter-from
Removed) after the transition/animation is complete.v-leave-from
: Defines the beginning state of the exit transition. Immediately after the exit transition is triggered, the next frame is removed.v-leave-active
: Defines the state when the exit transition takes effect. Applies throughout the exit transition phase, takes effect immediately when the exit transition is triggered, and removes after the transition/animation is complete. This class can be definedLeave the transition process time
.delay
andCurve function
.v-leave-to
: Leaving the end state of transition. The next frame takes effect after the exit transition is triggered (at the same timev-leave-from
Removed) after the transition/animation is complete.
then v-enter-from will be replaced with my-transition-enter-from.
Custom transition class class name
You can customize the transition class name using the following attributes:
enter-from-class
enter-active-class
enter-to-class
leave-from-class
leave-active-class
leave-to-class
<link
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
rel="stylesheet"
type="text/css"
/>
<div id="demo">
<button @click="show = ! show">
Toggle render
</button>
<transition
name="custom-classes-transition"
enter-active-class="animate__animated animate__tada"
leave-active-class="animate__animated animate__bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
Copy the code
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
Copy the code
Excessive JavaScript hooks
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
:css="false"
>
<! -... -->
</transition>
Copy the code
// ...
methods: {
// --------
// ENTERING
// --------
beforeEnter(el) {
// ...
},
// When combined with CSS
The done callback is optional
enter(el, done) {
// ...
done()
},
afterEnter(el) {
// ...
},
enterCancelled(el) {
// ...
},
// --------
/ / when they leave
// --------
beforeLeave(el) {
// ...
},
// When combined with CSS
The done callback is optional
leave(el, done) {
// ...
done()
},
afterLeave(el) {
// ...
},
// leaveCancelled is used only in V-show
leaveCancelled(el) {
// ...}}Copy the code
Composition API
setup
Component options
To get started with the composite API, we first need a place where we can actually use it. In Vue components, we call this location setup.
The new setup option is executed before the component is created and acts as an entry point to the composite API once props is resolved.
insetup
You should avoid usingthis
Because it will not find the component instance
Setup calls occur before data, computed, or methods are resolved, so they cannot be retrieved in SETUP.
The setup option is a function that receives props and context, in addition to exposing everything that setup returns to the rest of the component (compute properties, methods, lifecycle hooks, and so on) as well as the component’s template.
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String.required: true}},setup(props) {
console.log(props) // { user: '' }
return {} // Anything returned here can be used for the rest of the component
}
// The "rest" of the component
}
Copy the code
Warehouse list example: The view of the warehouse list has search and filter capabilities
This component has the following responsibilities:
- The repository for this user is retrieved from the assumed external API and refreshed as the user makes any changes
- use
searchQuery
String search repository - use
filters
Object filter repository
Original implementation
// src/components/UserRepositories.vue
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String.required: true
}
},
data () {
return {
repositories: []./ / 1
filters: {... },/ / 3
searchQuery: ' ' / / 2}},computed: {
filteredRepositories () { ... }, / / 3
repositoriesMatchingSearchQuery () { ... }, / / 2
},
watch: {
user: 'getUserRepositories' / / 1
},
methods: {
getUserRepositories () {
// Use 'this.user' to get the user repository
}, / / 1
updateFilters () { ... }, / / 3
},
mounted () {
this.getUserRepositories() / / 1}}Copy the code
1. Get the user’s repository from the assumed external API and refresh it if the user makes any changes
- Warehouse list
- A function to update the warehouse list
- Returns lists and functions that can be accessed by other component options
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
// Inside our component
setup (props) {
let repositories = []
const getUserRepositories = async () => {
repositories = await fetchUserRepositories(props.user)
}
return {
repositories,// Not yet in effect because the 'repositories' variable is non-reactive. This means that from the user's point of view, the warehouse list will always be empty.
getUserRepositories // The returned function behaves the same as the method}}Copy the code
The previous code is not yet in effect because the repositoriesvariable is non-reactive. This means that from the user’s point of view, the warehouse list will always be empty.
withref
Response variable of
In Vue 3.0, we can make any reactive variable work anywhere with a new ref function, as follows:
import { ref } from 'vue'
const counter = ref(0)
Copy the code
Ref takes the parameter and returns it wrapped in an object with a value property, which can then be used to access or change the value of a reactive variable:
import { ref } from 'vue'
const counter = ref(0)
console.log(counter) // { value: 0 }
console.log(counter.value) / / 0
counter.value++
console.log(counter.value) / / 1
Copy the code
Encapsulating values in an object may seem unnecessary, but it is necessary in order to keep the behavior of different data types consistent in JavaScript.
Ref creates a reactive reference to our value. The concept of references is often used throughout the composite API.
Going to the repository list example, let’s create a reactive Repositories variable
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'
// In our component
setup (props) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
return {
repositories,
getUserRepositories
}
}
Copy the code
Now, every time we call getUserRepositories, something will change, and the view will be updated to reflect the change
insetup
Register lifecycle hooks in
To make the composite API as functional as the optional API, we also need a way to register the lifecycle hooks in setup.
The lifecycle hooks on the composite API have the same name as the optional API, but are prefixed with ON: Mounted looks like onMounted.
These functions accept a callback that is executed when the hook is called by the component.
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'
// In our component
setup (props) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
onMounted(getUserRepositories) // In 'Mounted', call 'getUserRepositories'
return {
repositories,
getUserRepositories
}
}
Copy the code
watch
Reactive change
Just as we used the Watch option in the component and set the listener on user, we can do the same with the watch function imported from Vue. It takes three arguments:
- A reactive reference or getter that you want to listen for
- A callback
- Optional configuration options
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) = > {
console.log('The new counter value is: ' + counter.value)
})
Copy the code
Here is the equivalent optional API:
export default {
data() {
return {
counter: 0}},watch: {
counter(newValue, oldValue) {
console.log('The new counter value is: ' + this.counter)
}
}
}
Copy the code
Now let’s apply this to the warehouse list example:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'
// In our component
setup (props) {
// Use 'toRefs' to create a responsive reference to prop's' user 'property
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async() = > {// Update 'prop.user' to 'user.value' to access the reference value
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
// Set a listener on the reactive reference of User Prop
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
}
Copy the code
ToRefs is used at the top of our setup. This is to ensure that our listeners react to changes in user Prop.
independentcomputed
attribute
You can use computed functions imported from Vue to create computed properties outside the Vue component
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() = > counter.value * 2)
counter.value++
console.log(counter.value) / / 1
console.log(twiceTheCounter.value) / / 2
Copy the code
You pass the first argument to the computed function, which is a getter-like callback function that outputs a read-only responsive reference.
To access the value of the newly created computed variable, we need to use.value like ref
2. Warehouse list example: BasedsearchQuery
Filter, this time using computed properties
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs, computed } from 'vue'
// In our component
setup (props) {
// Create a responsive reference to the 'user' property of props using 'toRefs'
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async() = > {// Update 'props. User' to 'user.value' to access the reference value
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
// Set a listener on the reactive reference of User Prop
watch(user, getUserRepositories)
const searchQuery = ref(' ')
const repositoriesMatchingSearchQuery = computed(() = > {
return repositories.value.filter(
repository= > repository.name.includes(searchQuery.value)
)
})
return {
repositories,
getUserRepositories,
searchQuery,
repositoriesMatchingSearchQuery
}
}
Copy the code
Extract a separateCombinatorial function
Create the useUserRepositories function:
// src/composables/useUserRepositories.js
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'
export default function useUserRepositories(user) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
}
Copy the code
Then there is the search function:
// src/composables/useRepositoryNameSearch.js
import { ref, computed } from 'vue'
export default function useRepositoryNameSearch(repositories) {
const searchQuery = ref(' ')
const repositoriesMatchingSearchQuery = computed(() = > {
return repositories.value.filter(repository= > {
return repository.name.includes(searchQuery.value)
})
})
return {
searchQuery,
repositoriesMatchingSearchQuery
}
}
Copy the code
Now that we have two separate functional modules, we can start using them in our components. Here’s how to do it:
// src/components/UserRepositories.vue
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import { toRefs } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String.required: true
}
},
setup (props) {
const { user } = toRefs(props)
const { repositories, getUserRepositories } = useUserRepositories(user)
const {
searchQuery,
repositoriesMatchingSearchQuery
} = useRepositoryNameSearch(repositories)
return {
// Because we don't care about unfiltered warehouses
// We can expose the filtered results under the name 'Repositories'
repositories: repositoriesMatchingSearchQuery,
getUserRepositories,
searchQuery,
}
},
data () {
return {
filters: {... },/ / 3}},computed: {
filteredRepositories () { ... }, / / 3
},
methods: {
updateFilters () { ... }, / / 3}}Copy the code
Setup
parameter
props
context
Props
The first argument in the setup function is props. As expected in a standard component, the props in the setup function are reactive and will be updated when a new prop is passed in.
Because props are reactive, you can’t use ES6 deconstruction, which eliminates the responsiveness of prop
// MyBook.vue
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
Copy the code
If you need to deconstruct a prop, you can do this using the toRefs function in the setup function:
// MyBook.vue
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
Copy the code
If title is an optional prop, there may be no title in the props passed in. In this case, toRefs will not create a ref for the title. You need to use toRef instead:
// MyBook.vue
import { toRef } from 'vue'
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
Copy the code
Context
The second argument passed to the setup function is context. Context is a plain JavaScript object that exposes the component’s three properties: attrs, slots, and emit
// MyBook.vue
export default {
setup(props, context) {
// Attribute (non-responsive object)
console.log(context.attrs)
// slot (non-responsive object)
console.log(context.slots)
// Trigger event (method)
console.log(context.emit)
}
}
Copy the code
Context is a normal JavaScript object, that is, it is not reactive, which means you can safely use ES6 deconstruction on context:
// MyBook.vue
export default {
setup(props, { attrs, slots, emit }){... }}Copy the code
Attrs and slots are stateful objects that are always updated as the component itself is updated. This means you should avoid deconstructing them and always refer to properties as attrs.x or slots.x.
Access the component’s property
When SETUP is executed, the component instance has not yet been created. Therefore, you can only access the following property:
props
attrs
slots
emit
In other words, you will not have access to the following component options:
data
computed
methods
Use with templates
If setup returns an object, the property of that object and the property in the props argument passed to setup are both accessible in the template:
Refs returned from setup are automatically shallow unpacked when accessed in templates, so.value should not be used in templates
<! -- MyBook.vue --><template>
<div>{{ collectionName }}: {{ readersNumber }} {{ book.title }}</div>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
props: {
collectionName: String
},
setup(props) {
const readersNumber = ref(0)
const book = reactive({ title: 'Vue 3 Guide' })
// Expose to template
return {
readersNumber,
book
}
}
}
</script>
Copy the code
Using render functions
Setup can also return a render function that directly uses reactive state declared in the same scope:
// MyBook.vue
import { h, ref, reactive } from 'vue'
export default {
setup() {
const readersNumber = ref(0)
const book = reactive({ title: 'Vue 3 Guide' })
// Note that we need to explicitly call the value of ref
return () = > h('div', [readersNumber.value, book.title])
}
}
Copy the code
usethis
Inside setup(), this is not a reference to the active instance, because setup() is called before the other component options are resolved, so this inside setup() behaves completely differently than this in the other options. This makes setup() confusing when used with other optional apis.
Lifecycle hook
The following table contains how to invoke lifecycle hooks within setup () :
Option type API | Hook inside setup |
---|---|
beforeCreate |
Not needed* |
created |
Not needed* |
beforeMount |
onBeforeMount |
mounted |
onMounted |
beforeUpdate |
onBeforeUpdate |
updated |
onUpdated |
beforeUnmount |
onBeforeUnmount |
unmounted |
onUnmounted |
errorCaptured |
onErrorCaptured |
renderTracked |
onRenderTracked |
renderTriggered |
onRenderTriggered |
activated |
onActivated |
deactivated |
onDeactivated |
setup
Is aroundbeforeCreate
和 created
Lifecycle hooks run, so you don’t need to explicitly define them. In other words, any code written in these hooks should be written directly insetup
Write in function
These functions accept a callback function that will be executed when the hook is called by the component:
// MyBook.vue
export default {
setup() {
// mounted
onMounted(() = > {
console.log('Component is mounted! ')}}}Copy the code
Provide / Inject
Called during setup() of the current active instance
<! -- src/components/MyMap.vue --><template>
<MyMarker />
</template>
<script>
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
provide: {
location: 'North Pole'.geolocation: {
longitude: 90.latitude: 135}}}</script>
Copy the code
<! -- src/components/MyMarker.vue --><script>
export default {
inject: ['location'.'geolocation']}</script>
Copy the code
insetup()
The use ofprovide
Start by explicitly importing the provide method from VUE. This allows us to call provide to define each property.
provide
The property function allows you to define a property with two arguments:
- name (
<String>
Type) - value
With the MyMap component, the values of provide can be refactored as follows:
<! -- src/components/MyMap.vue --><template>
<MyMarker />
</template>
<script>
import { provide } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
provide('location'.'North Pole')
provide('geolocation', {
longitude: 90.latitude: 135}}})</script>
Copy the code
Add responsiveness
To increase responsiveness between provide values and inject values, we can use ref or Reactive when providing values
<! -- src/components/MyMap.vue --><template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90.latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
}
</script>
Copy the code
Modify the reactive property
When reactive provide/Inject values are used, it is recommended to limit all changes to the reactive property to the component that defines provide as much as possible.
<! -- src/components/MyMap.vue --><template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90.latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
return {
location
}
},
methods: {
updateLocation() {
this.location = 'South Pole'}}}</script>
Copy the code
Sometimes we need to update the data inside the inject data component. In this case, we recommend providing a method that is responsible for changing the responsive property.
<! -- src/components/MyMap.vue --><template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90.latitude: 135
})
const updateLocation = () = > {
location.value = 'South Pole'
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation)
}
}
</script>
Copy the code
<! -- src/components/MyMarker.vue --><script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location'.'The Universe')
const userGeolocation = inject('geolocation')
const updateUserLocation = inject('updateLocation')
return {
userLocation,
userGeolocation,
updateUserLocation
}
}
}
</script>
Copy the code
To ensure that throughprovide
The data passed will not be changed by inject’s component, which we recommend for the provider’s propertyreadonly
<! -- src/components/MyMap.vue --><template>
<MyMarker />
</template>
<script>
import { provide, reactive, readonly, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90.latitude: 135
})
const updateLocation = () = > {
location.value = 'South Pole'
}
provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)
}
}
</script>
Copy the code
Template reference (ref)
When using composite apis, the concepts of reactive and template references are the same. To get a reference to an element or component instance within the template, we can declare ref as usual and return it from setup() :
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() = > {
// DOM elements will be assigned to ref after initial rendering
console.log(root.value) // <div>This is a root element</div>
})
return {
root
}
}
}
</script>
Copy the code
In the previous steps: 1, expose root in the render context; 2, bind it to div as its ref with ref=”root”
In the virtual DOM patching algorithm, if the REF key of a VNode corresponds to the REF in the rendering context, the corresponding element or component instance of a VNode will be assigned to the value of that REF.
JSX
export default {
setup() {
const root = ref(null)
return () = >
h('div', {
ref: root
})
// with JSX
return () = > <div ref={root} />}}Copy the code
v-for
(there are multiple Refs in the page)
Composite API template references have no special handling when used within V-for. Instead, use function references to perform custom processing:
<template>
<div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
{{ item }}
</div>
</template>
<script>
import { ref, reactive, onBeforeUpdate } from 'vue'
export default {
setup() {
const list = reactive([1.2.3])
const divs = ref([])
// Make sure to reset the ref before each update
onBeforeUpdate(() = > {
divs.value = []
})
return {
list,
divs
}
}
}
</script>
Copy the code
Listening for template references
Listening for changes to template references can replace the lifecycle hooks demonstrated in the previous example.
But a key difference from lifecycle hooks is that watch() and watchEffect() run side effects before the DOM is mounted or updated, so the template reference has not yet been updated when the listener runs.
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, watchEffect } from 'vue'
export default {
setup() {
const root = ref(null)
watchEffect(() = > {
// This side effect runs before DOM updates, so the template reference does not yet hold a reference to the element.
console.log(root.value) // => null
})
return {
root
}
}
}
</script>
Copy the code
Listeners that use template references should be defined with the Flush: ‘POST’ option, which runs side effects after DOM updates, ensuring that template references are in sync with the DOM and refer to the correct elements.
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, watchEffect } from 'vue'
export default {
setup() {
const root = ref(null)
watchEffect(() = > {
console.log(root.value) // => <div>This is a root element</div>
},
{
flush: 'post'
})
return {
root
}
}
}
</script>
Copy the code
Mixin
Mixins provide a very flexible way to distribute reusable functionality in Vue components.
A mixin object can contain any component option.
When a component uses a mixin object, the options for all mixin objects are “blended” into the options for the component itself.
// define a mixin object
const myMixin = {
created() {
this.hello()
},
methods: {
hello() {
console.log('hello from mixin! ')}}}// define an app that uses this mixin
const app = Vue.createApp({
mixins: [myMixin]
})
app.mount('#mixins-basic') // => "hello from mixin!"
Copy the code
Data option merge
Each mixin can have its own data function. Each data function is called and the returned results are merged. In case of a property conflict, the component’s own data takes precedence.
const myMixin = {
data() {
return {
message: 'hello'.foo: 'abc'}}}const app = Vue.createApp({
mixins: [myMixin],
data() {
return {
message: 'goodbye'.bar: 'def'}},created() {
console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" }}})Copy the code
Hook function merge
The hook function of the same name will be merged into an array, so both will be called.
The hook of a mixin object will be called before the component’s own hook
const myMixin = {
created() {
console.log('Mixin object's hook is called')}}const app = Vue.createApp({
mixins: [myMixin],
created() {
console.log('Component hook called')}})// => "Mixin object hook is called"
// => "Component hook is called"
Copy the code
Value is an option for the objectmethods
,components
和 directives
Will be merged into the same object. When two object key names conflict, the component object’s key-value pair is taken
const myMixin = {
methods: {
foo() {
console.log('foo')},conflicting() {
console.log('from mixin')}}}const app = Vue.createApp({
mixins: [myMixin],
methods: {
bar() {
console.log('bar')},conflicting() {
console.log('from self')}}})const vm = app.mount('#mixins-basic')
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
Copy the code
Global mixin
const app = Vue.createApp({
myOption: 'hello! '
})
// Inject a handler for the custom option 'myOption'.
app.mixin({
created() {
const myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
app.mount('#mixins-global') // => "hello!"
Copy the code
Mixins can also be registered globally. Use with extreme care! Once a global mixin is used, it affects every component created later (for example, every child component).
const app = Vue.createApp({
myOption: 'hello! '
})
// Inject a handler for the custom option 'myOption'.
app.mixin({
created() {
const myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
// Add myOption to the child component as well
app.component('test-component', {
myOption: 'hello from component! '
})
app.mount('#mixins-global')
// => "hello!"
// => "hello from component!"
Copy the code
The custom option merge strategy app. Config. OptionMergeStrategies
The merge policy accepts the value of this option defined on the parent and child instances as the first and second arguments, respectively
const app = Vue.createApp({})
app.config.optionMergeStrategies.customOption = (toVal, fromVal) = > {
// return mergedVal
}
Copy the code
const app = Vue.createApp({
custom: 'hello! '
})
app.config.optionMergeStrategies.custom = (toVal, fromVal) = > {
console.log(fromVal, toVal)
// => "goodbye!" , undefined
// => "hello", "goodbye!"
return fromVal || toVal
}
app.mixin({
custom: 'goodbye! '.created() {
console.log(this.$options.custom) // => "hello!"}})Copy the code
Custom instruction
Custom instructions are used when low-level operations need to be performed on ordinary DOM elements
Global registration
const app = Vue.createApp({})
// Register a global custom directive 'V-focus'
app.directive('focus', {
// When the bound element is mounted into the DOM...
mounted(el) {
// Focus elements
el.focus()
}
})
Copy the code
Local registration
The component also accepts a cache option:
directives: {
focus: {
// The definition of a directive
mounted(el) {
el.focus()
}
}
}
Copy the code
use
<input v-focus />
Copy the code
Hook function
An instruction definition object can provide the following hook functions (all optional) :
created
: called before the attribute or event listener of the bound element is applied. The instruction needs to be appended in the normalv-on
This is useful when the event listener is called before the event listener.beforeMount
Called when the directive is first bound to an element and before the parent component is mounted.mounted
: called after the parent component of the bound element has been mounted.beforeUpdate
: called before updating the VNode containing the component.updated
: in a VNode that contains componentsVnodes and their childrenCall after update.beforeUnmount
: called before uninstalling the parent component of the bound elementunmounted
: is called only once when the directive is unbound from an element and the parent component is unmounted.
Dynamic instruction parameter
Customize the parameters of the API hook functions (i.e., EL, Binding, vNode, and prevVnode)
The parameters of an instruction can be dynamic. For example, in v-myDirective :[argument]=”value”, the argument argument can be updated based on component instance data! This allows custom instructions to be used flexibly in applications
Example:
<div id="dynamicexample">
<h2>Scroll down the page</h2>
<input type="range" min="0" max="500" v-model="pinPadding">
<p v-pin:[direction] ="pinPadding">Stick me {{ pinPadding + 'px' }} from the {{ direction || 'top' }} of the page</p>
</div>
Copy the code
const app = Vue.createApp({
data() {
return {
direction: 'right'.pinPadding: 200
}
}
})
app.directive('pin', {
mounted(el, binding) {
el.style.position = 'fixed'
const s = binding.arg || 'top'
el.style[s] = binding.value + 'px'
},
updated(el, binding) {
const s = binding.arg || 'top'
el.style[s] = binding.value + 'px'
}
})
app.mount('#dynamic-arguments-example')
Copy the code
Function shorthand
Mounts and updated trigger the same behavior, regardless of the other hook functions. You can do this by passing the callback to the command:
app.directive('pin'.(el, binding) = > {
el.style.position = 'fixed'
const s = binding.arg || 'top'
el.style[s] = binding.value + 'px'
})
Copy the code
Object literals
If the directive requires multiple values, you can pass in a JavaScript object literal. Remember that instruction functions can accept any valid JavaScript expression.
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Copy the code
app.directive('demo'.(el, binding) = > {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
Copy the code
Used in components
Like non-prop attributes, when used in components, custom directives are always applied to the root node of the component
<my-component v-demo="test"></my-component>
Copy the code
app.component('my-component', {
template: '
// V-demo directive will be applied here
My component content
'
})
Copy the code
Unlike attributes, directives are not passed to another element via v-bind=”$attrs”
Teleport
Sometimes a part of a component template logically belongs to that component, and from a technical point of view, it is best to move that part of the template to a location in the DOM other than the Vue app.
Teleport provides a clean way to control which parent node in the DOM renders HTML without having to resort to global state or split it into two components.
<teleport to="Label name"></teleport>
Copy the code
Common Scenarios
Create a modal box component that contains full-screen mode
The logic of the modal box is expected to exist in the component, but the quick positioning of the modal box is difficult to solve with CSS, or requires changing the component composition.
const app = Vue.createApp({});
app.component('modal-button', {
template: `
I'm a modal!
`.data() {
return {
modalOpen: false}}})Copy the code
Modify modal-button to use
and tell Vue “teleport this HTML to the ‘body’ tag”
app.component('modal-button', {
template: `
I'm a teleported modal! (My parent is "body")
`.data() {
return {
modalOpen: false}}})Copy the code
Use with Vue Components
If
contains a Vue component, it will still be a logical child of the parent of < Teleport > :
The injection from the parent component works as expected, and the child component is nested beneath the parent component in Vue Devtools, not where the actual content is moved to
const app = Vue.createApp({
template: ` Root instance
`
})
app.component('parent-component', {
template: ` This is a parent component
`
})
app.component('child-component', {
props: ['name'].template: `
Hello, {{ name }}
`
})
Copy the code
In this case, even if the Child-Component is rendered in a different place, it will still be a child of parent-Component and will receive a Name prop from it
Use multiple teleports on the same target
A reusable
component that may have multiple instances active at the same time. In this case, multiple < Teleport > components can mount their contents to the same target element. The order is the order in which they are mounted
<teleport to="#modals">
<div>A</div>
</teleport>
<teleport to="#modals">
<div>B</div>
</teleport>
<! -- result-->
<div id="modals">
<div>A</div>
<div>B</div>
</div>
Copy the code