Mixed with

basis

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 mixin, all mixin options are “blended” into the component’s own options.

Example:

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

Option to merge

When components and mixins have options with the same name, those options are “merged” in the appropriate way.

For example, data objects are internally recursively merged and component data takes precedence in the event of a conflict.

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

The hook function of the same name will be merged into an array, so both will be called. In addition, hooks mixed in with objects are called before the component’s own hooks.

const myMixin = {
  created() {
    console.log('mixin hook called')}}const app = Vue.createApp({
  mixins: [myMixin],
  created() {
    console.log('component hook called')}})// => "mixin hook called"
// => "component hook called"
Copy the code

Options that are values for objects, such as Methods, Components, and directives, will be combined 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

With global

You can also apply mixins globally to Vue applications:

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

Mixin can also be registered globally. Use with extreme care! Once 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

Customize option merge policy

The custom option will use the default strategy of simply overwriting existing values. If you want a custom options with custom logic to merge, you can ask the app. Config. OptionMergeStrategies add a function:

const app = Vue.createApp({})

app.config.optionMergeStrategies.customOption = (toVal, fromVal) = > {
  // return mergedVal
}
Copy the code

The merge policy accepts the value of this option defined on the parent and child instances as the first and second arguments, respectively. Let’s examine what these parameters are when using mixins:

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

As you can see, in the console, we print toVal and fromVal first from mixin and then from app. If it does, we always return fromVal, that’s why this.$options.custom is set to hello! At last. Let’s try changing the policy to always return a value from a subinstance:

const app = Vue.createApp({
  custom: 'hello! '
})

app.config.optionMergeStrategies.custom = (toVal, fromVal) = > toVal || fromVal

app.mixin({
  custom: 'goodbye! '.created() {
    console.log(this.$options.custom) // => "goodbye!"}})Copy the code

Teleport

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.

Let’s 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

Therefore, once we click the button to open the mode, Vue will correctly render the modal content as a child of the Body tag.

Use multiple teleports on the same target

A common use-case scenario is 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 will be a simple append — the later mount will follow the earlier mount in the target element.

<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

The plug-in

Writing a plug-in

To better understand how to create your own version of the vue.js plug-in, we’ll create a very simplified version of the plug-in that displays strings prepared by i18n.

Whenever this plug-in is added to the application, the Install method is called if it is an object. If it is a function, the function itself will be called. In both cases — it receives two arguments: the App object generated by Vue’s createApp and the option passed in by the user.

Let’s start by setting up the plug-in object. It is recommended to create it in a separate file and export it, as shown below, to preserve the contained and detached logic.

// plugins/i18n.js
export default {
  install: (app, options) = > {
    // Plugin code goes here}}Copy the code

We want a function to translate the whole application available key, so we will use the app. The config. GlobalProperties exposed it.

This function will receive a key string, which we will use to find the transformed string in the user-provided options.

// plugins/i18n.js
export default {
  install: (app, options) = > {
    app.config.globalProperties.$translate = key= > {
      return key.split('. ').reduce((o, i) = > {
        if (o) return o[i]
      }, i18n)
    }
  }
}
Copy the code

We assume that when the user uses the plug-in, an object containing the translated key will be passed in the Options parameter. Our $translate function will use a string such as greetings.hello to look inside the user-supplied configuration and return the transformed value – in this case Bonjour! .

Such as:

greetings: {
  hello: 'Bonjour! '
}
Copy the code

Plug-ins also allow us to use Inject to provide functionality or attributes to the users of the plug-in. For example, we can allow the application to access the options parameter to be able to use the translation object.

// plugins/i18n.js
export default {
  install: (app, options) = > {
    app.config.globalProperties.$translate = key= > {
      return key.split('. ').reduce((o, i) = > {
        if (o) return o[i]
      }, i18n)
    }

    app.provide('i18n', options)
  }
}
Copy the code

Plug-in users can now inject[in18] into their component and access the object.

In addition, since we have access to app objects, the plug-in can use all other functions, such as mixin and directive. To learn more about createApp and Application instances, see the Application API documentation.

// plugins/i18n.js
export default {
  install: (app, options) = > {
    app.config.globalProperties.$translate = (key) = > {
      return key.split('. ')
        .reduce((o, i) = > { if (o) return o[i] }, i18n)
    }

    app.provide('i18n', options)

    app.directive('my-directive', {
      bind (el, binding, vnode, oldVnode) {
        // some logic ...}... }) app.mixin({created() {
        // some logic ...}... }}})Copy the code

The use of plug-in

After initializing the Vue application with createApp(), you can add the plug-in to your application by calling the Use () method.

We will demonstrate this using the i18nPlugin created in the writing plug-in section.

The use() method takes two arguments. The first is the plug-in to install, in this case the i18nPlugin.

It also automatically prevents you from using the same plug-in more than once, so multiple calls to the same plug-in will only install the plug-in once.

The second parameter is optional and depends on each specific plug-in. In the case of the i18nPlugin, it is an object with a transformed string.

INFO

If you are using a third-party plug-in (such as Vuex or Vue Router), always check the documentation to see what a particular plug-in is expected to receive as a second parameter.

import { createApp } from 'vue'
import Root from './App.vue'
import i18nPlugin from './plugins/i18n'

const app = createApp(Root)
const i18nStrings = {
  greetings: {
    hi: 'Hallo! '
  }
}

app.use(i18nPlugin, i18nStrings)
app.mount('#app')
Copy the code