Mixin

This is the 17th day of my participation in the August Challenge

Sometimes the same code logic exists between components, and we want to extract the same code logic.

One way supported in both Vue2 and Vue3 is to use mixins to do this:

  • Mixin provides a very flexible way to comeDistribute reusable functionality in Vue components
  • A Mixin object can containAny component options
  • When a component uses a Mixin object,The options of all Mixin objects are mixed into the options of the component itselfIn the

Mixin definer -- /mixins/ foomixn.js

export default {
  data() {
    return {
      message: 'something in fooMixn'}},methods: {
    foo() {
      console.log('foo')}}}Copy the code

Mixin user -- app.vue

<template>
  <div>
    <h2>{{ message }}</h2>
    <button @click="foo">click me</button>
  </div>
</template>

<script>
import fooMixin from './mixins/fooMixin'

export default {
  name: 'App'.// A mixin is essentially an object
  // A component can be mixed with multiple components, so the value of a mixins is an object
  mixins: [fooMixin]
}
</script>
Copy the code

Merging rules

  1. If it is the return value object of the data function

    • Return value objects are merged by default
    • If the properties of the data return value object conflict, thenRetain the data of the component itself
  2. How to lifecycle hook functions

    • The lifecycle hook functions will be merged into the array and will be called

    • Execute the logic in the Mixin first, and the logic in the execution component for the lifecycle hooks

  3. Options that are values for objects, such as Methods, Components, and directives, will be combined into the same object

    • For example, if both have the methods option and both have methods defined, they will all work
    • But if the object’sThe key is the sameSo what wouldTakes the key-value pair of the component object

With global

If there are certain options in the component that all components need to have, then we can use global mixins:

  • Global mixins can be registered using the application app’s method Mixin
  • Once registered, globally mixed options affect each component
  • The global mixin lifecycle takes precedence because it is inserted into the array first
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.mixin({
  created() {
    console.log('Global interfuse')
  }
})

app.mount('#app')
Copy the code

extends

Another property that is very similar to Mixins is extends

Based on the component

<template>
  <div>
    <! If you use inheritance, you can only inherit the contents of script, but not the contents of template.
    <h1>Base</h1>
  </div>
</template>

<script>
export default {
  name: 'BaseComponent'.data() {
    return {
      msg: 'base msg'}},methods: {
    foo() {
      console.log('foo')}}}</script>
Copy the code

Using inherited components

<template>
  <div>
    <h2>{{ msg }}</h2>
    <button @click="foo">click ms</button>
  </div>
</template>

<script>
import BaseComponent from './BaseComponent.vue'

export default {
  name: 'App'.// Extend BaseComponent
  // Components can only have single inheritance
  extends: BaseComponent
}
</script>
Copy the code

Composition API

In Vue2, we write components using the Options API:

One feature of the Options API is that the corresponding function modules are written in the corresponding properties

For example, data defines data, methods in methods, computed properties in computed, watches for property changes, and life cycle hooks

But there’s a big downside to this:

  • When we implement a feature, the code logic for that feature is split into properties

  • As our components become larger and more complex, the list of logical concerns grows, and the logic of the same function is broken down very thinly

  • Especially for those who didn’t write the components in the first place, the component’s code is hard to read and understand (other people who read the components)

The Vue Composition API (VCA) is designed to solve these problems

Composition apis can gather together code related to the same logical concern

Conclusion: The code written by option API is relatively scattered, which is not conducive to later maintenance and extraction (code reuse).

The Composition API adds together code from the Option API, increasing readability and reusability

At the same time, it is convenient to pull out the code, and the code can be pulled out into a hook function to improve the reusability of the code

Setup

To start using the Composition API, we need a place where we can actually use it (code)

In a Vue component, this location is the setup function

parameter

The setup function, which takes two arguments:

The first parameter :props:

  • Properties passed from the parent component are put into the props object, which we can get directly from the props parameter if we need to use it in setup

  • For defining the types of props, we use the same rule as before, in the props option

  • And it is still possible to use properties in the template, such as message

  • If we want to use props in the setup function, we cannot get this

  • In the setup function, there is no this keyword

  • Because props is passed directly as a parameter to the setup function, we can use it as a parameter

The second argument :context

The other argument is context, which we also call a SetupContext, which contains three properties:

  • Attrs: All non-prop attributes
  • Slots: Slots passed by the parent component
  • Emit: We use emit when we need to emit events from within our component (since we cannot access this, we cannot emit events from this.$emit)
<template>
  <div>
    <h2>{{ message }}</h2>
  </div>
</template>

<script>
export default {
  name: 'Home'.props: {
    message: {
      type: String.required: true}},setup(props, { emit }) {
    // Get the data from the parent component
    console.log(props)

    // Similar to the this.$emit method in vue2
    emit('homeClick')}}</script>
Copy the code
The return value

Since setup is a function, it can also have a return value

