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

mountInstead 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 oneVueSubclass 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 itsDOMThe inside of the HTMLAs 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

withkeyManage 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 valuekeyThe 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-ifUsed 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

.exactThe 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

.lazyinchangeSynchronize 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

.numberConverts the user’s input value to a numeric type

<input v-model.number="age" type="number" />
Copy the code

.trimAutomatically 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.componentGlobal 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 componentemitsOptions 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
  • itvalueAttribute is bound to a name namedmodelValueOn the prop of
  • In itsinputThe event is triggered when a new value is passed through a customupdate:modelValueEvent 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-modelAnother 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 >,

  • ,
  • , and < SELECT >, have strict restrictions on which elements can appear inside them. Some elements, such as

    , and
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

will be replaced with “Add Todo” when the component is rendered.

<! -- 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

<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

<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.

 provideIs 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.
  • usewatchersTo 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 (usev-if)
  • Conditional display (usev-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 intransitionVue does the following when an element is in a component:

  1. Automatically sniff out if the target element has CSS transitions or animations applied, and if so, add/remove CSS class names when appropriate.
  2. If the transition component provides JavaScript hook functions, these hook functions will be called at the appropriate time.
  3. 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’snextTickDifferent concept)

The transition class

There are six class switches in the entry/exit transition.

  1. 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.
  2. 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.delayandCurve function.
  3. 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-fromRemoved) after the transition/animation is complete.
  4. v-leave-from: Defines the beginning state of the exit transition. Immediately after the exit transition is triggered, the next frame is removed.
  5. 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.delayandCurve function.
  6. 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-fromRemoved) 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

setupComponent 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.

insetupYou should avoid usingthisBecause 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:

  1. The repository for this user is retrieved from the assumed external API and refreshed as the user makes any changes
  2. usesearchQueryString search repository
  3. usefiltersObject 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.

withrefResponse 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

insetupRegister 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

watchReactive 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.

independentcomputedattribute

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: BasedsearchQueryFilter, 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

  1. props
  2. 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

setupIs aroundbeforeCreate 和 createdLifecycle hooks run, so you don’t need to explicitly define them. In other words, any code written in these hooks should be written directly insetupWrite 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.

provideThe property function allows you to define a property with two arguments:
  1. name (<String>Type)
  2. 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 throughprovideThe 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 和 directivesWill 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-onThis is useful when the event listener is called before the event listener.
  • beforeMountCalled 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 element
  • unmounted: 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 ScenariosCreate 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: `  
       `.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: `  
       
        
       `.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