1. v-model

(1) Multiple V-Model binding

<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>
Copy the code
const app = Vue.createApp({})

app.component('user-name', {
  props: {
    firstName: String.lastName: String
  },
  template: `   `
})
Copy the code

(2) Custom modifiers

We can create an example custom modifier,.capitalize, that capitalizes the first letter of the string provided by the V-Model binding.

The modifier v-model added to the component will be provided to the component through modelModifiersprop. In the example below, we create a component that contains a prop with a modelModifiers default value of empty objects.

When the component’s Created lifecycle hook is triggered, modelModifiersprop contains capitalize and its value is true,, Because this is the V-Model.capitalize set on the V-ModelBinding, if you don’t add. Capitalize the modelModifiers is the default null object

<modelComponent v-model.capitalize="modelValue"></modelComponent>
Copy the code
app.component('my-component', {
  props: {
    modelValue: String.modelModifiers: {
      default: () = >({})}},methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)}this.$emit('update:modelValue', value)
    }
  },
  template: ``
})
Copy the code

2. Inheritance of non-prop properties

Attributes (such as class ID) are automatically inherited from the root node by default, for example:

app.component('date-picker', {
  template: ` 
      
`
}) Copy the code
<! -- Date-picker component with a non-prop attribute -->
<date-picker data-status="activated"></date-picker>

<! -- Rendered date-picker component -->
<div class="date-picker" data-status="activated">
  <! -- Rendered date-picker component -->
<div class="date-picker" data-status="activated">
  <input type="datetime" />
</div>
Copy the code

If you want attributes to be inherited to a node other than the root node, you can set inheritAttrs: false in the component’s options.

app.component('date-picker', {
  inheritAttrs: false,
  template: `
    <div class="date-picker">
      <input type="datetime" v-bind="$attrs" />
    </div>`})Copy the code

3. The slot

(1) Scoped Slots

Sometimes we need to get the child component content data in the parent component slot. For example, we have a component that contains a to-do list.

app.component('todo-list', {
  data() {
    return {
      items: ['Feed a cat'.'Buy milk']}},template: ` 
      
  • {{ item }}
`
}) Copy the code

We might want to customize the contents of the parent component by replacing the default contents with slots:

<todo-list>
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
Copy the code

However, this won’t work because only the

component can access the item, and we can’t get the slot content provided by its parent.

To make item available in the slot provided by the parent, we can add an element and bind it as an attribute:

<ul>
  <li v-for="( item, index ) in items">
    <slot v-bind:item="item"></slot>
  </li>
</ul>
Copy the code

The parent component:

<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

(2) Shorthand and accept JavaScript expressions

This can be abbreviated if the default slot is used, and because scoped slots work by wrapping the contents of the slot inside a function that passes a single argument, V-slot can accept any valid JavaScript expression that can appear at the argument position defined by the function:

<todo-list v-slot="{ item = 'Placeholder' }">
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
Copy the code

(3) Dynamic slot

<base-layout>
  <template v-slot:[dynamicSlotName] >.</template>
</base-layout>
Copy the code

(4) Grammar sugar

V-slot can be replaced with #. For example, v-slot:header can be rewritten to #header:

<base-layout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default="{ item }">
    <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

4. provide/inject

(1) By default, the following is used for non-response data transmission

The parent component:

<template>
  <modelComponent></modelComponent>
</template>
Copy the code
import modelComponent from '.. /components/model-component.vue'
export default {
  name: 'home'.provide: {
    user: 'John Doe'}}Copy the code

Child components:

<template>{{user}}</template>
Copy the code
export default {
  name: 'model-component'.inject: ['user'],}Copy the code

To provide some component instance properties, write:

import modelComponent from '.. /components/model-component.vue'
export default {
  name: 'home'.data() {
    return {
      todos: ['Feed a cat'.'Buy tickets']}},provide() {
    return {
      todoLength: this.todos.length
    }
  }
}
Copy the code

Child components:

<template>Todos length {{todoLength}}</template>
Copy the code
export default {
  name: 'model-component'.inject: ['todoLength'],}Copy the code

