Join the Newsletter!

Exclusive content & updates. No spam.

Skip to content

The Computed Inlining Refactoring Pattern in Vue

Published: at 

TLDR

Improve your Vue component performance and readability by applying the Computed Inlining pattern - a technique inspired by Martin Fowler’s Inline Function pattern. By consolidating helper functions directly into computed properties, you can reduce unnecessary abstractions and function calls, making your code more straightforward and efficient.

Introduction

Vue 3’s reactivity system is powered by computed properties that efficiently update only when their dependencies change. But sometimes we overcomplicate our components by creating too many small helper functions that only serve a single computed property. This creates unnecessary indirection and can make code harder to follow.

The Computed Inlining pattern addresses this problem by consolidating these helper functions directly into the computed properties that use them. This pattern is the inverse of Martin Fowler’s Extract Function pattern and is particularly powerful in the context of Vue’s reactive system.

Understanding Inline Function

This pattern comes from Martin Fowler’s Refactoring catalog, where he describes it as a way to simplify code by removing unnecessary function calls when the function body is just as clear as its name. You can see his original pattern here: refactoring.com/catalog/inlineFunction.html

Here’s his example:

function getRating(driver) {
  return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}

function moreThanFiveLateDeliveries(driver) {
  return driver.numberOfLateDeliveries > 5;
}

After applying the Inline Function pattern:

function getRating(driver) {
  return (driver.numberOfLateDeliveries > 5) ? 2 : 1;
}

The code becomes more direct and eliminates an unnecessary function call, while maintaining readability.

Bringing Inline Function to Vue Computed Properties

In Vue components, we often create helper functions that are only used once inside a computed property. While these can improve readability in complex cases, they can also add unnecessary layers of abstraction when the logic is simple.

Let’s look at how this pattern applies specifically to computed properties in Vue.

Before Refactoring

Here’s how a Vue component might look before applying Computed Inlining:

// src/components/OrderSummary.vue
<script setup lang="ts">
import { computed, ref, watch } from 'vue'

interface OrderItem {
  id: number
  quantity: number
  unitPrice: number
  isDiscounted: boolean
}

const orderItems = ref<OrderItem[]>([
  { id: 1, quantity: 2, unitPrice: 100, isDiscounted: true },
  { id: 2, quantity: 1, unitPrice: 50, isDiscounted: false }
])

const taxRate = ref(0.1)
const discountRate = ref(0.15)
const shippingCost = ref(15)
const freeShippingThreshold = ref(200)

// Helper function to calculate item total
function calculateItemTotal(item: OrderItem): number {
  if (item.isDiscounted) {
    return item.quantity * item.unitPrice * (1 - discountRate.value)
  }
  return item.quantity * item.unitPrice
}

// Helper function to sum all items
function calculateSubtotal(): number {
  return orderItems.value.reduce((sum, item) => {
    return sum + calculateItemTotal(item)
  }, 0)
}

// Helper function to determine shipping
function getShippingCost(subtotal: number): number {
  return subtotal > freeShippingThreshold.value ? 0 : shippingCost.value
}

// Computed property for subtotal
const subtotal = computed(() => {
  return calculateSubtotal()
})

// Computed property for tax
const tax = computed(() => {
  return subtotal.value * taxRate.value
})

// Watch for changes to update final total
const finalTotal = ref(0)
watch(
  [subtotal, tax],
  ([newSubtotal, newTax]) => {
    const shipping = getShippingCost(newSubtotal)
    finalTotal.value = newSubtotal + newTax + shipping
  },
  { immediate: true }
)
</script>

The component works but has several issues:

After Refactoring with Computed Inlining

Now let’s apply Computed Inlining to simplify the code:

// src/components/OrderSummary.vue
<script setup lang="ts">
import { computed, ref } from 'vue'

interface OrderItem {
  id: number
  quantity: number
  unitPrice: number
  isDiscounted: boolean
}

const orderItems = ref<OrderItem[]>([
  { id: 1, quantity: 2, unitPrice: 100, isDiscounted: true },
  { id: 2, quantity: 1, unitPrice: 50, isDiscounted: false }
])

const taxRate = ref(0.1)
const discountRate = ref(0.15)
const shippingCost = ref(15)
const freeShippingThreshold = ref(200)

const orderTotal = computed(() => {
  // Calculate subtotal with inline discount logic
  const subtotal = orderItems.value.reduce((sum, item) => {
    const itemTotal = item.isDiscounted
      ? item.quantity * item.unitPrice * (1 - discountRate.value)
      : item.quantity * item.unitPrice
    return sum + itemTotal
  }, 0)

  // Calculate tax
  const tax = subtotal * taxRate.value

  // Determine shipping cost inline
  const shipping = subtotal > freeShippingThreshold.value ? 0 : shippingCost.value

  return subtotal + tax + shipping
})
</script>

The refactored version:

Best Practices

When to Use Computed Inlining

When to Avoid Computed Inlining

Conclusion

The Computed Inlining pattern in Vue is a practical application of Martin Fowler’s Inline Function refactoring technique. It helps streamline your reactive code by:

While not appropriate for every situation, Computed Inlining is a valuable tool in your Vue refactoring toolkit, especially when optimizing components with many small helper functions.

Try applying Computed Inlining in your next Vue component refactoring, and see how it can make your code both simpler and more efficient.

References

Stay Updated!

Subscribe to my newsletter for more TypeScript, Vue, and web dev insights directly in your inbox.

  • Background information about the articles
  • Weekly Summary of all the interesting blog posts that I read
  • Small tips and trick
Subscribe Now

Most Related Posts