Search code examples
vue.jsvuejs3vue-composition-api

Vue 3 separation of logic with setup notation and composition API


In Vue 3 (composition API with setup method), in order to separate logic, you've to create other methods outside of your setup method (which are named composition functions) :

<script>
export default {
   setup(){
       return { ...useSearch(), useAuth() }
   }
}
function useSearch(){
    // creates reactive data, computed, etc and returns it
}
function useAuth(){
   // creates reactive data, computed, etc and returns it
}
</script>

But if you're using the syntactic sugar setup inside the definition of your script (<script setup>), how one is supposed to separate the logic ? Should one just put everything in the unique script block without clear delimitations ? Should one create a new script block for every logical aspect ? I'm looking for the best practice to do that.


Solution

  • As a rule of thumb, if a piece of code has a chance to become a composable or global store (Pinia setup functions are written the same way and can be easily refactored in both ways), write it as a composable. Otherwise it can be left as is.

    The name useAuth suggests like it could be reused between multiple components, and is therefore suitable to be a composable.

    In this case loose coupling between the composables without extracting them to a separate module would make the implementation more complex (dependency injections, etc). This won't improve the testability because a component is tested as a single unit, local functions cannot be effectively mocked and spied.

    There is no significant difference between script and script setup in this regard, except that the result of a composable should be assigned to a variable in order to be exposed to a template, which is good because doing this instead of ...useSearch() allows to avoid refactoring when the result needs to be used inside a script.

    It would be:

    <script setup>
    const search = useSearch();
    function useSearch() {...}
    ...
    

    Which would roughly result in:

    <script>
    ...
    setup() {
      const search = useSearch();
      function useSearch() {...}
      return {
        search,
      ...
    

    This results in a negligible decrease in performance that falls into the premature optimization category and can be ignored. This also makes the composables prone to accidental dependency on local variables, which would require refactoring to extract them to a separate module if necessary; this would naturally be avoided if they were defined in a parent scope.