(2) Make the transferred data responsive

The parent component:

<template>
  <h3>Change the value of provide</h3>
  <input type="text" v-model="provideVal" />
  <modelComponent></modelComponent>
  <h3>Change user to {{user}}</h3>
  <button @click="user.push('3')">Adding the number of Users</button>
  <proComponent></proComponent>
</template>
Copy the code
import { provide,ref,reactive } from 'vue'
import modelComponent from '.. /components/model-component.vue'
import proComponent from '.. /components/provide-componet.vue'

export default {
  name: 'home'.components: {
    modelComponent,
    proComponent
  },
  setup() { // The setup function is executed before the component instance is created. So the only properties that can be accessed are props, {attrs, slots, emit}.
    const provideVal = ref(' '); // ref makes the independent primitive type reactive, passing values by reference. (But after the test can also make object,array into responsive, internal changes will also respond)
    const user = reactive(['2']); // Reactive Makes objects, arrays, and so on reactive, a "deep" transformation that affects all nested properties
    provide('provideVal', provideVal);
    provide('user', user);
    return {
      provideVal,
      user
    }
  }
}
Copy the code

Child components:

ModelComponent:

<template>
  <h2>provide</h2>
  <h4>Provide {{provideVal}}</h4>
  <input type="text" v-model="provideVal">
</template>
Copy the code
import { inject } from 'vue'
export default {
  name: 'model-component'.setup() {
    const provideVal = inject('provideVal');
    return {
      provideVal
    }
  }
}
Copy the code

ProComponent:

<template>
  <h2>The value of user in the child component is {{user}}</h2>
</template>
Copy the code
import { inject } from 'vue'
export default {
  name: 'provide-component'.setup() {
    const user = inject('user');
    return {
      user
    }
  }
}
Copy the code

5. Load components asynchronously

Vue provides a defineAsyncComponent method that allows us to load components asynchronously.

(1) Global registration

import { createApp,defineAsyncComponent } from 'vue'
import App from './App.vue'

const modelComponent = defineAsyncComponent(() = >
  import('./components/model-component.vue'));const app = createApp(App);
app.component('modelComponent', modelComponent);
app.mount('#app');
Copy the code

(2) Local registration of components

import { createApp, defineAsyncComponent } from 'vue'

createApp({
  // ...
  components: {
    modelComponent: defineAsyncComponent(() = >
      import('./components/modelComponent.vue'))}})Copy the code

(3) Optional variables

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent({
  loader: () = > import('./Foo.vue')
  // The common loaded component that is displayed when the component is loaded
  loadingComponent: LoadingComponent,
  // Public load failed component displayed when component load failed
  errorComponent: ErrorComponent,
  // Displays the delay before the common component is loaded. Default: 200ms.
  delay: 200.// Load timeout displays loading error components. Default: Infinity.
  timeout: 3000.Suspense>. Default: true defines whether to use the parent component.
  suspensible: false./ * * * *@param {*} error Error message object
   * @param {*} Retry retry function *@param {*} Fail load error end *@param {*} Number of attempts */
  onError(error, retry, fail, attempts) {
    if (error.message.match(/fetch/) && attempts <= 3) {
      // retry on fetch errors, 3 max attempts
      retry()
    } else {
      // Note that retry/fail are like resolve/reject of a promise:
      // one of them must be called for the error handling to continue.
      fail()
    }
  },
})
Copy the code

The Suspense> component is used to show fallback content while waiting for an asynchronous component to resolve, set in the parent component. Usage:

<template> 
  <div v-if="errMsg"> {{ errMsg }} </div> 
  <Suspense v-else> 
    <template #default> 
      <articleInfo/>// Components that need to be loaded asynchronously</template> 
    <template #fallback> 
      <div>Is desperately loading...</div>/ / loading condition</template> 
  </Suspense> 
