SoFunction
Updated on 2025-04-04

vue uses mixins to optimize components

vue provides the mixins API, which allows us to extract the reusable functions in the component, put them into the mixin, and then introduce mixin into the component, which makes the component no longer look bloated and improves the reusability of the code.

How to understand mixins? We can understand mixins as an array, where there are single or multiple mixins in the array. The essence of mixin is a JS object, which can have all the properties that are owned in vue instances such as data, created, methods, etc., and even nest mixins again in mixins, It's amazing!

Let me give you a simple chestnut:

<div >
  <h1>{{ message }}</h1>
</div>
const myMixin = {
  data() {
    return {
      message: 'this is mixin message'
    }
  },
  created() {
    ('mixin created')
  }
}

const vm = new Vue({
  el: '#app',
  mixins: [myMixin],

  data() {
    return {
      message: 'this is vue instance message'
    }
  },
  created() {
    ()
    // => Root Vue Instance
    ('vue instance created')
    // => created myMixin
    // => created Root Vue Instance
  }
})

When mixins and Vue Instance are merged, hook functions such as created will be merged into an array. The hooks of mixins will be called first. When the key values ​​of data and methods objects conflict, component will be preferred.

PS: If you don’t have a clear idea of ​​mixins, you can govue official documentationTake a look at the basic concepts and usage of vue mixins.

mixins implementation

So how is mixins implemented? When vue is instantiated, the mergeOptions function is called to merge options, and the function declaration is in the vue/src/core/util/ file.

export function mergeOptions(
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  ...
  // If there is a recursive call to mergeOptions to implement attribute copying  const extendsFrom = 
  if (extendsFrom) {
    parent = mergeOptions(parent, extendsFrom, vm)
  }
  // If there is a recursive call to mergeOptions to implement attribute copying  if () {
    for (let i = 0, l = ; i &lt; l; i++) {
      parent = mergeOptions(parent, [i], vm)
    }
  }
  // Declare options empty object, used to save the attribute copy result  const options = {}
  let key
  // traverse the parent object and call mergeField to copy the attribute  for (key in parent) {
    mergeField(key)
  }
  // traverse the parent object and call mergeField to copy the attribute  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
  // Property copy implementation method  function mergeField(key) {
    // Penetration assignment, defaultStrat    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

In order to keep the code concise, the code that is not important to the mergeOptions function has been deleted. Let's take a look at the rest.

const extendsFrom = 
if (extendsFrom) {
  parent = mergeOptions(parent, extendsFrom, vm)
}

First, declare that the extendsFrom variable is saved. If extendsFrom is true, recursively call mergeOptions to copy the attribute, and save the merge result to the parent variable.

if () {
  for (let i = 0, l = ; i < l; i++) {
    parent = mergeOptions(parent, [i], vm)
  }
}

If true, loop the mixins array, call mergeOptions recursively to implement attribute copying, and still save the merge result to the parent variable.

Next is about the attribute assignment of parent and child:

const options = {}
let key

for (key in parent) {
  mergeField(key)
}

for (key in child) {
  if (!hasOwn(parent, key)) {
    mergeField(key)
  }
}

Declare options empty object, used to save the result of the attribute copy, and also serves as the return value of the recursive call to mergeOptions.

Here, for...in will be called to loop the parent, and the mergeField function will be continuously called in the loop.

Then call for...in to loop on child. This is a bit different here. HaOwn will be called to determine whether there is this key on parent. If the mergeField function is not called again, this avoids repeated calls.

So what exactly is this mergeField function used for?

function mergeField(key) {
  // Penetration assignment, defaultStrat  const strat = strats[key] || defaultStrat
  options[key] = strat(parent[key], child[key], vm, key)
}

The mergeField function receives a key and first declares the strat variable. If strats[key] is true, strats[key] is assigned to strat.

const strats = 
...
optionMergeStrategies: (null),
...

strats is actually (null), used to create a new object. strats is an empty object generated by calling (null).

By the way, vue is also exposed to the outside, allowing custom option merge strategies to be implemented.

If strats[key] is false, || will be used for penetration assignment here, and the defaultStrat default function will be assigned to strat.

const defaultStrat = function(parentVal: any, childVal: any): any {
  return childVal === undefined ? parentVal : childVal
}

The defaultStrat function returns a ternary expression. If childVal is undefined, it returns parentVal, otherwise it returns childVal. Here, childVal is mainly given priority, which is why there are priority levels such as component > mixins > extends.

The mergeField function will finally assign the result of calling strat to options[key].

The mergeOptions function will finally merge all options, mixins, and extends, and return the options object, and then instantiate vue.

