2af477397e
I hope that coding results will be better and more consistent
7.3 KiB
7.3 KiB
description, applyTo
| description | applyTo |
|---|---|
| Instructions for writing JavaScript following project conventions and ES6+ best practices | **/*.js,**/*.mjs |
JavaScript Development Instructions
Follow ES6+ best practices and project-specific patterns for JavaScript code.
Module System
ES6 Modules
Use ES6 import/export syntax:
// Named exports
export const API = axios.create({ ... })
export function helper() { ... }
// Default export
export default {
messages: { ... }
}
// Imports
import API from '../utils/api'
import { createI18n } from 'vue-i18n'
API Configuration (utils/api.js)
Standard Axios Instance Pattern
import axios from 'axios'
const API = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8180'
})
// Request interceptor - adds auth token
API.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
// Response interceptor - handles 401
API.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token')
// Optional: redirect to login
}
return Promise.reject(error)
}
)
export default API
Key points:
- Single Axios instance for the entire app
- Auto-adds Authorization header from localStorage
- Auto-handles 401 responses
- Uses Vite environment variables
Pinia Store Pattern (stores/)
Standard Store Structure
import { defineStore } from 'pinia'
import { createI18n } from 'vue-i18n'
import de from '@/locales/de'
import en from '@/locales/en'
export const i18n = createI18n({
legacy: false,
locale: localStorage.getItem('language') || 'de',
fallbackLocale: 'en',
messages: { de, en }
})
export const useLanguageStore = defineStore('language', {
state: () => ({
currentLanguage: localStorage.getItem('language') || 'de'
}),
actions: {
setLanguage(lang) {
this.currentLanguage = lang
i18n.global.locale.value = lang
localStorage.setItem('language', lang)
}
}
})
Locale Files (locales/de, locales/en)
Translation Object Structure
Important: Locale files use .js extension and export objects (not .json):
// locales/de
export default {
char: 'Figur',
menu: {
Datasheet: 'Datenblatt',
Skill: 'Fertigkeiten'
},
export: {
selectTemplate: 'Vorlage wählen',
exportPDF: 'PDF Export',
exporting: 'Exportiere...',
pleaseSelectTemplate: 'Bitte Vorlage auswählen'
}
}
Conventions:
- Nested objects for grouping related translations
- camelCase for keys
- Always add to both
deandenfiles simultaneously - Keep structure identical between languages
Async/Await Patterns
Error Handling
try {
const response = await API.get('/api/endpoint')
return response.data
} catch (error) {
console.error('Operation failed:', error)
throw error // or handle gracefully
}
Multiple Parallel Requests
const [characters, templates] = await Promise.all([
API.get('/api/characters'),
API.get('/api/pdf/templates')
])
Browser APIs
LocalStorage Usage
// Save
localStorage.setItem('token', response.data.token)
localStorage.setItem('language', 'de')
// Retrieve
const token = localStorage.getItem('token')
const lang = localStorage.getItem('language') || 'de'
// Remove
localStorage.removeItem('token')
Blob/File Handling
// Create blob from response
const blob = new Blob([response.data], { type: 'application/pdf' })
const url = window.URL.createObjectURL(blob)
// Open in new window
const pdfWindow = window.open(url, '_blank')
// Clean up after use
setTimeout(() => window.URL.revokeObjectURL(url), 10000)
URL Parameters
// Build query string
const params = new URLSearchParams({
template: templateId,
showUserName: 'true'
})
const url = `/api/export?${params.toString()}`
// Extract from params
const queryParams = Object.fromEntries(params)
Event Handling
Debouncing/Throttling
For search inputs or resize events:
let debounceTimer
function debounce(func, delay = 300) {
return (...args) => {
clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => func(...args), delay)
}
}
// Usage
const search = debounce(async (query) => {
const results = await API.get(`/api/search?q=${query}`)
}, 300)
Cleanup
// Save timer ID for cleanup
this.timer = setTimeout(() => { ... }, 5000)
// Clean up in component lifecycle
beforeUnmount() {
if (this.timer) clearTimeout(this.timer)
}
Array/Object Operations
Array Methods
// Filter
const learned = skills.filter(s => s.Fertigkeitswert > 0)
// Map
const names = characters.map(c => c.name)
// Find
const char = characters.find(c => c.id === 18)
// Some/Every
const hasSkills = character.fertigkeiten.some(f => f.Fertigkeitswert > 0)
Object Destructuring
// Response destructuring
const { data, headers, status } = response
// Props destructuring
const { character, template, showUserName = false } = options
Spread Operator
// Merge objects
const merged = { ...defaults, ...userOptions }
// Copy array
const copy = [...originalArray]
Common Patterns
Loading State Management
export default {
data() {
return {
isLoading: false,
data: null,
error: null
}
},
async created() {
await this.loadData()
},
methods: {
async loadData() {
this.isLoading = true
this.error = null
try {
const response = await API.get('/api/data')
this.data = response.data
} catch (error) {
this.error = error.message
} finally {
this.isLoading = false
}
}
}
}
Form Validation
validateForm() {
if (!this.selectedTemplate) {
alert(this.$t('export.pleaseSelectTemplate'))
return false
}
return true
}
async submit() {
if (!this.validateForm()) return
// Proceed with submission
}
Best Practices
- Use
constby default,letwhen reassignment needed, nevervar - Prefer arrow functions for callbacks and short functions
- Use template literals for string interpolation
- Handle promise rejections with try/catch or .catch()
- Clean up timers and intervals in component lifecycle
- Use optional chaining
?.for nested properties - Use nullish coalescing
??instead of||for default values - Keep functions small and single-purpose
- Document complex logic with comments
- Use meaningful variable names - avoid single letters except loops
Anti-Patterns to Avoid
❌ Don't use var - use const or let
❌ Don't ignore promise rejections
❌ Don't mutate function parameters
❌ Don't create memory leaks (clean up listeners, timers)
❌ Don't use eval() or new Function()
❌ Don't mix callbacks and promises
❌ Don't forget to handle edge cases (null, undefined, empty arrays)
❌ Don't use == - always use === for comparisons
Environment Variables (Vite)
Accessing Variables
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8180'
const isDev = import.meta.env.DEV
const isProd = import.meta.env.PROD
Convention: Prefix all custom variables with VITE_