SoFunction
Updated on 2025-03-03

Vue3 Combination Function Composable Best Practical Battle

introduction

As of now, Composable (combination function) should be the best way to organize business logic in VUE 3 applications.

It allows us to extract and reuse some small pieces of general logic, making our code easier to write, read and maintain.

Since this way of writing VUE code is relatively new, you might be wondering what are the best practices for writing combinatorial functions? This series of tutorials can be used as a reference guide for you and your team during the combined development process.

We will cover the following:

  • 1. How to make your combination more configurable through option object parameters; 👈Theme of this article
  • 2. Use ref and unref to make our parameters more flexible;
  • 3. How to make your return value more useful;
  • 4. Why can you make your combinatorial functions more powerful by starting from the interface definition?
  • 5. How to use asynchronous code without "waiting" - making your code easier to understand;

First, however, we need to make sure that our understanding of the combined functions is consistent. Let's take a moment to explain what a combinatorial function is.

What is a Composable-combination function?

According to official documentation, in the concept of Vue application, "combination function" is a function that uses the Vue combination API to encapsulate and reuse stateful logic.
This means that any stateful logic, which uses responsive processing, can be converted into a combined function. This is still somewhat different from our usual public method of removing packaging. The public method we encapsulate is often stateless: it returns the desired output immediately after receiving some input. Combination functions are often associated with state logic.

Let's see what is given by the officialuseMouseThis combination function:

import { ref, onMounted, onUnmounted } from 'vue'
// According to convention, the name of the combined function starts with "use"export function useMouse() {
  // The state encapsulated and managed by a combined function  const x = ref(0)
  const y = ref(0)
  // Combination functions can change their state at any time.  function update(event) {
     = 
     = 
  }
  // A combination function can also be attached to the life cycle of the component  // To start and uninstall side effects  onMounted(() => ('mousemove', update))
  onUnmounted(() => ('mousemove', update))
  //Expose managed states through return values  return { x, y }
}

We define the state asrefs,We update this status when the mouse moves. Returnxandy, we can use them in any component, and we can even use multiple combinatorial functions in nested.

When we use it in a component

<template>
  X: {{ x }} Y: {{ y }}
</template>
<script setup>
  import { useMouse } from './useMouse';
  const { x, y } = useMouse();
</script>

As you can see, by usinguseMouseWe can easily reuse this logic. With just a little code, we can get the mouse coordinate state in the component.

Now that we have the same understanding of combinatorial functions, let's take a look at the first method that can help us write better combinatorial functions.

Option object parameters

Most combinatorial functions will have one or two necessary parameters, and then there is a series of optional parameters to help with some additional configuration. When configuring a combination function, we can place a series of optional configurations into an option object parameter instead of a long list of parameters.

// Use option object parameter formconst title = useTitle('A new title', { titleTemplate: '&gt;&gt; %s &lt;&lt;' });
// Title is now "&gt;&gt; A new title &lt;&lt;"
// Use multi-parameter formconst title = useTitle('A new title', '&gt;&gt; %s &lt;&lt;');
// Title is now "&gt;&gt; A new title &lt;&lt;"

The form of option object parameters can bring us some convenience:

  • First of all, we don't have to remember the correct order of parameters, especially when there are many parameters. Although we can now avoid this kind of problem through Typescript and editor prompts, there are still differences in this way. With Javascript objects, the order of parameters is less important. (Of course, this also requires that our function definition needs to be clear and clear, we will discuss it later)
  • Secondly, the code is more readable because we know what the option does.
  • Third, the code is more extensible, and it is much easier to add new options in the future. This applies to both adding new options to the combinatorial function itself and for parameter passing in nested usage.

Therefore, using object parameters is more friendly, but how can we achieve it?

Implementation in Combination Functions

Now let's see how to use option object parameters in a combinatorial function. Let's give it touseMouseMake some extensions:

export function useMouse(options) {
  const {
    asArray = false,
    throttle = false,
  } = options;
  // ...
};

useMouseThere is no need to pass parameters, so we just add one to it.optionsParameters for some additional configuration. Through deconstruction, we can access all the optional parameters and set default values ​​for each parameter, which avoids the situation where there is no optional parameters passed in when calling without additional configuration.

Now let's see how the two combined functions on VueUse use this pattern.VueUseIt is a common tool set for combinatorial functions serving Vue3. Its original intention is to make all JS APIs that did not originally support responsiveness, eliminating programmers' writing related code themselves.

Let's take a look firstuseTitle, and then take a lookuseRefHistoryHow it is implemented.

Example - useTitle

useTitleThe function is very simple, it is used to update the page title.

const title = useTitle('Initial Page Title');
// Title: "Initial Page Title"
 = 'New Page Title';
// Title: "New Page Title"

It also has several selection parameters to facilitate additional flexibility. We can pass intitleTemplateAs a template, and throughobserveTo call its settings observable (internal implementation through MutationObserver):

const title = useTitle('Initial Page Title', {
  titleTemplate: '>> %s <<',
  observe: true,
});
// Title: ">> Initial Page Title <<"
 = 'New Page Title';
// Title: ">> New Page Title <<"

When we look at itSource codeYou can see the following processing when

export function useTitle(newTitle, options) {
  const {
    document = defaultDocument,
    observe = false,
    titleTemplate = '%s',
  } = options;
  // ...
}

useTitleContains a must-pass parameter and an optional parameter object. As we described above, it is implemented entirely in this pattern.
Next, let's take a look at how a more complex combo function uses the option object pattern.

Example - useRefHistory

useRefHistoryIt can help us track all changes in a responsive variable, allowing us to easily perform undo and restore operations.

// Set up the count ref and track it
const count = ref(0);
const { undo } = useRefHistory(count);
// Increment the count
++;
// Log out the count, undo, and log again
(); // 1
undo();
(); // 0

It supports setting up many different configuration options

{
  deep: false,
  flush: 'pre',
  capacity: -1,
  clone: false,
  // ...
}

If you want to know the complete list of these options and their corresponding functions, you can check it outRelated Documents, I won't repeat it here.

We can pass the option object as a second parameter to further configure the behavior of this combined function, the same as in our previous example:

export function useRefHistory(source, options) {
  const {
    deep = false,
    flush = 'pre',
    eventFilter,
  } = options;
  // ...
}

We can see that only a part of the parameter values ​​are deconstructed inside it, becauseuseRefHistoryInternal dependencyuseManualHistoryThis combination function, other option parameters will be passed touseManualHistorywhen expanding and merge.

// ...
const manualHistory = useManualRefHistory(
  source,
  {
    // Pass along the options object to another composable
    ...options,
    clone:  || deep,
    setSource,
  },
);
// ...

This is also consistent with what we mentioned earlier: Combination functions can be used in nesting.

summary

This article is the first part of "Combined Function Best Practice". We examined how to improve the flexibility of combined functions through option object parameters. There is no need to worry about the problem caused by the incorrect parameter order, and the configuration items can be added and expanded flexibly. We not only studied this pattern itself, we also used theuseTitleanduseRefHistoryI learned how to implement this pattern. They are slightly different, but this pattern itself is very simple, and we can do it through it with limited.

In the next article, we will introduce how to make our parameters more flexible through ref and unref

// Works if we give it a ref we already have
const countRef = ref(2);
useCount(countRef);
// Also works if we give it just a number
const countRef = useRef(2);

This adds flexibility and allows us to use our combination in more cases in the application.

The above is the detailed content of the best practical application of Vue3 Composable. For more information about Vue3 Composable, please follow my other related articles!