Merge of hook functions

Let's take a look at how hook functions are merged.

function mergeHook(
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
  return childVal
    ? parentVal
      ? (childVal)
      : (childVal)
      ? childVal
      : [childVal]
    : parentVal
}

LIFECYCLE_HOOKS.forEach(hook => {
  strats[hook] = mergeHook
})

Loops the LIFECYCLE_HOOKS array, constantly calls the mergeHook function, and assigns the return value to strats[hook].

export const LIFECYCLE_HOOKS = [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed',
  'activated',
  'deactivated',
  'errorCaptured'
]

LIFECYCLE_HOOKS is the declared vue hook function string.

The mergeHook function returns 3-layer nested ternary expressions.

return childVal
  ? parentVal
    ? (childVal)
    : (childVal)
    ? childVal
    : [childVal]
  : parentVal

The first layer, if childVal is true, returns the second layer of ternary expression, and if false, returns parentVal.

The second layer, if parentVal is true, returns the array merged by parentVal and childVal, and if parentVal is false, returns the third layer ternary expression.

The third layer, if childVal is an array, return childVal, otherwise wrap childVal into an array and return.

new Vue({
  created: [
    function() {
      ('Changzhuan!  ')
    },
    function() {
      ('Ducks!  ')
    }
  ]
})
// => At any time!// => Ducks!

Project Practice

Of course, those who use vue are also indispensable to using element-ui in the project. For example, when using Table tables, it is inevitable to declare some parameters required for tableData, total, pageSize for TableData, and Pagination.

We can write duplicate data and methods in a tableMixin.

export default {
  data() {
    return {
      total: 0,
      pageNo: 1,
      pageSize: 10,
      tableData: [],
      loading: false
    }
  },

  created() {
    ()
  },

  methods: {
    // Pre-declaration to prevent errors    searchData() {},

    handleSizeChange(size) {
       = size
      ()
    },

    handleCurrentChange(page) {
       = page
      ()
    },

    handleSearchData() {
       = 1
      ()
    }
  }
}

When we need to use it, we can directly introduce it:

import tableMixin from './tableMixin'

export default {
  ...
  mixins: [tableMixin],
  methods: {
    searchData() {
      ...
    }
  }
}

We will redeclare the searchData method within the component. A key similar to this method object form. If the key is the same, the key in the component will override the key in tableMixin.

Of course, we can also nest mixins in mixins and declare axiosMixin:

import tableMixin from './tableMixin'

export default {
  mixins: [tableMixin],

  methods: {
    handleFetch(url) {
      const { pageNo, pageSize } = this
       = true

      ({
        method: 'post',
        url,
        data: {
          ...,
          pageNo,
          pageSize
        }
      })
        .then(({ data = [] }) => {
           = data
           = false
        })
        .catch(error => {
           = false
        })
    }
  }
}

Introducing axiosMixin:

import axiosMixin from './axiosMixin'

export default {
  ...
  mixins: [axiosMixin],
  created() {
    ('/user/12345')
  }
}

In axios, we can pre-process the subsequent calls of axios success and error, and have we written a lot of code less?

extend

By the way, extend, similar to mixins, can only pass in one option object, and mixins has a higher priority, which will override extend's key value of the same name.

// If there is a recursive call to mergeOptions to implement attribute copyingconst extendsFrom = 
if (extendsFrom) {
  parent = mergeOptions(parent, extendsFrom, vm)
}
// If there is a recursive call to mergeOptions to implement attribute copyingif () {
  for (let i = 0, l = ; i &lt; l; i++) {
    parent = mergeOptions(parent, [i], vm)
  }
}
// If there is a recursive call to mergeOptions to implement attribute copyingconst extendsFrom = 
if (extendsFrom) {
  parent = mergeOptions(parent, extendsFrom, vm)
}
// If there is a recursive call to mergeOptions to implement attribute copyingif () {
  for (let i = 0, l = ; i &lt; l; i++) {
    parent = mergeOptions(parent, [i], vm)
  }
}

In the mergeOptions function, extends will be copied first, and then mixin will be copied. When calling the mergeField function, the child's key will be preferred.

Although the key of the same name of extends will be overwritten by mixins, extends is preferred.

Summarize

Note the priority of mixins in vue, component > mixins > extends.

We will call mixins a component modularity for the time being. We can flexibly use component modularity, which can extract the repeated code in the component to realize code reuse, and also make our code clearer and greatly improve efficiency.

Of course, there are even more magical operations for mixins waiting for you to explore.

The above is the detailed content of vue using mixins to optimize components. For more information about vue using mixins to optimize components, please pay attention to my other related articles!