8

What would be preferred way to register functions in Vue Component. I tend to register in component only methods that are required by view (or require direct component data access) and other functions that are not required by view outside vue component. The assumption is that utilityFunction() and utilityFunctionTwo() are currently used only inside this component.

This is an example:

<template>
    <button @click="bar"></button>
    <button @click="foo"></button>
    <button @click="baz"></button>
</template>

<script>
  export default {
      name: "SampleComponent",
      data() {
          return {
              someVariable: "ABC",
              otherVariable: 123
          }
      },
      methods: {
          foo() {
              //some logic
              utilityFunction(this.someVariable);
              //other logic
          },
          bar() {
              //some logic
              utilityFunction(this.someVariable);
              utilityFunctionTwo(this.otherVariable);
              //some other logic
          },
          baz() {
              //some logic
              utilityFunctionTwo(this.someVariable);
              //some other logic
          }
      }
  }
  function utilityFunction(arg){
      //do something
  }
  function utilityFunctionTwo(arg){
      //do something
  }
</script>

Arguments may be different, utility functions can be pure and return something or mutate argument. There can be a lot of different scenarios. But I hope you have got the point.

The other approach to do it is to add those functions as methods to your component. Like this:

<template>
    <button @click="bar"></button>
    <button @click="foo"></button>
    <button @click="baz"></button>
</template>

<script>
    export default {
        name: "SampleComponent",
        data() {
            return {
                someVariable: "ABC",
                otherVariable: 123
            }
        },
        methods: {
            foo() {
                //some logic
                this.utilityFunction();
                //other logic
            },
            bar() {
                //some logic
                this.utilityFunction();
                this.utilityFunctionTwo(this.otherVariable);
                //some other logic
            },
            baz() {
                //some logic
                this.utilityFunctionTwo(this.someVariable);
                //some other logic
            },
            utilityFunction() {
                //do something
                console.log(this.someVariable)
                //other stuff
            },
            utilityFunctionTwo(arg) {
                //do something
            }
        }
    }
</script>

In this approach you sometimes don't need to pass argument to method, as it have access to components data object.

I slightly prefer first approach due reasons:

  1. I have short list of methods used by template or required by component (for some reason). Sometimes this list can get quite long if all methods are put there.
  2. If function will be usable elsewhere in the future I will able to easily add it to some .js file and import it in other components.
  3. Functions does not have access to components scope if they don't need to
  4. It saves me from typing this keyword ;-) And sometimes might be useful if used inside lambda.

I am not sure if this is a matter of opinion and you can prefer one over the other approach purely by your personal preferences or are they any objective reasons that you should prefer one over the other, like (but not limited to) performance of component or principles of software design that are broken by one solution.

Zychoo
  • 625
  • 2
  • 8
  • 25

1 Answers1

6

The difference is that utilityFunction and utilityFunctionTwo helper functions are untestable. They can't be accessed directly. They couldn't be mocked, even if they were:

  export function utilityFunction(arg){
      //do something
  }

  export function utilityFunctionTwo(arg){
      //do something
  }

As explained here, due to how modules work, it's possible to spy or mock a function only if it was exported from a module and is used in another module.

In order to be fully testable or reusable, utilityFunction and utilityFunctionTwo should be moved to another module.

Since they are used privately by a component and aren't reused, a reasonable alternative is to make them private methods, this can be designated with underscore notation:

    methods: {
        ...
        _utilityFunction() {
            //do something
        },
        _utilityFunctionTwo(arg) {
            //do something
        }
    }

A downside is that methods cannot be automatically removed as dead code in case they aren't used, but regular functions can.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks for that answer. The problem with testing those methods is not that big. What I am really up to is to test `foo` `bar` and `baz` and their effect. Since they are my main concern. In future I might find another solution to get rid of utility methods and don't need to test them without main ones. This approach might be taken straight from unit testing in Java which is my primary background. You don't test private implementations, you test methods that rely on them. – Zychoo Sep 25 '19 at 11:45
  • 1
    *You don't test private implementations, you test methods that rely on them* - that's a matter of testing methodology and depends on specific case. Testing the behaviour instead of implementation blurs the line between unit and functional tests. You likely wouldn't run into the same dilemma in Java because methods are everywhere; even if they are private, they can be mocked or spied when needed. Any way, testing is the only difference I see here. – Estus Flask Sep 25 '19 at 14:25