ホーム

Blog

Best Practices in Vue 3

Vue.js 3 is one of the most elegant and efficient JavaScript frameworks for building modern web applications

更新日:2025/10/23

Best Practices in Vue 3

Introduction

Vue.js 3 is one of the most elegant and efficient JavaScript frameworks for building modern web applications. Its Composition API, reactivity system, and flexibility make it a top choice among developers worldwide. However, like any powerful tool, achieving high-quality, maintainable, and performant applications requires understanding and following best practices. This guide explores the most effective strategies and techniques for developing with Vue 3, including examples and explanations to help you write professional, scalable code.

1. Project Structure and Organization

A well-organized project structure improves scalability, readability, and team collaboration. Vue 3 allows flexible setups, but consistency is key. Always follow a clear convention for your directories.

Use a modular and feature-based structure rather than grouping only by file type.


src/
├─ api/            // API request functions
├─ assets/         // Images, fonts, and static files
├─ components/     // Reusable UI components
├─ composables/    // Reusable logic using Composition API
├─ layouts/        // Layout components
├─ pages/          // View-level components (for routing)
├─ store/          // Pinia stores
├─ router/         // Vue Router setup
├─ utils/          // Utility functions
└─ App.vue

1.2 Naming Conventions

  • Use PascalCase for components (e.g., UserCard.vue).
  • Use camelCase for composables and helpers (e.g., useAuth.js).
  • Use kebab-case for route names (e.g., user-profile).

2. Composition API Best Practices

The Composition API in Vue 3 provides a flexible and scalable way to manage logic. It promotes better code organization and reusability. However, misusing it can lead to unreadable code.

2.1 Use Composables for Reusable Logic

Whenever you find logic that could be reused across multiple components, extract it into a composable. This promotes DRY (Don’t Repeat Yourself) principles.


// composables/useFetch.js
import { ref } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)
  const loading = ref(false)

  const fetchData = async () => {
    loading.value = true
    try {
      const res = await fetch(url)
      data.value = await res.json()
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }

  return { data, error, loading, fetchData }
}

2.2 Keep Composables Focused

Each composable should handle a single, well-defined purpose. Avoid making “god” composables that handle multiple unrelated tasks.

2.3 Avoid Overusing watch

Overuse of watchers can complicate reactivity and cause performance issues. Prefer computed properties when possible, as they are cached and declarative.


// ❌ Bad: Using watch for derived state
watch(() => count.value, (newCount) => {
  double.value = newCount * 2
})

// ✅ Good: Use computed instead
const double = computed(() => count.value * 2)

3. Component Design Principles

Components are the backbone of Vue applications. Proper component design ensures reusability, readability, and performance.

3.1 Keep Components Small and Focused

Each component should ideally do one thing well. If a component grows too large, split it into smaller subcomponents.

3.2 Use Props and Emits Correctly

Props and emits define the public API of your component. Be explicit about what props your component accepts and what events it emits.


<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  label: String,
  modelValue: String
})

const emit = defineEmits(['update:modelValue'])
</script>

<template>
  <label>{{ label }}</label>
  <input
    :value="modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  />
</template>

3.3 Avoid Deep Prop Drilling

If you find yourself passing props through multiple layers of components, consider using the Context API or a store (e.g., Pinia) to share state efficiently.

3.4 Use Slots for Flexibility

Slots allow components to remain flexible while providing structure.


<template>
  <div class="card">
    <header>
      <slot name="header">Default Header</slot>
    </header>
    <main>
      <slot></slot>
    </main>
  </div>
</template>

4. State Management Best Practices

Vue 3 officially recommends Pinia for state management. It’s type-safe, modular, and more intuitive than Vuex.

4.1 Define Stores per Domain

Create one store per domain (e.g., user, product, settings) rather than one large store.


// stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    isLoggedIn: false
  }),
  actions: {
    login(username) {
      this.name = username
      this.isLoggedIn = true
    },
    logout() {
      this.name = ''
      this.isLoggedIn = false
    }
  }
})

4.2 Persist Important State

For user sessions or preferences, persist state using localStorage or plugins.


import { useUserStore } from '@/stores/user'

const userStore = useUserStore()
userStore.$subscribe((mutation, state) => {
  localStorage.setItem('user', JSON.stringify(state))
})

4.3 Avoid Storing Derived State

Do not store data that can be computed from existing state. Use computed properties or getters instead.

5. Performance Optimization

Performance issues can easily appear in large applications. Vue 3 offers many ways to optimize rendering and reactivity.