</template> 
<script> 
import { onErrorCaptured,defineAsyncComponent } from 'vue'
export default {
  name: 'home'.components: {
    articleInfo: defineAsyncComponent(() = >
      import('./components/articleInfo.vue'))},setup() {
    setup () { 
    const errMsg = ref(null) 
    onErrorCaptured(e= > { // Catch loading component error message
      errMsg = 'Well, something's wrong! ' 
      return true 
    })} 
    return { errMsg }
  }
} 
</script> 
Copy the code

6. The transition component

(1) Specify the transition time

<transition :duration="1000">.</transition>/ / or<transition :duration="{ enter: 500, leave: 800 }">.</transition>
Copy the code

(2) Define 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" <!--add:css="false"Is to skip overCSSdetection-->
>
  <! -... -->
</transition>
Copy the code
// ...
methods: {
  // --------
  // ENTERING
  // --------

  beforeEnter(el) {
    // ...
  },
  // The done callback is required, otherwise the hook function will be called synchronously and the transition will complete immediately
  enter(el, done) {
    // ...
    done()
  },
  afterEnter(el) {
    // ...
  },
  enterCancelled(el) {
    // ...
  },

  // --------
  // LEAVING
  // --------

  beforeLeave(el) {
    // ...
  },
  // The done callback is required, otherwise the hook function will be called synchronously and the transition will complete immediately
  leave(el, done) {
    // ...
    done()
  },
  afterLeave(el) {
    // ...
  },
  // leaveCancelled can only be used for V-show
  leaveCancelled(el) {
    // ...}}Copy the code

(3) Use transition effects for initial rendering

<transition appear>
  <! -... -->
</transition>
Copy the code

(4) Transition mode

Transition you can choose from two additional transition modes:

  • Default: Transitions between old and new elements.

  • In-out: The transition of the new element is completed first, and the transition of the old element is completed later.

  • Out-in: The current old element is moved out first and then the new element is moved in.

These modes are usually used to switch between two elements. Such as:

<transition name="mode-fade" mode="out-in">
  <button v-if="on" key="on" @click="on = false">
    on
  </button>
  <button v-else key="off" @click="on = true">
    off
  </button>
</transition>
Copy the code
#demo {
  position: relative;
}

button {
  position: absolute;
}

.mode-fade-enter-active..mode-fade-leave-active {
  transition: opacity .5s ease
}

.mode-fade-enter-from..mode-fade-leave-to {
  opacity: 0
}

button {
  background: #05ae7f;
  border-radius: 4px;
  display: inline-block;
  border: none;
  padding: 0.5 rem 0.75 rem;
  text-decoration: none;
  color: #ffffff;
  font-family: sans-serif;
  font-size: 1rem;
  cursor: pointer;
  text-align: center;
  -webkit-appearance: none;
  -moz-appearance: none;
}
Copy the code

7. Mixins

Mixins can make any Vue component option reusable. When a component uses mixins, the options are mixed into the component’s own options. If there are duplicate options, the component’s own options take precedence. If there are the same hook functions, the function content is merged together, and the Mixin hooks are called before the component’s own hooks. Examples are as follows:

const myMixin = {
  data() {
    return {
      message: 'hello'.foo: 'abc'}},created() {
    console.log('mixin hook called')}}const app = Vue.createApp({
  mixins: [myMixin],
  data() {
    return {
      message: 'goodbye'.bar: 'def'}},created() {
    console.log(this.$data)
  }
})
// => "mixin hook called"
// => { message: "goodbye", foo: "abc", bar: "def" }
Copy the code

8. teleport

The Teleport component allows us to render the target component in a specified location.

app.component('modal-button', {
  template: `
    <button @click="modalOpen = true">
        Open full screen modal! (With teleport!)
    </button>

    <teleport to="body">
      <div v-if="modalOpen" class="modal">
        <div>
          I'm a teleported modal! 
          (My parent is "body")
          <button @click="modalOpen = false">
            Close
          </button>
        </div>
      </div>
    </teleport>
  `,
  data() {
    return { 
      modalOpen: false
    }
  }
})
Copy the code

The Modal component accurately renders as a child of the body.