The return value of setup can be used in the template, which means that we can use the return value of setup instead of the data option

<template>
  <div>
    <h2>{{ msg }}</h2>
  </div>
</template>

<script>
export default {
  name: 'Home'.setup() {
    return {
      msg: 'message'}}}</script>
Copy the code

But the return value of the setup function, by default, is just a normal variable, and Vue does not track its changes to cause responsive actions on the interface

<template>
  <div>
    <button @click="increment">{{ count }}</button>
  </div>
</template>

<script>
export default {
  name: 'Home'.setup() {
    let count = 0

    // When the button is clicked, the setup count changes, but the interface count does not notice the change
    const increment = () = > count++

    return  {
      count,
      increment
    }
  }
}
</script>
Copy the code

Reactive

When we process our data using reactive functions, dependency collection takes place when the data is used again

When the data changes, all the collected dependencies are used to perform corresponding reactive actions (such as updating the interface)

In fact, the data option we write is also internally handed to reactive functions to program reactive objects

<template>
  <div>
    <button @click="increment">{{ state.count }}</button>
  </div>
</template>

<script>
import { reactive } from 'vue'

export default {
  name: 'Home'.setup() {
    const state = reactive({
      count: 0
    })

    const increment = () = > state.count++

    return  {
      state,
      increment
    }
  }
}
</script>
Copy the code

Ref

The Reactive API has restrictions on the type passed in, requiring that it be an object or array type

If we pass a basic data type (String, Number, Boolean) a warning is raised

At this point Vue3 gives us another API: the REF API

  • Ref returns a mutable reactive object that maintains its internal value as a reactive reference, which is where the ref name comes from
  • Its internal values are maintained in the ref’s value attribute
<template>
  <div>
    <! Vue will automatically unpack the value of ref in the template, so we don't need to use ref. Value in the template.
    <button @click="increment">{{ count }}</button>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  name: 'Home'.setup() {
    const count = ref(0)

    // Inside the setup function, it is still a ref reference, so we still need to use the ref.value method to operate on it
    const increment = () = > count.value++

    return  {
      count,
      increment
    }
  }
}
</script>
Copy the code
<template>
  <div>
    <! Value = info; value = info;
    {{ info.name }} --- {{ info.age }}
    <button @click="changeAge">change age</button>
  </div>
</template>

<script>
  import { ref } from 'vue'

  export default {
    name: 'Home'.setup() {
      // Ref arguments can also be object types - not recommended - subsequent destructions cannot be performed using toRefs or toRef
      const info = ref({ name: 'Klaus'.age: 23 })

      // The value is still used by the value attribute
      const changeAge = () = > info.value.age++

      return {
        info,
        changeAge
      }
    }
  }
</script>
Copy the code

Tips: Don’t use Reactive for code that can be implemented using REF

Because the reactive data implemented using the REF function is independent, it can be integrated with business logic

However, the reactive data declared by Reactive should be those that are strongly interconnected

Reactive integration of poorly connected data increases code coupling and is not conducive to code splitting and reuse

// Name and age are strongly correlated, but they have no relation to count and MSG, so they should not be placed in a Reactive object
const state = reactive({
  name: 'Klaus'.age: 23.count: 0.msg: 'Hello World'
})

// The following format is recommended
// This makes it easy to integrate the corresponding data into the corresponding hooks functions
const info = reactive({
  name: 'Klaus'.age: 23
})

const count = ref(0)
const msg = ref('Hello World')
Copy the code

readonly

We can get a reactive object by reactive or ref, but in some cases we pass this in somewhere else

How do you prevent reactive objects that want to be used in another place (component) but cannot be modified?

Vue3 provides us with a method for readonly

Readonly returns a read-only Proxy for the native object (that is, it is still a Proxy, but the Proxy’s set method has been hijacked and cannot be modified)

// Common objects
setup() {
  const counter = {
    count: 0
  }

  const readOnlyCounter = readonly(counter)

  const change = () = > readOnlyCounter.count++ // error

  return {
    counter,
    readOnlyCounter,
    change
  }
}
Copy the code
/ / reactive object
const counter = reactive({
  count: 0
})

const readOnlyCounter = readonly(counter)

const change = () = > readOnlyCounter.count++ // error

return {
  counter,
  readOnlyCounter,
  change
}
Copy the code
/ / ref object
const counter = ref(0)

const readOnlyCounter = readonly(counter)

const change = () = > readOnlyCounter.value++ // error

return {
  counter,
  readOnlyCounter,
  change
}
Copy the code

The following rules apply when using readOnly:

  • Objects returned by readonly are not allowed to be modified
  • However, original objects processed by readonly are allowed to be modified
  • Essentially, the setter method for the object returned by ReadOnly has been hijacked

At this point, we can use readOnly when we are passing data to other components and want them to use what we are passing, but do not allow them to modify it