5.1 Use v-once and v-memo

If an element or component does not change, use v-once to render it once.


<h1 v-once>Static Title</h1>

5.2 Avoid Unnecessary Reactive Dependencies

Reactivity can be expensive if used carelessly. Avoid making entire objects reactive when you only need a single value.


// ❌ Avoid this
const state = reactive({ count: 0 })

// ✅ Better
const count = ref(0)

5.3 Use Lazy Loading with Vue Router

Lazy load components to improve initial load time.


const routes = [
  {
    path: '/about',
    component: () => import('@/pages/About.vue')
  }
]

5.4 Use Keyed v-for

Always use a unique key for list rendering to help Vue efficiently track changes.


<li v-for="item in items" :key="item.id">{{ item.name }}</li>

6. Template and Styling Best Practices

Clean templates and organized styles make your Vue components easy to read and maintain.

6.1 Use Scoped Styles

Use <style scoped> to prevent CSS conflicts between components.


<style scoped>
.button {
  background-color: #42b983;
}
</style>

6.2 Prefer CSS Modules or Utility Frameworks

For larger projects, use CSS modules or utility frameworks like Tailwind CSS for better maintainability.

6.3 Keep Templates Simple

Avoid putting complex logic inside templates. Use computed properties and methods instead.


// ❌ Bad
<div>{{ user.age >= 18 ? 'Adult' : 'Minor' }}</div>

// ✅ Good
<div>{{ userStatus }}</div>

<script setup>
const userStatus = computed(() => user.age >= 18 ? 'Adult' : 'Minor')
</script>

7. Testing and Debugging Best Practices

Testing ensures that your Vue components work as expected and remain stable during updates.

7.1 Use Vue Test Utils and Jest

Vue Test Utils is the official unit testing library for Vue components. Combine it with Jest for comprehensive testing.


// Example: Button.spec.js
import { mount } from '@vue/test-utils'
import Button from '@/components/Button.vue'

test('renders label correctly', () => {
  const wrapper = mount(Button, {
    props: { label: 'Click Me' }
  })
  expect(wrapper.text()).toContain('Click Me')
})

7.2 Use DevTools for Debugging

Install Vue DevTools to inspect component trees, props, events, and performance in real-time.

7.3 Log Responsibly

Avoid leaving console logs in production. Use proper error handling or logging tools like Sentry.

8. Error Handling and Validation

Graceful error handling is critical for a robust user experience.

8.1 Use try...catch in Async Functions


try {
  await api.getUser()
} catch (error) {
  console.error('Failed to fetch user', error)
}

8.2 Global Error Boundaries

You can use the errorCaptured lifecycle hook or global handlers for unhandled errors.


onErrorCaptured((err, instance, info) => {
  console.error('Error:', err, info)
  return false
})

8.3 Input Validation

Use libraries like yup or vee-validate for consistent form validation.

9. Code Quality and Maintainability

Maintainability ensures your project remains easy to evolve over time.

9.1 Use ESLint and Prettier

Linting enforces consistency across your codebase. Use eslint-plugin-vue for Vue-specific rules.

9.2 Follow Vue Style Guide

Follow the official Vue Style Guide for naming conventions, template structure, and recommended practices.

9.3 Document Your Components

Document component props, events, and usage examples to help your team and future maintainers.

10. Deployment and Production Tips

After development, optimizing for production is crucial for performance and reliability.

10.1 Use Environment Variables

Keep API keys and environment-specific configurations outside of your source code.


VITE_API_URL=https://api.example.com

10.2 Enable Code Splitting

Vite and Vue automatically support code splitting, but be sure to lazy-load large dependencies.

10.3 Optimize Images and Assets

Use modern image formats (WebP, AVIF) and lazy loading for images to reduce bandwidth.

10.4 Monitor Performance

Use tools like Lighthouse or Sentry Performance to track load times and errors in production.

Conclusion

Building scalable, maintainable, and performant Vue 3 applications requires more than just syntax knowledge. It involves adhering to consistent architecture, following composition patterns, optimizing rendering, and writing clean, testable code. By following the best practices outlined above — from composables and component structure to state management and deployment — you ensure your Vue 3 projects remain stable, readable, and efficient. Vue’s ecosystem is constantly evolving, so keep learning and refining your practices to stay ahead in modern web development.

オフショア開発のご紹介資料

氏名 *

会社名 *

部署 *

電話番号 *

メールアドレス *

資料を選ぶ

送信前に当サイトの、
プライバシーポリシーをご確認ください。