# API Integration Standards for Vite
This document outlines the coding standards and best practices for integrating APIs into Vite applications. These standards are designed to promote maintainability, performance, security, and a consistent development experience.
## 1. Architectural Considerations
### 1.1. Separation of Concerns
**Standard:** Separate API interaction logic from component logic.
**Do This:** Create dedicated modules or services for handling API requests.
**Don't Do This:** Directly embed API calls within Vue components or other UI components.
**Why:** Separation of concerns improves code reusability, testability, and maintainability. Makes UI components cleaner and simpler.
**Example:**
"""typescript
// src/services/api.ts
import axios from 'axios';
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL, // Use Vite's environment variables
headers: {
'Content-Type': 'application/json',
},
});
export const getPosts = async () => {
try {
const response = await apiClient.get('/posts');
return response.data;
} catch (error) {
console.error("Failed to fetch posts:", error);
throw error; // Re-throw to allow component-level error handling
}
};
export const createPost = async (postData: any) => {
try {
const response = await apiClient.post('/posts', postData);
return response.data;
} catch (error) {
console.error("Failed to create post:", error);
throw error;
}
};
"""
"""vue
"""
### 1.2. Environment Variables
**Standard:** Utilize Vite's environment variable system for API base URLs and sensitive configuration settings.
**Do This:** Access API base URLs through "import.meta.env.VITE_API_BASE_URL".
**Don't Do This:** Hardcode URLs or sensitive API keys directly in the source code.
**Why:** Improves security by preventing accidental exposure of secrets and allows easy configuration for different environments (development, staging, production). Vite's environment variable system is designed for frontend applications.
**Example:**
".env":
"""
VITE_API_BASE_URL=https://api.example.com
VITE_API_KEY=supersecretapikey
"""
"src/services/api.ts":
"""typescript
import axios from 'axios';
const apiKey = import.meta.env.VITE_API_KEY; //Access Key
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
});
export default apiClient;
"""
### 1.3. Data Transformation
**Standard:** Transform API responses into a format suitable for the application's needs within the API service.
**Do This:** Map, filter, or reshape data within "api.ts" before it reaches components.
**Don't Do This:** Perform complex data transformations directly inside Vue components.
**Why:** Keeps components focused on presentation and simplifies data handling.
**Example:**
"""typescript
// src/services/api.ts
import apiClient from './apiClient';
interface RawPost {
id: string;
title: string;
content: string;
authorId: string;
publishedAt: string;
}
interface TransformedPost {
id: string;
title: string;
content: string;
author: string;
publishedDate: Date;
}
export const getPosts = async (): Promise => {
try {
const response = await apiClient.get('/posts');
//transformation logic.
return response.data.map(post => ({
id: post.id,
title: post.title,
content: post.content,
author: getAuthorName(post.authorId), // Assuming a function to get author's name
publishedDate: new Date(post.publishedAt)
}));
} catch (error) {
console.error("Failed to fetch posts:", error);
throw error;
}
};
"""
## 2. Implementation Details
### 2.1. HTTP Client Libraries
**Standard:** Use a modern HTTP client library like "axios" or "ky".
**Do This:** Install "axios" or "ky" via "npm install axios" or "npm install ky". Configure with a base URL.
**Don't Do This:** Use "fetch" directly without error handling, or older libraries less actively maintained.
**Why:** Modern libraries provide features such as automatic JSON parsing, interceptors, request cancellation, and better error handling.
**Example (axios):**
"""typescript
// src/services/apiClient.ts
import axios from 'axios';
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000, // Timeout after 10 seconds
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor (optional)
apiClient.interceptors.request.use(
(config) => {
// Add authentication token here if needed
const token = localStorage.getItem('authToken');
if (token) {
config.headers.Authorization = "Bearer ${token}";
}
return config;
},
(error) => {
console.error('Request error:', error);
return Promise.reject(error);
}
);
// Response interceptor (optional)
apiClient.interceptors.response.use(
(response) => {
return response;
},
(error) => {
console.error('Response error:', error);
return Promise.reject(error);
}
);
export default apiClient;
"""
### 2.2. Error Handling
**Standard:** Implement comprehensive error handling at both the service and component levels.
**Do This:** Use "try...catch" blocks in API service functions. Display user-friendly error messages in components and consider central error logging.
**Don't Do This:** Ignore errors or display generic error messages without context.
**Why:** Proper error handling improves user experience, helps debug issues, and prevents application crashes.
**Example (service level):**
"""typescript
// src/services/api.ts
import apiClient from './apiClient';
export const fetchData = async () => {
try {
const response = await apiClient.get('/data');
return response.data;
} catch (error: any) {
console.error("API Error:", error.message);
// Optionally, log the error to a central logging service.
throw new Error('Failed to fetch data from the API.'); // Custom error message
}
};
"""
**Example (component level):**
"""vue
"""
### 2.3. Data Validation
**Standard:** Validate both request and response data to ensure data integrity.
**Do This:** Use libraries like "zod" or "yup" to define schemas and validate data before sending requests and after receiving responses.
**Don't Do This:** Assume data from the API is always in the correct format.
**Why:** Prevents unexpected errors and ensures data consistency throughout the application.
**Example (using "zod"):**
Install "zod": "npm install zod"
"""typescript
// src/services/api.ts
import apiClient from './apiClient';
import { z } from 'zod';
const postSchema = z.object({
id: z.string().uuid(),
title: z.string().min(5).max(100),
content: z.string(),
createdAt: z.string().datetime(),
});
type Post = z.infer;
export const getPost = async (id: string): Promise => {
try {
const response = await apiClient.get("/posts/${id}");
const parsedData = postSchema.parse(response.data); // Validate response
return parsedData;
} catch (error: any) {
console.error("Failed to fetch post:", error);
if (error instanceof z.ZodError) {
console.error("Validation Error: ", error.errors);
throw new Error("Invalid post data received from the API.");
}
throw error;
}
};
"""
### 2.4. Caching
**Standard:** implement client-side caching for frequently accessed API data to improve performance.
**Do This:** Utilize techniques like "localStorage", "sessionStorage", or dedicated caching libraries like "vue-query" (Tanstack Query) or "swr" (Stale-While-Revalidate). Consider using "Cache-Control" headers on the server side for HTTP caching.
**Don't Do This:** Fetch the same data repeatedly without caching.
**Why:** Reduces network requests, improves load times, offers a better user experience, and reduces load on the backend server.
**Example (using "sessionStorage")**
"""typescript
// src/services/api.ts
import apiClient from './apiClient';
export const getCachedData = async () => {
const cachedData = sessionStorage.getItem('apiData');
if (cachedData) {
return JSON.parse(cachedData);
}
try {
const response = await apiClient.get('/data');
sessionStorage.setItem('apiData', JSON.stringify(response.data));
return response.data;
} catch (error) {
console.error("Failed to fetch data:", error);
throw error;
}
};
"""
**Example ("vue-query")**:
Install Dependencies: "npm install @tanstack/vue-query"
"""vue
// src/components/UserList.vue
"""
### 2.5. Authentication and Authorization
**Standard:** Implement secure authentication and authorization mechanisms to protect API endpoints.
**Do This:** Use industry-standard protocols like OAuth 2.0 or JWT for authentication. Store tokens securely (e.g., using "httpOnly" cookies or the browser's "Credential Management API"). Always validate user roles and permissions on the server-side.
**Don't Do This:** Store sensitive credentials in plain text in local storage or cookies without proper protection. Implement authorization solely on the client-side.
**Why:** Protects sensitive data and prevents unauthorized access to API resources.
**Example (using JWT and a secure cookie - Backend responsibility, but influences frontend)**
*Backend (Node.js with Express, simplified)*
"""javascript
// Example. In reality, you'd use a full-fledged auth library like Passport
app.post('/login', async (req, res) => {
// ... Authentication logic ...
const user = { id: 123, username: 'testuser' };
const token = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: '1h' });
res.cookie('authToken', token, {
httpOnly: true, // Prevents client-side JavaScript access
secure: process.env.NODE_ENV === 'production', // Only send over HTTPS in production
sameSite: 'strict', // Protect against CSRF attacks
});
res.json({ message: 'Login successful' });
});
app.post('/logout', (req, res) => {
res.clearCookie('authToken');
res.json({ message: 'Logged out' });
});
// Example middleware
function authenticateToken(req, res, next) {
const token = req.cookies.authToken;
if (!token) return res.sendStatus(401); // Unauthorized
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403); // Forbidden
req.user = user;
next();
});
}
app.get('/protected', authenticateToken, (req, res) => {
res.json({ message: 'Protected route accessed by user ' + req.user.username});
});
"""
*Frontend (Vite app)*
"""typescript
// src/services/api.ts
import apiClient from './apiClient';
import { ref } from 'vue';
const isAuthenticated = ref(false);
// Check if the user is initially authenticated, perhaps on app load
const checkAuthStatus = async () => {
try {
// A request to a protected endpoint to check authentication
await apiClient.get('/protected');
isAuthenticated.value = true;
} catch (error: any) {
isAuthenticated.value = false;
console.error("Not Authenticated");
}
};
checkAuthStatus();
export { isAuthenticated };
"""
**Important Considerations:**
* **Refresh Tokens:** Implement refresh tokens for long-lived sessions.
* **Token Revocation:** Allow users to revoke tokens (e.g., on logout).
* **CORS:** Configure CORS (Cross-Origin Resource Sharing) to restrict which origins can access the API.
* **Rate Limiting:** Implement rate limiting to prevent abuse of the API.
### 2.6. Optimistic Updates
**Standard:** Implement optimistic updates where appropriate for a better user experience.
**Do This:** Update the UI immediately as if the API call was successful, and revert the update if the call fails. This makes the application feel more responsive.
**Don't Do This:** Wait for the API call to complete before updating the UI, which can lead to a slower and less responsive user experience.
**Why:** It provides a smoother and faster user experience, by making the UI feel more responsive.
**Example (Optimistic Update)**
"""vue
// src/components/TaskList.vue
"""
## 3. Vite Specific Considerations
### 3.1 "import.meta.env" Usage
**Standard:** Leverage "import.meta.env" for accessing environment variables in Vite.
**Do This:** Ensure variables are prefixed with VITE_ to be exposed to the client-side code in Vite.
**Don't Do This:** Directly use "process.env" variables without proper Vite configuration, since they might not be available in the browser.
**Why:** Vite automatically handles replacement during the build process, making it easy to manage different configurations for various deployment environments.
**Example:**
In ".env" file:
"""
VITE_API_BASE_URL=https://api.example.com
"""
In "src/services/api.ts":
"""typescript
const baseURL = import.meta.env.VITE_API_BASE_URL;
"""
### 3.2 Proxy Configuration
**Standard:** Use Vite's "server.proxy" option for proxying API requests during development.
**Why:** This avoids CORS issues when the frontend and backend are running on different ports during development. Vite handles proxying efficiently.
**Example:**
In "vite.config.ts" or "vite.config.js":
"""typescript
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'http://localhost:3000', // Backend server address
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
});
"""
This configuration proxies all requests to "/api/*" to "http://localhost:3000/*".
### 3.3. Build Optimization
**Standard:** Ensure API interaction code is optimized for production builds by using code splitting and tree shaking.
**Do This:** Keep API services modular and import only necessary functions in components.
**Don't Do This:** Import entire API service modules when only specific functions are needed.
**Why:** Improves initial load time and reduces the overall bundle size of the application. Vite's module system enables effective tree shaking.
**Example:**
Instead of:
"""typescript
// src/components/MyComponent.vue
import * as api from '@/services/api'; // Avoid this
api.getData();
"""
Do this:
"""typescript
// src/components/MyComponent.vue
import { getData } from '@/services/api'; // Prefer this
getData();
"""
## 4. Common Anti-Patterns
* **Global State Mismanagement:** Avoid using global state (e.g., Vuex) excessively for API data caching. Opt for component-level state management, or dedicated caching solutions like "vue-query".
* **Tight Coupling:** Prevent tight coupling between UI components and API endpoint details, by abstracting API calls to services.
* **Ignoring Cancellation:** In scenarios that need it (e.g. search bars), don't forget to handle cancellation of ongoing API requests when the user makes changes, to avoid race conditions.
## 5. Security Best Practices
* **Input Sanitization:** Sanitize user inputs before sending them to the API to prevent injection attacks.
* **Output Encoding:** Encode data received from the API before rendering it in the UI to prevent cross-site scripting (XSS) attacks. Libraries like DOMPurify can help.
* **HTTPS:** Always use HTTPS to encrypt communication between the client and the API server.
* **Regular Updates:** Keep all dependencies, including the HTTP client library and Vite itself, up to date to patch any security vulnerabilities.
By adhering to these standards, you can ensure that your Vite applications have robust, maintainable, and secure API integrations. Remember to continuously review and update these standards as the Vite ecosystem evolves.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Core Architecture Standards for Vite This document outlines the core architectural standards and best practices for developing with Vite. It serves as a guide for developers and provides context for AI coding assistants to generate consistent, maintainable, performant, and secure Vite code. ## 1. Fundamental Architecture Patterns Vite's architecture leverages modern JavaScript and web development best practices, emphasizing speed, simplicity, and developer experience. ### 1.1. Module Federation Vite has first class support for Module Federation. It's critical to understand the implications of exposing or consuming modules. **Do This:** * Use Module Federation to build composable applications where independent teams own different parts of the UI. * Version your exposed modules so breaking changes do not impact consuming applications. * Implement proper error handling mechanisms when dealing with remotely loaded modules. **Don't Do This:** * Don't overuse Module Federation. Weigh the benefits against the added complexity. * Don't expose internal implementation details as modules. * Avoid circular dependencies between federated modules. **Why:** Module Federation facilitates code reuse and independent deployments, promoting scalable and maintainable applications. """javascript // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import federation from "@originjs/vite-plugin-federation"; //expose component export default defineConfig({ plugins: [ vue(), federation({ name: 'remote-app', filename: 'remoteEntry.js', exposes: { './RemoteButton': './src/components/RemoteButton.vue', }, shared: ['vue'] }) ], build: { target: 'esnext' } }) """ """javascript // consuming application vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import federation from "@originjs/vite-plugin-federation"; export default defineConfig({ plugins: [ vue(), federation({ name: 'host-app', remotes: { remote_app: "http://localhost:4173/assets/remoteEntry.js", // adjust path if needed }, shared: ['vue'] }) ], build: { target: 'esnext' } }) """ ### 1.2. Component-Based Architecture Vite projects often utilize component-based architectures, particularly when using frameworks like Vue or React. **Do This:** * Break down UIs into reusable, independent components. * Follow the Single Responsibility Principle (SRP) for each component. * Use props to pass data and events to communicate between components. **Don't Do This:** * Avoid creating monolithic components with too much logic. * Don't tightly couple components to specific data sources. * Don't mutate props directly within a component. Use "emit" in Vue, or state management solutions. **Why:** Component-based architecture improves code organization, reusability, and testability. """vue // MyComponent.vue <template> <div> <h1>{{ title }}</h1> <p>{{ description }}</p> <button @click="handleClick">Click me</button> </div> </template> <script setup> import { defineProps, defineEmits } from 'vue'; const props = defineProps({ title: { type: String, required: true }, description: { type: String, default: '' } }); const emit = defineEmits(['update']); const handleClick = () => { emit('update', 'New value'); }; </script> """ ### 1.3. State Management Choose a preferred state management solution and apply it consistently across your project. **Do This:** * Select state management based on the complexity of your application. Pinia and Vuex are common choices with Vue. Redux or Zustand integrate well with React. * Centralize application state. * Use mutations or actions to update state predictably. **Don't Do This:** * Overuse global state management for simple component communication. Props and events often suffice. * Directly modify state without using defined actions/mutations. **Why:** Consistent state management makes applications predictable and reduces debugging time. """javascript // Pinia store example (store.js) import { defineStore } from 'pinia'; export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), actions: { increment() { this.count++; }, decrement() { this.count--; } }, getters: { doubleCount: (state) => state.count * 2 } }); """ ### 1.4 API Layer Isolate API interactions. This promotes loose coupling and enables easier testing and maintenance. **Do This:** * Create dedicated API service modules. * Handle all API calls, data transformation, and error handling within this layer. * Abstract away specific API implementations. **Don't Do This:** * Directly making API calls within components * Mixing API logic with unrelated code * Exposing API keys or secrets directly in the client-side code. Use environment variables. **Why:** An API layer makes API calls easier to manage, and easier to swap out. """javascript // apiService.js import axios from 'axios'; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api'; export const getPosts = async () => { try { const response = await axios.get("${API_BASE_URL}/posts"); return response.data; } catch (error) { console.error('Error fetching posts:', error); throw error; } }; export const createPost = async (postData) => { try { const response = await axios.post("${API_BASE_URL}/posts", postData); return response.data; } catch (error) { console.error('Error creating post:', error); throw error; } }; """ ## 2. Project Structure and Organization Principles A well-defined project structure is crucial for maintainability, scalability, and team collaboration. ### 2.1. Standard Directory Structure Follow a consistent directory structure across all Vite projects. A recommended structure is: """ vite-project/ ├── public/ # Static assets ├── src/ # Source code │ ├── assets/ # Images, fonts, etc. │ ├── components/ # Reusable UI components │ ├── composables/ # Vue composables │ ├── layouts/ # Application layouts │ ├── pages/ # Page components (if using a router) │ ├── styles/ # Global styles and themes │ ├── utils/ # Utility functions │ ├── App.vue # Root component │ ├── main.js # Entry point │ └── router.js # Router configuration ├── .env # Environment variables ├── vite.config.js # Vite configuration ├── package.json # Project dependencies └── README.md # Project documentation """ **Do This:** * Adhere to the proposed structure or establish a project-specific standard. * Keep components self-contained within the "components" directory. * Group related files within meaningful subdirectories. **Don't Do This:** * Store files in arbitrary locations without a clear organizational principle. * Mix different types of files within the same directory. * Create overly deep or complex directory structures. **Why:** A consistent structure simplifies navigation, enhances discoverability, and promotes code reuse, leading to better collaboration and maintainability. ### 2.2. Naming Conventions Establish clear naming conventions for files, directories, components, and variables. **Do This:** * Use descriptive and meaningful names. * Follow consistent naming patterns (e.g., PascalCase for components, camelCase for variables). * Use a consistent suffix for component files (e.g., ".vue" for Vue components, ".jsx" for React components). * Use kebab-case for directories and file names, especially when components are accessed through HTML. **Don't Do This:** * Use ambiguous or cryptic names. * Mix different naming conventions within the same project. * Use reserved keywords as names. **Why:** Consistent naming improves code readability, reduces confusion, and makes it easier to understand the purpose of each file and variable. **Example:** """ components/ ├── MyButton.vue // PascalCase for component filename ├── user-profile/ // kebab-case for directories │ └── UserProfile.vue // PascalCase for component filename """ """javascript // MyButton.vue <script setup> const buttonText = 'Click me'; // camelCase for variables const handleClick = () => { // camelCase for function names console.log('Button clicked'); }; </script> """ ### 2.3. Modular Design Break down code into small, independent, and reusable modules. **Do This:** * Create dedicated modules for specific functionalities. * Export only necessary functions and variables from each module. * Use ES module syntax ("import" and "export") to manage dependencies. **Don't Do This:** * Create large, monolithic modules with multiple responsibilities. * Export unnecessary variables or functions. * Rely on global variables for inter-module communication. **Why:** Modular design promotes code reuse, simplifies maintenance, and makes it easier to test and debug individual components. """javascript // utils/formatDate.js export function formatDate(date) { const options = { year: 'numeric', month: 'long', day: 'numeric' }; return new Date(date).toLocaleDateString(undefined, options); } // components/MyComponent.vue import { formatDate } from '../utils/formatDate.js'; <template> <p>Published: {{ formattedDate }}</p> </template> <script setup> import { ref, onMounted } from 'vue'; import { getPosts } from '../apiService.js'; const formattedDate = ref(''); onMounted(async () => { const posts = await getPosts(); formattedDate.value = formatDate(posts[0].createdAt); }); </script> """ ### 2.4. Environment Variables Use environment variables to manage configuration settings that vary between environments (development, staging, production). Vite handles environment variables differently than other bundlers. **Do This:** * Store sensitive settings in environment variables instead of hardcoding them. * Use the ".env" file for development environment variables. * Prefix environment variables with "VITE_" to expose them to client-side code. * Use "import.meta.env" to access environment variables in your code. **Don't Do This:** * Commit ".env" files to version control. Add them to ".gitignore". Use ".env.example" to track the *names* of the environment variables. * Store sensitive information directly in your code. * Expose sensitive environment variables to the client-side code. **Why:** Environment variables improve security, simplify configuration management, and allow you to deploy the same code to different environments without modification. """ // .env VITE_API_BASE_URL=https://api.example.com VITE_APP_TITLE=My Awesome App """ """javascript // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], define: { 'process.env': {} //required if you're migrating a web app to vite } }) """ """javascript // components/MyComponent.vue <template> <h1>{{ appTitle }}</h1> </template> <script setup> const appTitle = import.meta.env.VITE_APP_TITLE; </script> """ ## 3. Vite Ecosystem & Plugins Leverage the Vite ecosystem of plugins and integrations to extend its functionality. ### 3.1. Plugin Usage Use plugins to extend Vite's capabilities, such as adding support for different file formats, optimizing assets, or integrating with other tools. **Do This:** * Use official or well-maintained community plugins. * Configure plugins properly according to their documentation. * Understand the specific tasks performed by each plugin. **Don't Do This:** * Add unnecessary plugins that duplicate functionality. * Use outdated or abandoned plugins. * Ignore plugin configuration options. **Why:** Plugins provide a modular way to extend Vite's functionality and integrate with other tools. **Example:** """javascript // vite.config.js import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import eslintPlugin from 'vite-plugin-eslint'; export default defineConfig({ plugins: [ vue(), eslintPlugin() ] }); """ ### 3.2. HMR (Hot Module Replacement) Take full advantage of Vite's HMR feature for rapid development. **Do This:** * Ensure that your components and modules are designed to support HMR. * Use state management solutions that are compatible with HMR. * Use Vite's built-in HMR API to handle custom HMR logic. * When using Vue, ensure components are correctly re-rendered when changes occur. **Don't Do This:** * Rely on full page reloads during development. * Ignore HMR errors. **Why:** HMR dramatically improves developer productivity by allowing you to see changes in the browser instantly without losing application state. ### 3.3. Code Splitting Utilize Vite's built-in code splitting to optimize the loading performance of your application. **Do This:** * Use dynamic imports ("import()") to load modules on demand. * Group related modules into separate chunks. * Analyze the bundle size and identify opportunities for code splitting. * Leverage Vite's automatic code splitting for routes and components. **Don't Do This:** * Load all modules upfront in a single large bundle. * Create overly granular code splits that result in too many small files. **Why:** Code splitting reduces the initial load time of your application by only loading the code that is needed for the current page or component. This significantly improves the user experience, especially on slow network connections. ## 4. Version Control and Collaboration ### 4.1. Git Workflow Adopt a standardized Git workflow for team collaboration. **Do This:** * Use feature branches for developing new features or fixing bugs. * Create pull requests for code review. * Write clear and concise commit messages. * Use Git tags to mark releases. **Don't Do This:** * Commit directly to the main branch without code review. * Write vague or uninformative commit messages. * Ignore code review feedback. **Why:** Git workflows facilitate collaboration, track changes, and ensure code quality. ### 4.2. Code Review Implement a code review process to catch errors, enforce standards, and share knowledge. **Do This:** * Review code changes thoroughly. * Provide constructive feedback. * Focus on code quality, readability, and maintainability. * Use automated code analysis tools to identify potential issues. **Don't Do This:** * Skip code reviews. * Provide vague or unhelpful feedback. * Ignore code analysis warnings. **Why:** Code review improves code quality, prevents errors, and promotes knowledge sharing. ### 4.3. Documentation Create clear and concise documentation for your Vite projects. **Do This:** * Write a README.md file that describes the project, how to set it up, and how to use it. * Document complex components and modules. * Use inline comments to explain tricky or non-obvious code. * Keep documentation up-to-date. **Don't Do This:** * Skip documentation entirely. * Write vague or incomplete documentation. * Let documentation become outdated. **Why:** Documentation makes it easier for others (and yourself) to understand, use, and maintain your code. ## 5. Performance Optimization Techniques ### 5.1. Lazy Loading Use lazy loading for components, images, and other assets that are not immediately visible on the screen. **Do This:** * Use dynamic imports ("import()") to load components on demand. * Use the "loading="lazy"" attribute for images. * Use Intersection Observer API for more advanced lazy loading scenarios. **Don't Do This:** * Load all assets upfront, even if they are not immediately needed. * Use lazy loading for critical above-the-fold content. **Why:** Lazy loading improves the initial load time of your application by only loading assets as they become visible on the screen. ### 5.2. Image Optimization Optimize images to reduce their file size without sacrificing quality. **Do This:** * Use appropriate image formats (e.g., WebP, AVIF). * Compress images using tools like ImageOptim or TinyPNG. * Resize images to the appropriate dimensions. * Use responsive images ("<picture>" element or "srcset" attribute). **Don't Do This:** * Use excessively large images. * Use inappropriate image formats (e.g., PNG for photographs). * Ignore image optimization techniques. **Why:** Optimized images reduce the page size and improve loading performance. ### 5.3. Minification and Compression Minify and compress JavaScript, CSS, and HTML files to reduce their file size. Vite handles this automatically in production mode. **Do This:** * Ensure that your Vite configuration is set up to minify and compress assets. * Use tools like Terser or esbuild for JavaScript minification. * Use gzip or Brotli compression on your server. **Don't Do This:** * Deploy unminified or uncompressed assets to production. * Disable minification or compression. **Why:** Minification and compression reduce the page size and improve loading performance. ## 6. Security Best Practices ### 6.1. Dependency Management Keep dependencies up to date to patch security vulnerabilities. **Do This:** * Regularly update dependencies using "npm update" or "yarn upgrade". * Use a tool like Snyk or Dependabot to monitor dependencies for vulnerabilities. * Remove unused dependencies. **Don't Do This:** * Use outdated dependencies. * Ingore security alerts. **Why:** Keeping dependencies up to date mitigates known security vulnerabilities. ### 6.2. Input Validation Validate user input to prevent injection attacks. **Do This:** * Validate all user input on both the client-side and server-side. * Use appropriate validation techniques to prevent SQL injection, XSS, and other attacks. * Encode or sanitize user input before displaying it on the page. **Don't Do This:** * Trust user input without validation. * Display raw user input on the page. **Why:** Input validation prevents malicious code from being injected into your application. ### 6.3. Content Security Policy (CSP) Use CSP to restrict the sources of content that your application is allowed to load. **Do This:** * Configure CSP headers on your server. * Specify the allowed sources for scripts, styles, images, and other resources. * Use a stricter CSP policy in production than in development. **Don't Do This:** * Disable CSP entirely. * Use a overly permissive CSP policy. **Why:** CSP mitigates the risk of XSS attacks.
# Tooling and Ecosystem Standards for Vite This document outlines coding standards and best practices for the Vite ecosystem, focusing on tooling and related libraries/extensions. These standards promote maintainability, performance, security, and a consistent developer experience. ## 1. Recommended IDEs and Extensions ### 1.1. Standard * **Do This:** Use VS Code as the primary IDE due to its rich ecosystem of extensions and excellent support for JavaScript, TypeScript, and front-end development. * **Don't Do This:** Rely solely on basic text editors without IDE-level features. ### 1.2. Essential VS Code Extensions * **Do This:** Install the following extensions to enhance your Vite development workflow: * **ESLint:** For consistent code style and early error detection. * **Prettier:** For automatic code formatting. * **Volar:** (Recommended for Vue 3 projects) or **Vue Language Features (VLS)** (for older Vue versions). Provides enhanced syntax highlighting, auto-completion, and type checking specifically tailored for Vue.js components within Vite. * **TypeScript Vue Plugin (Volar)**: Improves TypeScript support within Vue SFCs (Single-File Components). * **EditorConfig for VS Code:** Ensures consistent coding styles across different IDEs and team members by reading ".editorconfig" files. * **npm Intellisense:** Autocompletes npm modules in "import" statements. * **Path Intellisense:** Autocompletes file paths. * **JavaScript (ES6) code snippets:** Provides helpful code snippets for common JavaScript/ES6 patterns. * **Vite Plugin for VS Code:** (if available and actively maintained) Directly integrates Vite commands and features within VS Code, simplifying development tasks. * **Why:** These extensions provide real-time linting, formatting, and code completion, leading to fewer errors, increased productivity, and a consistent codebase. * **Anti-Pattern:** Neglecting linting or formatting tools leads to inconsistent styling, making the code harder to read and maintain. ### 1.3 Configuration * **Do This:** Configure ESLint, Prettier, and EditorConfig at the project level to enforce consistent coding styles across the team. Use ".eslintrc.js", ".prettierrc.js", and ".editorconfig" files at the project root. * **Example ".eslintrc.js":** """javascript module.exports = { root: true, env: { node: true, browser: true, es2021: true, }, extends: [ 'eslint:recommended', 'plugin:vue/vue3-essential', '@vue/eslint-config-typescript/recommended', 'prettier', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the LAST extension in the array. ], parserOptions: { ecmaVersion: 2020, }, rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'vue/multi-word-component-names': 'off', // Often not practical for smaller components. Adjust as needed. }, overrides: [ { files: ['*.vue'], parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', sourceType: 'module', ecmaFeatures: { jsx: true, }, }, }, ], }; """ * **Example ".prettierrc.js":** """javascript module.exports = { semi: false, singleQuote: true, trailingComma: 'es5', printWidth: 100, tabWidth: 2, }; """ * **Example ".editorconfig":** """ini root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false """ * **Why:** Project-level configuration ensures that all team members adhere to the same coding standards. This simplifies code reviews and reduces merge conflicts caused by style differences. Using "@vue/eslint-config-typescript/recommended" provides sensible defaults for Typscript-based Vue projects, integrating well with Vite projects using Vue and Typescript. ## 2. Dependency Management ### 2.1. Using "npm" or "yarn" or "pnpm" * **Do This:** Use "npm","yarn", or "pnpm" as your package manager. "pnpm" is often preferred for its efficient disk space usage and speed advantages due to its use of symlinked "node_modules". * **Don't Do This:** Manually download and manage dependencies or rely on outdated package managers. ### 2.2. Version Control * **Do This:** Use "npm ci", "yarn install --frozen-lockfile", or "pnpm install --frozen-lockfile" in CI/CD environments to ensure deterministic builds based on the "package-lock.json", "yarn.lock", or "pnpm-lock.yaml" file. * **Why:** "npm ci", "yarn install --frozen-lockfile", and "pnpm install --frozen-lockfile" prevent unexpected dependency updates from breaking builds. ### 2.3. Dependency Updates * **Do This:** Regularly update dependencies to patch security vulnerabilities and access new features. Use tools like Dependabot to automate dependency updates and receive security alerts. * **Don't Do This:** Ignore dependency updates for extended periods. ### 2.4. Peer Dependencies * **Do This:** Carefully manage peer dependencies to avoid conflicts and ensure compatibility between your packages and their dependencies. Pay close attention to warnings about unmet peer dependencies during installation. * **Why:** Mismatched peer dependencies can lead to runtime errors and unexpected behavior. ### 2.5. Dev Dependencies * **Do This:** Clearly distinguish between production dependencies (listed under "dependencies") and development dependencies (listed under "devDependencies"). Only include packages needed at runtime in the "dependencies" section. * **Why:** Including unnecessary development dependencies in production bundles increases bundle size. ## 3. Vite Plugins ### 3.1. Standard * **Do This:** Leverage Vite plugins to extend Vite's functionality and integrate with other tools. * **Don't Do This:** Reinvent the wheel by implementing functionality that is already available in existing plugins. ### 3.2. Key Plugins * **Do This:** Consider using the following plugins for common tasks: * "@vitejs/plugin-vue": Official Vue 3 support. Crucial for Vue-based projects. * "@vitejs/plugin-vue-jsx": Official Vue JSX support. * "vite-plugin-html": For transforming "index.html" during build (e.g., injecting environment variables). * "vite-plugin-compression": Compresses assets (e.g., gzip, brotli) for improved performance. * "vite-plugin-imagemin": Optimizes images during the build process. * "vite-plugin-pwa": Generates a Progressive Web App manifest. * "vite-tsconfig-paths": Allows importing modules using paths defined in "tsconfig.json". Essential for avoiding relative path hell. * "rollup-plugin-visualizer": Visualizes the size and composition of your bundles for optimization purposes. * "unplugin-auto-import": Automatically imports APIs from Vue, React, or other libraries. Reduces boilerplate. * "unplugin-vue-components": Auto-imports Vue components. Reduces boilerplate. * "vite-plugin-inspect ": Inspects the intermediate state of Vite's transforms. Helpful for debugging plugin issues. * **Why:** Plugins enhance Vite's capabilities and streamline common development tasks. ### 3.3. Plugin Configuration * **Do This:** Configure plugins correctly in "vite.config.js" or "vite.config.ts". Provide necessary options and ensure plugins are enabled in the appropriate environments (development vs. production). * **Example:** """typescript import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import viteCompression from 'vite-plugin-compression' import { resolve } from 'path' export default defineConfig({ plugins: [ vue(), viteCompression({ algorithm: 'gzip', ext: '.gz', deleteOriginFile: false, // Keep the original files }) ], resolve: { alias: { '@': resolve(__dirname, 'src'), // For absolute imports }, }, build: { rollupOptions: { output: { manualChunks(id) { // Code splitting if (id.includes('node_modules')) { return id.toString().split('node_modules/')[1].split('/')[0].toString(); } } } } } }) """ * **Why:** Incorrect plugin configuration can lead to unexpected behavior or build errors. The "resolve.alias" configuration significantly improves code readability by enabling absolute imports using the "@" symbol as an alias for the "src" directory. This avoids deeply nested relative paths ("../../../components/MyComponent.vue") and makes refactoring easier. Implementing "rollupOptions.output.manualChunks" enables code splitting for improved performance. ### 3.4. Custom Plugins * **Do This:** Create custom plugins when you need to implement functionality that is not available in existing plugins. Follow the Vite Plugin API documentation. * **Don't Do This:** Eject from Vite's build process and reinvent the entire build pipeline. * **Example:** """typescript // vite-plugin-example.ts import { Plugin } from 'vite'; export function vitePluginExample(): Plugin { return { name: 'vite-plugin-example', transform(code, id) { if (id.endsWith('.vue')) { // Modify Vue component code return code.replace('This is a placeholder', 'This is a Vite plugin!'); } return code; }, }; } """ """typescript // vite.config.ts import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { vitePluginExample } from './vite-plugin-example'; export default defineConfig({ plugins: [vue(), vitePluginExample()], }); """ * **Why:** Custom plugins provide a flexible way to extend Vite's functionality and adapt it to your specific needs. Using the "transform" hook illustrated above enables modification of the code during the build process, in this case modifying Vue component content. ## 4. Debugging and Profiling ### 4.1. Standard * **Do This:** Utilize browser developer tools and Vite's built-in features for debugging and profiling your application. * **Don't Do This:** Resort to "console.log" statements as the primary debugging method for complex issues. ### 4.2. Debugging Techniques * **Do This:** Use the "debugger" statement to set breakpoints in your code. Inspect variables and step through the execution flow using the browser's debugger. * **Do This:** Use source maps to debug your code in its original form, even after it has been transformed and bundled. * **Use editor integration:** VS Code's debugger is now capable of debugging Vite applications directly. Configure launch configurations in ".vscode/launch.json" for seamless debugging. * **Example ".vscode/launch.json":** """json { "version": "0.2.0", "configurations": [ { "type": "chrome", "request": "launch", "name": "Launch Chrome against localhost", "url": "http://localhost:5173", // Or whatever port Vite is using "webRoot": "${workspaceFolder}/src" } ] } """ ### 4.3. Profiling Techniques * **Do This:** Use the browser's performance profiling tools to identify performance bottlenecks in your application. Analyze CPU usage, memory allocation, and rendering performance. * **Do This:** Use the "rollup-plugin-visualizer" to analyze bundle sizes and identify large dependencies. Implement code splitting to reduce initial load times. * **Do This:** Analyze the network tab to identify slow-loading resources. Optimize images and other assets to reduce file sizes. * **Why:** Profiling helps identify and address performance bottlenecks, improving the overall user experience. ### 4.4. Vite Devtools * **Do This:** Investigate and utilize any available Vite Devtools extensions for your browser. These tools can provide insights into Vite's internal state and simplify debugging. ## 5. Continuous Integration and Deployment (CI/CD) ### 5.1. Standard * **Do This:** Automate the build, test, and deployment process using a CI/CD pipeline. * **Don't Do This:** Manually build and deploy your application. ### 5.2. Popular CI/CD Services * **Do This:** Consider using the following CI/CD services: * GitHub Actions * GitLab CI * CircleCI * Travis CI ### 5.3. CI/CD Configuration * **Do This:** Configure your CI/CD pipeline to perform the following steps: 1. **Checkout code:** Clone the repository. 2. **Install dependencies:** Use "npm ci", "yarn install --frozen-lockfile", or "pnpm install --frozen-lockfile" to install dependencies from the lockfile. 3. **Run tests:** Execute unit tests, integration tests, and end-to-end tests. 4. **Build application:** Run "vite build" to build the application. 5. **Deploy application:** Deploy the built assets to your hosting provider (e.g., Netlify, Vercel, AWS S3). * **Example GitHub Actions Workflow (".github/workflows/deploy.yml"):** """yaml name: Deploy to Netlify on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 # Or your preferred Node version - name: Install dependencies run: npm ci # or yarn install --frozen-lockfile or pnpm install --frozen-lockfile - name: Run tests run: npm test # Or your test command - name: Build run: npm run build # Or yarn build or pnpm build - name: Deploy to Netlify uses: netlify/actions/deploy@v1 with: publishDir: './dist' # Your build output directory netlify_auth_token: ${{ secrets.NETLIFY_AUTH_TOKEN }} netlify_site_id: ${{ secrets.NETLIFY_SITE_ID }} """ * **Why:** CI/CD pipelines automate the build, test, and deployment process, reducing the risk of human error and ensuring consistent deployments. ### 5.4. Environment Variables * **Do This:** Use environment variables to configure your application in different environments (development, staging, production). Access environment variables using "import.meta.env" in Vite. * **Don't Do This:** Hardcode sensitive information (e.g., API keys, database passwords) in your code. * **Example:** """typescript // vite.config.ts import { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), '') return { plugins: [vue()], define: { __APP_ENV__: JSON.stringify(env.APP_ENV), }, } }) """ """vue <template> <div> {{ appEnv }} </div> </template> <script setup> import { ref } from 'vue'; const appEnv = ref(import.meta.env.VITE_APP_ENV); </script> """ ### 5.5. Staging Environments * **Do This:** Implement a staging environment that mirrors your production environment as closely as possible. Deploy code to the staging environment before deploying to production. * **Why:** Staging environments allow you to test changes in a realistic environment and catch potential issues before they affect your users. ## 6. Collaboration and Communication ### 6.1. Standard * **Do This:** Use collaborative tools and effective communication strategies to ensure that team members are aligned and informed about project progress and decisions. * **Don't Do This:** Work in isolation without communicating with your team. ### 6.2. Communication Tools * **Do This:** Use tools like Slack, Microsoft Teams, or Discord for real-time communication. Use project management tools like Jira, Trello, or Asana to track tasks and manage workflows. * **Why:** Effective communication tools facilitate collaboration and ensure that everyone is on the same page. ### 6.3. Code Reviews * **Do This:** Conduct thorough code reviews to identify potential issues and ensure code quality. Use pull requests to manage code changes and facilitate code reviews. * **Why:** Code reviews help catch errors, improve code quality, and promote knowledge sharing. ### 6.4. Documentation * **Do This:** Document your code and project architecture. Use tools like Storybook or Styleguidist to document UI components. * **Why:** Documentation makes it easier for team members to understand and maintain the codebase. By adhering to these coding standards, your team can build maintainable, performant, and secure Vite applications efficiently. These standards will help ensure consistent code quality across the entire project.
# State Management Standards for Vite This document outlines the recommended coding standards for state management in Vite projects. Adhering to these standards improves code maintainability, enhances performance, and promotes predictable application behavior. ## 1. Architectural Overview: Choosing a State Management Solution Selecting the right state management solution is crucial for the scalability and maintainability of your Vite application. Consider the complexity of your application before deciding on a technology. ### 1.1. Standard: Evaluate Application Complexity * **Do This:** Assess the size, complexity, and data flow of your application early in the development process. * **Don't Do This:** Automatically adopt a complex state management solution for simple applications or components. **Why:** Over-engineering adds unnecessary complexity and overhead. Simple applications may benefit from simpler approaches. ### 1.2. Standard: Consider Lightweight Options First * **Do This:** For smaller applications, use "useState" and "useReducer" from React (or equivalents in Vue/Svelte) for local component state and simple global state management with Context API/Provide/Inject patterns. * **Don't Do This:** Immediately jump to Redux/Vuex/Pinia for trivial state needs. **Why:** React's built-in hooks and Context API provide a functional and efficient approach for managing state in smaller applications without the boilerplate associated with larger libraries. **Example (React with Context API):** """jsx // Context.js import React, { createContext, useState, useContext } from 'react'; const ThemeContext = createContext(); export const ThemeProvider = ({ children }) => { const [theme, setTheme] = useState('light'); const toggleTheme = () => { setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light')); }; const value = { theme, toggleTheme }; return ( <ThemeContext.Provider value={value}> {children} </ThemeContext.Provider> ); }; export const useTheme = () => useContext(ThemeContext); // Component.jsx import React from 'react'; import { useTheme } from './Context'; const MyComponent = () => { const { theme, toggleTheme } = useTheme(); return ( <div className={"App ${theme}"}> <h1>Current Theme: {theme}</h1> <button onClick={toggleTheme}>Toggle Theme</button> </div> ); }; export default MyComponent; """ **Example (Vue 3 with Provide/Inject):** """vue // App.vue - Providing the state <template> <MyComponent /> </template> <script setup> import { provide, ref } from 'vue'; import MyComponent from './MyComponent.vue'; const theme = ref('light'); const toggleTheme = () => { theme.value = theme.value === 'light' ? 'dark' : 'light'; }; provide('theme', theme); provide('toggleTheme', toggleTheme); </script> // MyComponent.vue - Injecting the state <template> <div :class="'App ' + theme"> <h1>Current Theme: {{ theme }}</h1> <button @click="toggleTheme">Toggle Theme</button> </div> </template> <script setup> import { inject } from 'vue'; const theme = inject('theme'); const toggleTheme = inject('toggleTheme'); </script> """ ### 1.3. Standard: When to Use State Management Libraries * **Do This:** Utilize libraries like Zustand, Jotai, Recoil, or Pinia when your application reaches a scale where managing global state with Context/Provide/Inject becomes cumbersome, or you need advanced features like time-travel debugging, middleware, or centralized state mutations. * **Don't Do This:** Introduce a complex state management library without a clear understanding of its benefits and tradeoffs. **Why:** State management libraries provide structured approaches to managing global application state, improving testability, and simplifying debugging. ### 1.4. Standard: Choose a Reactive State Management Solution * **Do This:** Prioritize reactive state management libraries that automatically update components when the state changes (e.g., Pinia for Vue, Zustand or Valtio for frameworks that need a more explicit reactivity model outside of a framework e.g. Vanilla JS, Svelte, React if you use a non-reactive state model). * **Don't Do This:** Manually trigger updates or rely on manual data synchronization. **Why:** Reactivity simplifies component logic, reduces boilerplate code, and ensures that the UI accurately reflects the application's state. ## 2. State Management Implementation Standards ### 2.1. Standard: Single Source of Truth * **Do This:** Ensure that each piece of data has a single, authoritative source within your state management system. * **Don't Do This:** Duplicate or derive data in multiple places without proper synchronization. **Why:** A single source of truth prevents inconsistencies and simplifies debugging by making it clear where data originates. **Example (Zustand):** """javascript import { create } from 'zustand' const useStore = create((set) => ({ bears: 0, increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), removeAllBears: () => set({ bears: 0 }), })) function BearCounter() { const bears = useStore((state) => state.bears) return <h1>{bears} around here ...</h1> } function BearIncreaseButton() { const increasePopulation = useStore((state) => state.increasePopulation) return <button onClick={increasePopulation}>one up</button> } useStore.subscribe(console.log) """ ### 2.2. Standard: Immutability * **Do This:** Treat state as immutable, meaning that you should create a new copy of the state whenever you need to update it. Avoid direct modifications to existing state objects. * **Don't Do This:** Mutate state directly, as this can lead to unpredictable component behavior and difficult-to-debug issues. **Why:** Immutability facilitates efficient change detection, simplifies debugging with features like time-travel debugging, and improves performance by avoiding unnecessary re-renders. **Example (Pinia):** When using Pinia, state mutations are already handled reactively. You should still adopt immutability best practices when dealing with complex state structures, particularly within actions. """typescript import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, nestedObject: { value: 'initial' } }), actions: { increment() { this.count++ }, // Correct way to update nested objects immutably updateNestedValue(newValue: string) { // Option 1: Using the $patch method this.$patch((state) => { state.nestedObject = { ...state.nestedObject, value: newValue }; }); // Option 2: Replacing the entire nested object this.nestedObject = { value: newValue }; } }, }) """ ### 2.3. Standard: Explicit State Mutations * **Do This:** Use dedicated functions or actions to modify the state. Centralize these modifications in a predictable location. * **Don't Do This:** Directly modify state within components or spread state updates across multiple unrelated components. **Why:** Centralized state modifications make it easier to track changes, debug issues, and implement complex state transitions. **Example (Pinia with Actions):** """typescript import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ({ userData: null, isLoading: false, error: null, }), actions: { async fetchUserData(userId: string) { this.isLoading = true; try { const response = await fetch("/api/users/${userId}"); //Assumes API is properly configured with CORS this.userData = await response.json(); } catch (error) { this.error = error; } finally { this.isLoading = false; } }, }, }); """ ### 2.4. Standard: Selectors for Derived Data * **Do This:** Use selectors (computed properties in Vue/Pinia, selector functions in Redux/Recoil/Zustand) to derive data from the state. Cache and memoize selector results to avoid unnecessary computations on every render. * **Don't Do This:** Perform complex data transformations directly in components or recalculate derived data repeatedly. **Why:** Selectors improve performance by preventing redundant computations and encapsulate the logic for deriving data from the state. **Example (Pinia with Getters):** """typescript import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), getters: { doubleCount: (state) => state.count * 2, // Use this type if you use TypeScript doubleCountPlusOne(): number { return this.doubleCount + 1 }, }, actions: { increment() { this.count++ }, }, }) """ ### 2.5. Standard: Asynchronous Actions with Caution * **Do This:** Handle asynchronous operations (e.g., API calls) within actions. Use "async/await" or Promises appropriately. Implement error handling and loading states to provide feedback to the user. * **Don't Do This:** Perform asynchronous operations directly in components without managing loading states or handling errors. Mutate loading states directly in components. **Why:** Isolating asynchronous operations in actions improves testability and provides a centralized way to manage side effects. Managed loading states/errors enhances UX. """typescript import { defineStore } from 'pinia'; export const useDataStore = defineStore('data', { state: () => ({ data: null, loading: false, error: null, }), actions: { async fetchData() { this.loading = true; this.error = null; // Reset error state at the beginning try { const response = await fetch('/api/data'); // Assumes API properly configured for CORS and available if (!response.ok) { throw new Error("HTTP error! status: ${response.status}"); } this.data = await response.json(); } catch (e: any) { this.error = e.message; } finally { this.loading = false; } }, }, }); """ ### 2.6. Standard: Centralized API interaction * **Do This:** Abstract all API requests to dedicated service/APIModules. * **Don't Do This:** Perform your API request inside Vue components or Pinia actions. **Why:** Improves maintainability, reusability and avoids complexity in your Pinia stores. """typescript // api/todos.ts import { Todo } from '@/types' const BASE_URL = 'https://jsonplaceholder.typicode.com' const getTodos = async (): Promise<Todo[]> => { try { const response = await fetch("${BASE_URL}/todos") if (!response.ok) { throw new Error("HTTP error! Status: ${response.status}") } return await response.json() as Todo[] } catch (error) { console.error('Failed to fetch todos:', error) throw error // re-throw the error so the caller can handle it } } export { getTodos } """ ### 2.7 Standard: Modular State Management * **Do This:** Break down large stores into smaller, manageable modules. Group related state, actions, and getters into separate store files. * **Don't Do This:** Create a single, monolithic store that contains all of your application's state. **Why:** Improves code organization, maintainability, especially in larger applications. **Example (Pinia modular stores):** """typescript // stores/user.ts import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ name: 'John Doe', email: 'john.doe@example.com' }), getters: { profile: (state) => "${state.name} (${state.email})" } }) // stores/settings.ts import { defineStore } from 'pinia' export const useSettingsStore = defineStore('settings', { state: () => ({ theme: 'light', notificationsEnabled: true }), actions: { toggleTheme() { this.theme = this.theme === 'light' ? 'dark' : 'light' } } }) // Component.vue <script setup> import { useUserStore, useSettingsStore } from '@/stores' const userStore = useUserStore() // you can pass the entire store to the composable const settingsStore = useSettingsStore() </script> """ ## 3. Vite-Specific Considerations ### 3.1. Standard: Environment Variables in State * **Do This:** Access environment variables through "import.meta.env" instead of directly referencing "process.env". Define a clear schema for the environment variables used by your application. * **Don't Do This:** Hardcode sensitive information or directly expose "process.env" to the client-side code. **Why:** Vite exposes environment variables through "import.meta.env", which is specifically designed for browser environments. Direct "process.env" access will fail in the browser. **Snippet:** """javascript // Accessing an environment variable const apiUrl = import.meta.env.VITE_API_URL; // Ensure VITE_API_URL is prefixed with VITE_ """ ### 3.2. Standard: Lazy Loading Modules with State * **Do This:** Use dynamic imports ("import()") with state modules to reduce initial bundle size and improve loading performance, in concert with component-level lazy loading. * **Don't Do This:** Load all state modules upfront, as this can negatively impact the initial load time of your application. **Why:** Dynamic imports allow you to load state modules on demand, reducing the initial bundle size and improving performance. This is especially helpful for feature-rich applications. **Example:** """javascript // Load the user store only when needed async function loadUserStore() { const { useUserStore } = await import('./stores/user'); const userStore = useUserStore(); // ... use the store } """ ### 3.3. Standard: Minimizing Re-renders with "shallowRef" * **Do This:** Leveraging "shallowRef" and "shallowReactive" in Vue when fine-grained reactivity is not needed, particularly for large immutable data structures. * **Don't Do This:** Blindly using "ref" or "reactive" for all state, which can lead to unnecessary re-renders and performance bottlenecks. * **Why:** By default, Vue's reactivity system deeply observes all properties of an object, which can be overkill for data that doesn't change frequently. "shallowRef" and "shallowReactive" only track changes at the top level, providing a performance boost by avoiding unnecessary re-renders. **Example:** """vue <script setup> import { shallowRef, onMounted } from 'vue'; import { getLargeDataSet } from './api'; // Simulate fetching a large dataset const dataSet = shallowRef(null); const loadData = async () => { dataSet.value = await getLargeDataSet(); }; onMounted(loadData); </script> <template> <div v-if="dataSet"> <!-- Render the dataSet. Changes *within* the object will NOT trigger re-renders, only replacing the entire object will trigger an update. --> <p>Data loaded</p> </div> <div v-else>Loading...</div> </template> """ ### 3.4 Standard: Use Vite's HMR * **Do This:** Take advantage of Vite's Hot Module Replacement (HMR) to preserve state between code changes during development. * **Don't Do This:** Refresh entire application manually on even minor code edits. **Why:** HMR significantly speeds up the development process by allowing you to see changes instantly without losing the current application state. In state management this means preserving the current state within Pinia/Zustand/etc, so you don't have to manually reset or re-navigate. ## 4. Security Considerations ### 4.1. Standard: Avoid Storing Sensitive Information in Client-Side State * **Do This:** Store only non-sensitive data in client-side state. Handle sensitive information (e.g., user passwords, API keys) on the server-side or use secure storage mechanisms. * **Don't Do This:** Store sensitive information directly in the global state, where it can be accessed by malicious actors. **Why:** Client-side state is inherently exposed to the user. Storing sensitive information in the frontend poses a significant security risk. ### 4.2. Standard: Sanitize Data Retrieved form the Backend * **Do This:** Sanitize and validate any data received from the backend before storing it in the state. This reduces potential XSS (Cross-Site Scripting) risks. * **Don't Do This:** Directly store unsanitized data in the state, which can expose your application to security vulnerabilities. **Why:** Sanitizing data before storing in state reduces the risk of stored cross-site scripting ## 5. Performance Optimization ### 5.1. Standard: Minimize State Size * **Do This:** Store only the data needed for the current view or component. Avoid storing large, unnecessary data structures in the global state. * **Don't Do This:** Store excessive amounts of data in the global state, which can increase memory usage and slow down performance. **Why:** Reducing the state size improves performance by minimizing memory usage and reducing the amount of data that needs to be processed on every state change. ### 5.2. Standard: Optimize Data Structures * **Do This:** Use efficient data structures (e.g., Maps, Sets) for storing and accessing data in the state. Consider using Immutable.js for immutable data structures. * **Don't Do This:** Rely on inefficient data structures (e.g., arrays for lookups) when performance is critical. **Why:** Efficient data structures can significantly improve performance, especially when dealing with large datasets. ## 6. Common Anti-Patterns ### 6.1. Anti-Pattern: Prop Drilling * **Avoid:** Passing props through multiple layers of components to reach a deeply nested component that needs the data. * **Instead:** Use state management libraries (e.g., Pinia) or Context API/Provide/Inject to make the data available directly to the component that needs it. ### 6.2. Anti-Pattern: Mutating State Directly * **Avoid:** Directly modifying objects or arrays stored in the state. * **Instead:** Create new copies of the data using the spread operator or other immutable update techniques. ### 6.3. Anti-Pattern: Over-reliance on Global State * **Avoid:** Storing everything in the global state. Many components will do perfectly well with "useState". * **Instead:** Carefully choose which state truly is global to the application. ### 6.4. Anti-pattern: Tight Coupling to Specific State Management Library * **Avoid** Design code that is heavily dependent on the specifics(classes / functions) of a particular library. * **Instead** Abstract usage behind interfaces or custom hooks (as appropriate to your framework). This reduces your risk when it's time to upgrade that dependency or swap to a different solution. ## Conclusion These state management standards provide a solid foundation for building maintainable, performant, and secure Vite applications. By following these guidelines, development teams can ensure code consistency and improve the overall quality of their projects.
# Performance Optimization Standards for Vite This document outlines coding standards specifically for performance optimization in Vite projects. These standards aim to improve application speed, responsiveness, and resource usage, leveraging Vite's unique features and capabilities. This guide incorporates best practices based on the latest Vite version. ## 1. Bundling and Code Splitting Strategies ### 1.1. Dynamic Imports and Route-Based Chunking **Standard:** Use dynamic imports ("import()") for lazy-loading components and route-based chunking to split the application into smaller, more manageable bundles. **Why:** Reduces initial load time by only loading the code required for the current view. Route-based chunking minimizes redundant code across routes. **Do This:** """javascript // src/router/index.js import { createRouter, createWebHistory } from 'vue-router'; const routes = [ { path: '/', name: 'Home', component: () => import(/* webpackChunkName: "home" */ '../views/Home.vue') }, { path: '/about', name: 'About', component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ]; const router = createRouter({ history: createWebHistory(), routes }); export default router; """ **Don't Do This:** """javascript // Avoid importing all components upfront import Home from '../views/Home.vue'; // Avoid this for larger components. const routes = [ { path: '/', name: 'Home', component: Home } ]; """ **Anti-Pattern:** Importing large components eagerly within main application files drastically increases initial bundle size. **Technology Specific:** Vite automatically leverages Rollup's code splitting capabilities when you use dynamic imports. "/* webpackChunkName: "home" */" is a magic comment Vite uses to name the generated chunk. ### 1.2. Vendor Chunking Optimization **Standard:** Ensure your "vite.config.js" optimizes vendor chunking to reduce duplication of dependencies. Use "manualChunks" to manually control chunk creation when needed. **Why:** Improves caching and reduces overall bundle sizes. **Do This:** """javascript // vite.config.js import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [vue()], build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { // Create a vendor chunk for all node_modules dependencies return 'vendor'; } } } } } }); """ **Don't Do This:** """javascript // Avoid letting Rollup determine chunking without guidance for large projects. export default defineConfig({ plugins: [vue()] // No manualChunks configuration - Can lead to suboptimal chunking. }); """ **Anti-Pattern:** Letting Rollup handle chunking automatically *can* work in some scenarios, BUT in larger projects, this often leads to inefficient chunk creation, where the dependency code gets duplicated across chunks. Using "manualChunks" strategy with carefully crafted rules gives you the *control* you need for *optimal* performance in larger projects. Common rules are to put all "node_modules" in one "vendor" chunk, which should not change very often. **Technology Specific:** Vite's "build.rollupOptions.output.manualChunks" configuration allows fine-grained control over how Rollup splits your code into chunks. ### 1.3. Minimizing Dependencies (Tree Shaking) **Standard:** Use ES modules and take advantage of tree-shaking to eliminate unused code from your dependencies. **Why:** Tree-shaking reduces the bundle size by excluding dead code, leading to faster load times. **Do This:** """javascript // my-utils.js export function usefulFunction() { console.log('This function is used.'); } export function unusedFunction() { console.log('This function is never called.'); } """ """javascript // main.js import { usefulFunction } from './my-utils.js'; usefulFunction(); // Only 'usefulFunction' will be included in the bundle. """ **Don't Do This:** """javascript // Avoid importing the entire module if you only need one function. import * as utils from './my-utils.js'; // Avoid this, may include unused code. utils.usefulFunction(); """ **Anti-Pattern:** Importing the entire module using "import * as" prevents tree-shaking, as the bundler cannot determine which parts of the module are actually used. **Technology Specific:** Vite relies on Rollup, which performs tree-shaking automatically when using ES modules. Ensure your dependencies also provide ES module versions for optimal tree-shaking. ## 2. Asset Optimization ### 2.1. Image Optimization **Standard:** Optimize images by compressing them without losing significant quality, using appropriate formats (WebP), and serving them at the correct size. **Why:** Smaller images load faster and consume less bandwidth, improving the user experience. **Do This:** * Use tools like ImageOptim, TinyPNG, or ShortPixel to compress images losslessy or lossly as appropriate. * Use the "<picture>" element to provide multiple image formats and sizes: """html <picture> <source srcset="img/my-image.webp" type="image/webp"> <img src="img/my-image.jpg" alt="My Image"> </picture> """ **Don't Do This:** * Uploading unoptimized, high-resolution images directly to your project. **Anti-Pattern:** Serving large, unoptimized images is a common performance bottleneck, especially on mobile devices. **Technology Specific:** Consider using Vite plugins like "vite-plugin-imagemin" to automate image optimization during the build process. Vite's "public" directory allows you to serve static assets directly. ### 2.2. Font Optimization **Standard:** Use web fonts sparingly, load them asynchronously, and use "font-display: swap" to prevent blocking rendering. **Why:** Web fonts can significantly impact page load time if not managed correctly. **Do This:** """css /* Load font asynchronously */ @font-face { font-family: 'MyFont'; src: url('/fonts/MyFont.woff2') format('woff2'); font-display: swap; /* Use swap to prevent blocking */ } body { font-family: 'MyFont', sans-serif; } """ **Don't Do This:** """css /* Avoid blocking rendering */ @font-face { font-family: 'MyFont'; src: url('/fonts/MyFont.woff2') format('woff2'); /* Avoid: font-display: block; */ } """ **Anti-Pattern:** Using "font-display: block" can cause a flash of invisible text (FOIT) while the font is loading which is very poor UX. "font-display: swap" mitigates this by displaying fallback text until the font is loaded and then replaces it. **Technology Specific:** Vite automatically optimizes asset URLs in CSS and JavaScript. Consider using font subsetting tools to reduce font file sizes further. ### 2.3. SVG Optimization **Standard:** Optimize SVGs by removing unnecessary metadata and attributes. Consider using SVGs inline (when small) or as symbols in a sprite. **Why:** Optimized SVGs are smaller and render faster than unoptimized ones. **Do This:** * Use tools like SVGO to optimize SVGs: """bash #Example Command Line Usage svgo my-icon.svg """ * Use inline SVGs or SVG sprites for small icons: """html <!-- Inline SVG --> <svg width="24" height="24" viewBox="0 0 24 24"> <path fill="currentColor" d="..."></path> </svg> <!-- SVG Sprite --> <svg> <use xlink:href="#my-icon"></use> </svg> """ **Don't Do This:** * Using raster images (PNG, JPEG) when SVGs would provide better quality and smaller file sizes. **Anti-Pattern:** Using complex SVGs that are not properly optimized slows down rendering. **Technology Specific:** Vite's asset handling treats SVGs as modules, allowing you to import them directly into your JavaScript code. Can be used with Vue Single File Components very effectively. ## 3. Component Optimization (Vue Specific) ### 3.1. Virtualization for Large Lists **Standard:** Use virtualization (e.g., "vue-virtual-scroller", "vue-virtual-list") for rendering very long lists to only render elements visible in the viewport. **Why:** Virtualization significantly reduces the number of DOM elements, improving rendering performance. **Do This:** """vue <template> <RecycleScroller class="scroller" :items="items" :item-size="30"> <template v-slot="{ item }"> <div class="item">{{ item.text }}</div> </template> </RecycleScroller> </template> <script setup> import { RecycleScroller } from 'vue-virtual-scroller'; import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'; import { ref } from 'vue'; const items = ref(Array.from({ length: 10000 }, (_, i) => ({ id: i, text: "Item ${i}" }))); </script> <style> .scroller { height: 300px; overflow: auto; } .item { height: 30px; line-height: 30px; border-bottom: 1px solid #eee; } </style> """ **Don't Do This:** """vue <!-- Avoid rendering large lists directly --> <template> <div v-for="item in items" :key="item.id" class="item">{{ item.text }}</div> </template> """ **Anti-Pattern:** Rendering thousands of elements with "v-for" directly in the DOM can lead to severe performance issues, making the UI unresponsive. **Technology Specific:** Vue's reactivity system makes it efficient to update virtualized lists as the user scrolls. ### 3.2. Memoization and Computed Properties **Standard:** Use "computed" properties and "memoization" (e.g., "useMemo" or "computed" with dependency tracking) to avoid unnecessary re-renders in Vue components. **Why:** Computed properties are cached, preventing expensive calculations from being re-executed unless their dependencies change. **Do This:** """vue <template> <p>Result: {{ expensiveCalculation }}</p> </template> <script setup> import { ref, computed } from 'vue'; const input = ref(''); const expensiveCalculation = computed(() => { //Perform expensive calculation on input change console.log("Expensive calc running!"); //Only runs when input changes let result = input.value.split('').reverse().join(''); return result; }); </script> """ **Don't Do This:** """vue <template> <p>Result: {{ performExpensiveCalculation() }}</p> </template> <script setup> import { ref } from 'vue'; const input = ref(''); const performExpensiveCalculation = () => { //This function will re-run every render (bad for perf) console.log("Expensive calc running!"); let result = input.value.split('').reverse().join(''); return result; }; </script> """ **Anti-Pattern:** Directly executing expensive functions in the template will cause them to re-run every time the component re-renders, leading to performance issues. **Technology Specific:** Vue's "computed" properties offer a built-in memoization mechanism. You can use dependency tracking for granular control over when computed properties are re-evaluated. ### 3.3. "v-once" Directive for Static Content **Standard:** Use the "v-once" directive for parts of your Vue templates that contain static content that will never change. **Why:** "v-once" tells Vue to render the element/component only once and then skip future updates. **Do This:** """vue <template> <div v-once> <h1>Static Title</h1> <p>This content will never change.</p> </div> </template> """ **Don't Do This:** """vue <template> <div> <h1>Dynamic Title : {{ dynamicTitle }}</h1> <p>This content changes.</p> </div> </template> """ **Anti-Pattern:** Using "v-once" on dynamic content will prevent it from updating correctly which is obviously wrong. Only apply it to purely static sections of your template. **Technology Specific:** Vue's rendering engine will skip the diffing and patching for elements/components marked with "v-once". ### 3.4. Properly Keyed v-for Loops **Standard:** Always use a unique and stable "key" attribute when using "v-for", especially when the list is subject to mutations like adding, removing, or reordering items. **Why:** Keys help Vue track the identity of each node, allowing it to efficiently update the DOM when the list changes. Using the index as a key can lead to problems with re-rendering the items in unusual orders. **Do This:** """vue <template> <ul> <li v-for="item in items" :key="item.id">{{ item.text }}</li> </ul> </template> <script setup> import { ref } from 'vue'; const items = ref([ { id: 'a', text: 'Item A' }, { id: 'b', text: 'Item B' }, ]); </script> """ **Don't Do This:** """vue <template> <ul> <li v-for="(item, index) in items" :key="index">{{ item.text }}</li> </ul> </template> """ **Anti-Pattern:** Using the index as the "key" is an anti-pattern is because Vue may incorrectly reuse existing DOM elements for different list items if the order changes - This can lead to incorrect rendering and lost component state. Using a unique ID for key is critical in maintaining accurate performance. **Technology Specific:** Vue's virtual DOM uses the "key" attribute to optimize DOM updates. If keys are missing or incorrect, Vue may have to re-render entire list instead of just updating necessary elements. ## 4. Network Optimization ### 4.1. Caching Strategies (Browser and CDN) **Standard:** Implement aggressive browser caching using appropriate "Cache-Control" headers and leverage CDNs for static assets. **Why:** Caching reduces the number of requests to the server, improving load times, reducing bandwidth usage, and improving scalability. **Do This:** * Configure your server or CDN to set appropriate "Cache-Control" headers: """ Cache-Control: public, max-age=31536000 // 1 year for immutable assets Cache-Control: no-cache, must-revalidate //For assets that change often """ * Use a CDN like Cloudflare, AWS CloudFront, or Fastly to serve static assets. **Don't Do This:** * Setting short cache expiry times or disabling caching altogether for static assets. **Anti-Pattern:** Failing to leverage browser and CDN caching increases server load and slows down the application for returning users. **Technology Specific:** Vite's build process generates hashed filenames for assets, enabling long-term caching. Modern CDNs can intelligently cache and deliver content based on the user's location. ### 4.2. Resource Hints (Preload, Prefetch) **Standard:** Use resource hints like "<link rel="preload">" and "<link rel="prefetch">" to prioritize loading of critical assets and fetch resources that will be needed later. **Why:** Resource hints improve perceived performance by loading critical resources early and speeding up future navigation. **Do This:** """html <head> <link rel="preload" href="/fonts/MyFont.woff2" as="font" type="font/woff2" crossorigin> <link rel="prefetch" href="/components/MyComponent.vue" as="script"> </head> """ **Don't Do This:** * Overusing "preload" or "prefetch" can lead to unnecessary requests and negatively impact performance. Only use them strategically for specific resources. **Anti-Pattern:** Not using resource hints for critical resources causes delays in rendering and navigation. **Technology Specific:** Vite's plugin ecosystem may offer plugins that automatically inject resource hints based on your application's dependency graph. ### 4.3. Gzip or Brotli Compression **Standard:** Enable Gzip or Brotli compression on your web server to reduce the size of text-based assets (HTML, CSS, JavaScript). **Why:** Compression significantly reduces the size of responses, leading to faster download times and reduced bandwidth consumption. Brotli offers better compression ratios than Gzip. **Do This:** * Configure your web server (e.g., Nginx, Apache, Node.js) to enable Gzip or Brotli compression. * Example Nginx Configuration: """nginx gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/rss+xml application/atom+xml image/svg+xml; """ * Also consider setting the "Content-Encoding" HTTP header properly to let clients know that the resources are compressed. **Don't Do This:** * Serving uncompressed text-based assets. * Not configuring your server to correctly notify clients that they have been compressed. **Anti-Pattern:** Sending large uncompressed text files over the network is a significant waste of bandwidth and slows down page load times. **Technology Specific:** Vite's build process can be configured to generate pre-compressed versions of your assets (e.g., ".gz", ".br") for your server to serve directly. ## 5. Runtime Optimization ### 5.1. Debouncing and Throttling **Standard:** Implement debouncing and throttling techniques to limit the rate at which event handlers are executed, improving performance in scenarios like search input and window resizing. **Why:** Prevents excessive function calls that can degrade performance and responsiveness. **Do This:** """javascript // Debouncing Example function debounce(func, delay) { let timeout; return function(...args) { const context = this; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), delay); }; } window.addEventListener('resize', debounce(() => { // Perform expensive operation after resizing is complete console.log('Resized!'); }, 250)); //Throttling example function throttle (func, limit) { let inThrottle return function() { const args = arguments const context = this if (!inThrottle) { func.apply(context, args) inThrottle = true setTimeout(() => inThrottle = false, limit) } } } window.addEventListener('scroll', throttle(()=>{ console.log("Scrolling - throttled.") }, 500)); """ **Don't Do This:** * Executing expensive operations directly within event handlers without any rate limiting mechanisms. **Anti-Pattern:** Excessive function calls on event handlers can overwhelm the browser's resources and cause performance issues. **Technology Specific:** Libraries like Lodash provide utility functions for debouncing and throttling. Vue's reactivity system can be used to efficiently manage debounced or throttled values. ### 5.2. Web Workers for Background Tasks **Standard:** Offload CPU-intensive tasks to Web Workers to prevent blocking the main thread and maintain UI responsiveness. **Why:** Web Workers run in a separate thread, allowing you to perform calculations or data processing in the background without freezing the UI. **Do This:** """javascript // Create a Web Worker const worker = new Worker(new URL('./my-worker.js', import.meta.url)); worker.postMessage({ data: 'some data' }); worker.onmessage = (event) => { console.log('Received from worker:', event.data); }; // my-worker.js self.onmessage = (event) => { const data = event.data; // Perform heavy computation const result = someHeavyComputation(data); self.postMessage(result); }; """ Remember to use a build tool like Vite to properly bundle and serve the worker file. **Don't Do This:** *Performing long-running or computationally intensive tasks directly on the main thread.* **Anti-Pattern:** Blocking the main thread with CPU-bound tasks results in a janky and unresponsive UI. **Technology Specific:** Vite supports Web Workers out of the box. You can import Web Worker scripts using "new Worker(new URL('./my-worker.js', import.meta.url))". Ensure that your Web Worker code adheres to the same coding standards as your main application code. By adhering to these standards, development teams can create high-performance Vite applications that provide a smooth and responsive user experience.
# Code Style and Conventions Standards for Vite This document outlines the coding style and conventions to follow when developing with Vite. Adhering to these standards will improve code readability, maintainability, and consistency across projects. These guidelines are designed to be compatible with AI coding assistants such as GitHub Copilot and Cursor. ## 1. General Principles ### 1.1. Consistency * **Do This:** Maintain a consistent style throughout the codebase. Use tools like Prettier and ESLint to enforce these standards automatically. * **Don't Do This:** Mix different coding styles within the same project, as this reduces readability and increases cognitive load. * **Why:** Consistency is key to making code easier to understand and maintain. A predictable style allows developers to quickly grasp the structure and intent of the code. ### 1.2. Readability * **Do This:** Write code that is easy to read and understand. Use meaningful variable names, clear comments, and proper indentation. * **Don't Do This:** Write overly complex or convoluted code that is difficult to decipher. * **Why:** Readability is crucial for collaboration and long-term maintainability. Code should be self-explanatory and easy for others (and your future self) to understand. ### 1.3. Simplicity * **Do This:** Keep code simple and focused. Break down complex tasks into smaller, manageable functions or modules. * **Don't Do This:** Write large, monolithic functions or modules that are hard to test and maintain. * **Why:** Simpler code is easier to understand, test, and debug. It reduces the risk of introducing bugs and makes it easier to refactor when necessary. ## 2. Formatting ### 2.1. Indentation * **Do This:** Use 2 spaces for indentation. * **Don't Do This:** Use tabs or a combination of tabs and spaces. * **Why:** Two spaces provide a good balance between readability and code density. """javascript // Correct indentation function myFunction() { console.log('Hello, world!'); } // Incorrect indentation function myFunction() { console.log('Hello, world!'); } """ ### 2.2. Line Length * **Do This:** Limit lines to a maximum of 120 characters. * **Don't Do This:** Exceed the recommended line length. * **Why:** Shorter lines are easier to read and prevent horizontal scrolling. ### 2.3. Whitespace * **Do This:** Use whitespace to improve readability, including: * A blank line after each function or method. * Spaces around operators (=, +, -, *, /, etc.). * Spaces after commas in lists. * **Don't Do This:** Omit whitespace or use excessive whitespace. * **Why:** Proper whitespace enhances readability by visually separating code elements. """javascript // Correct usage of whitespace function add(a, b) { return a + b; } const result = add(1, 2); console.log(result); // Incorrect usage of whitespace function add(a,b){ return a+b; } const result=add(1,2); console.log( result ); """ ### 2.4. Semicolons * **Do This:** Always use semicolons at the end of statements. * **Don't Do This:** Rely on automatic semicolon insertion (ASI). * **Why:** Using semicolons explicitly avoids unexpected behavior due to ASI and improves code predictability. """javascript // Correct usage of semicolons const message = 'Hello, world!'; console.log(message); // Incorrect usage (relying on ASI) const message = 'Hello, world!' console.log(message) """ ### 2.5. Trailing Commas * **Do This:** Use trailing commas in object literals, arrays, and function parameters. * **Don't Do This:** Omit trailing commas. * **Why:** Trailing commas simplify adding, removing, or reordering elements. They also lead to cleaner diffs in version control. """javascript // Correct usage of trailing commas const person = { firstName: 'John', lastName: 'Doe', }; const numbers = [ 1, 2, 3, ]; function greet( name, greeting, ) { console.log("${greeting}, ${name}!"); } // Incorrect usage const person = { firstName: 'John', lastName: 'Doe' }; """ ## 3. Naming Conventions ### 3.1. Variables * **Do This:** Use descriptive, camelCase names. * **Don't Do This:** Use single-letter names or cryptic abbreviations. * **Why:** Clear variable names make code easier to understand. """javascript // Correct variable names const userFirstName = 'John'; const numberOfItems = 10; // Incorrect variable names const fn = 'John'; const noi = 10; """ ### 3.2. Functions * **Do This:** Use descriptive, camelCase names that clearly indicate the function's purpose. * **Don't Do This:** Use ambiguous or overly short names. * **Why:** Explicit function names improve code readability and maintainability. """javascript // Correct function names function calculateTotalAmount(price, quantity) { return price * quantity; } // Incorrect function names function calc(p, q) { return p * q; } """ ### 3.3. Constants * **Do This:** Use uppercase, snake_case names for constants. * **Don't Do This:** Use lowercase or camelCase names for constants. * **Why:** Uppercase names clearly indicate that the variable is a constant and should not be modified. """javascript // Correct constant names const MAX_USERS = 100; const API_URL = 'https://example.com/api'; // Incorrect constant names const maxUsers = 100; const apiUrl = 'https://example.com/api'; """ ### 3.4. Components (Vue/React with Vite) * **Do This:** Use PascalCase names for components. File names should match the component name. * **Don't Do This:** Use kebab-case or camelCase names. * **Why:** PascalCase is the standard convention for components in both Vue and React, enhancing consistency. """jsx // Correct component name and file name (e.g., MyComponent.jsx) function MyComponent() { return <div>My Component</div>; } export default MyComponent; // Incorrect component name function myComponent() { return <div>My Component</div>; } """ ### 3.5. File and Directory Structure * **Do This:** Structure your project logically, separating concerns into different directories (e.g., "components", "utils", "api"). Use descriptive file names following the component or module naming conventions. * **Don't Do This:** Dump all files into a single directory or use cryptic file names. * **Why:** A well-organized file structure makes it easier to navigate and maintain the project. """ /src ├── components/ │ ├── MyComponent.jsx │ ├── AnotherComponent.jsx ├── utils/ │ ├── api.js │ ├── helpers.js ├── App.jsx ├── main.jsx """ ## 4. Vite-Specific Conventions ### 4.1. Environment Variables * **Do This:** Use ".env" files for environment variables and access them via "import.meta.env". * **Don't Do This:** Hardcode sensitive information directly into the code. * **Why:** Environment variables allow you to configure your application for different environments (e.g., development, production) without modifying the code. """javascript // .env file VITE_API_URL=https://api.example.com // In your component or module const apiUrl = import.meta.env.VITE_API_URL; console.log(apiUrl); """ ### 4.2. Asset Imports * **Do This:** Import assets (images, fonts, etc.) directly using their relative paths. Vite handles these imports efficiently. * **Don't Do This:** Use absolute paths or manual asset management. * **Why:** Vite automatically optimizes and bundles imported assets, providing better performance and simplified asset management. """javascript // Correct asset import import logo from './assets/logo.svg'; function MyComponent() { return <img src={logo} alt="Logo" />; } export default MyComponent; """ ### 4.3. Dynamic Imports * **Do This:** Use dynamic imports ("import()") for code splitting, especially for large components or modules that are not immediately needed. * **Don't Do This:** Load all code upfront, which can slow down initial page load. * **Why:** Dynamic imports allow you to load code on demand, improving performance by reducing the initial bundle size. """javascript // Dynamic import async function loadComponent() { const { default: MyComponent } = await import('./components/MyComponent'); // Use MyComponent } loadComponent(); """ ### 4.4. Vite Plugins * **Do This:** Utilize Vite plugins for common tasks like transforming code, optimizing assets, and integrating with other tools. Configure plugins in "vite.config.js". * **Don't Do This:** Implement complex build processes manually when a plugin can handle it. * **Why:** Vite plugins extend Vite's functionality and streamline the build process. """javascript // vite.config.js import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [vue()], }); """ ### 4.5. Development Server * **Do This:** Leverage Vite's built-in development server for fast hot module replacement (HMR) during development. Take advantage of features like proxying API requests. * **Don't Do This:** Use a separate development server that doesn't integrate with Vite's build pipeline. * **Why:** Vite's development server provides a superior development experience with fast updates and seamless integration. """javascript // vite.config.js (proxy example) import { defineConfig } from 'vite'; export default defineConfig({ server: { proxy: { '/api': { target: 'https://api.example.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } }); """ ### 4.6. Build Configuration * **Do This:** Configure the build process in "vite.config.js" to optimize the output for production. Use features like code minification, tree shaking, and asset inlining. * **Don't Do This:** Use default build settings without considering production requirements. * **Why:** Proper build configuration is essential for optimizing performance and reducing the size of the production bundle. Consider using the "defineConfig" helper for type safety. """javascript // vite.config.js import { defineConfig } from 'vite'; import { terser } from 'rollup-plugin-terser'; export default defineConfig({ build: { minify: 'terser', // applies minification rollupOptions: { plugins: [terser()], // configures minification with terser }, }, }); """ ### 4.7. Server-Side Rendering (SSR) * **Do This:** If using SSR, structure your code to support both server and client environments. Use "process.server" (or equivalent with Vite SSR plugins) to conditionally execute server-specific code. Configure Vite for SSR builds. * **Don't Do This:** Write code that only works on one environment. * **Why:** Supporting both environments is crucial for SSR applications. """javascript // Example of conditional code execution if (import.meta.env.SSR) { // Server-side code console.log('Running on the server'); } else { // Client-side code console.log('Running on the client'); } """ ### 4.8 Lazy Loading Images * **Do This:** Implement Lazy Loading for images and other media to improve page load performance. * **Don't Do This:** Load all assets upfront, especially for content below the fold. * **Why:** Lazy loading improves initial load time by only loading visible assets. """html <img src="placeholder.jpg" data-src="actual-image.jpg" alt="My Image" loading="lazy"> """ ### 4.9 Minimize Third Party Libraries * **Do This:** Evaluate and minimize usage of third party libraries, choosing the smallest and most efficient libraries when necessary. * **Don't Do This:** Add numerous large libraries without considering their impact on performance. * **Why:** Reduces bundle size and dependencies, leading to faster load times and improved security. ## 5. Stylistic Consistency ### 5.1. Quotes * **Do This:** Use single quotes ("'") for strings, unless you need to embed variables. * **Don't Do This:** Use double quotes (""") unnecessarily. * **Why:** Single quotes are more common in JavaScript and can be slightly more efficient. """javascript // Correct usage of single quotes const message = 'Hello, world!'; // Use template literals for variable embedding const name = 'John'; const greeting = "Hello, ${name}!"; """ ### 5.2. Object Literals * **Do This:** Use concise syntax for object literals. * **Don't Do This:** Use verbose syntax unnecessarily. * **Why:** Concise syntax makes code cleaner and easier to read. """javascript // Correct concise syntax const person = { firstName: 'John', lastName: 'Doe', }; // Incorrect verbose syntax const person = { firstName: person.firstName, lastName: person.lastName }; """ ### 5.3. Arrow Functions * **Do This:** Use arrow functions ("=>") for concise, simple functions, especially when using higher-order functions like "map" and "filter". * **Don't Do This:** Use arrow functions for complex functions with multiple statements; use traditional function declarations for those. * **Why:** Arrow functions provide a more compact syntax and automatically bind "this" to the surrounding context, making them ideal for simple callbacks and expressions. """javascript // Correct usage of arrow functions const numbers = [1, 2, 3]; const squaredNumbers = numbers.map(number => number * number); // Use traditional function declaration for complex functions function calculateTotal(items) { let total = 0; for (const item of items) { total += item.price * item.quantity; } return total; } """ ### 5.4. Ternary Operators * **Do This:** Use ternary operators ("condition ? value1 : value2") for simple conditional assignments or expressions. * **Don't Do This:** Use ternary operators for complex logic or multiple nested conditions. * **Why:** Ternary operators provide a concise way to express simple conditional logic. """javascript // Correct usage of ternary operator const age = 20; const isAdult = age >= 18 ? true : false; // Avoid for complex logic const result = (a > b) ? (a > c ? a : c) : (b > c ? b : c); // Hard to read! """ ## 6. Error Handling ### 6.1. Try-Catch Blocks * **Do This:** Use "try-catch" blocks to handle potential errors in asynchronous operations, like API calls or dynamic imports. * **Don't Do This:** Ignore potential errors or rely solely on unhandled rejections. * **Why:** Proper error handling prevents unexpected crashes and provides a better user experience. """javascript // Correct error handling async function fetchData() { try { const response = await fetch('https://example.com/api/data'); const data = await response.json(); return data; } catch (error) { console.error('Error fetching data:', error); // Handle the error (e.g., display an error message to the user) } } fetchData(); """ ### 6.2. Error Logging * **Do This:** Log errors with sufficient context to help with debugging. Include relevant information like the component name, function name, and error message. * **Don't Do This:** Log errors without any context, making it difficult to troubleshoot. * **Why:** Detailed error logs are essential for diagnosing and fixing issues in production. ## 7. Comments and Documentation ### 7.1. Code Comments * **Do This:** Write clear and concise comments to explain complex logic, non-obvious code, or important decisions. * **Don't Do This:** Comment obvious code or write comments that are outdated or misleading. * **Why:** Comments should provide additional context and help developers understand the "why" behind the code. """javascript // This function calculates the total price of items in the cart function calculateTotalPrice(cartItems) { // ... } """ ### 7.2. JSDoc * **Do This:** Use JSDoc syntax to document functions, classes, and modules. * **Don't Do This:** Omit documentation for important parts of the codebase. * **Why:** JSDoc-style comments enable tools to generate API documentation and provide better code completion and type checking. """javascript /** * Greets a person with a given name. * * @param {string} name - The name of the person to greet. * @returns {string} A greeting message. */ function greet(name) { return "Hello, ${name}!"; } """ ## 8. Security ### 8.1 Input Validation * **Do This:** Validate all user inputs on both the client and server-side and sanitize potentially malicious data. * **Don't Do This:** Trust the client-side data without validation. * **Why:** Prevents XSS attacks, SQL injection, and other security vulnerabilities. ### 8.2 Avoid Hardcoding Sensitive Data * **Do This:** Use environment variables to store any keys or tokens that should not be exposed. * **Don't Do This:** Hard code API keys, passwords, or other sensitive information in the application code, risking exposure. * **Why:** Keeps private credentials from being exposed in the code repository. ### 8.3 Dependency Management * **Do This:** Regularly review and update project dependencies to patch security vulnerabilities as soon as they are released. Use tools like "npm audit" or "yarn audit". * **Don't Do This:** Use outdated dependencies without security patches. * **Why:** Prevents usage of compromised packages that may introduce security risks. ## 9. Continuous Integration/Continuous Deployment (CI/CD) ### 9.1. Automated Linting and Formatting * **Do This:** Integrate linting and formatting tools (e.g., ESLint, Prettier) into your CI/CD pipeline to automatically enforce coding standards on every commit. * **Don't Do This:** Rely solely on manual code reviews to catch style issues. * **Why:** Automated linting and formatting ensures consistent code style across the entire team, reduces the burden on code reviewers, and minimizes the risk of style-related bugs. ### 9.2. Automated Testing * **Do This:** Set up automated unit, integration, and end-to-end tests in your CI/CD pipeline to catch regressions and ensure code quality. * **Don't Do This:** Skip automated testing or rely solely on manual testing. * **Why:** Automated testing provides confidence in the codebase, accelerates development, and reduces the risk of introducing bugs into production. ## 10. Deprecated Features and Anti-Patterns in Vite ### 10.1. Legacy "require()" statements * **Avoid:** Using Node.js style "require()" statements in favor of modern ESM "import" statements. * **Why:** "import" statements are more efficient for tree shaking and produce smaller bundles. Also, mixing module systems can increase complexity. """javascript // Anti-pattern (CommonJS) const fs = require('fs'); // Preferred ESM syntax import fs from 'fs'; """ ### 10.2 Global Scope Pollution * **Avoid:** Adding variables and functions directly to the global scope (e.g., "window" in browsers). * **Why:** Can lead to unexpected conflicts if another script declares a variable with the same name. """javascript // Anti-pattern: Global scope pollution window.appVersion = '1.2.3'; // Avoid this // Preferred: Export in a module or use a dedicated object const appConfig = { version: '1.2.3', }; export default appConfig; """ ### Example Vite Configuration (vite.config.js): This is a comprehensive "vite.config.js" example incorporating many of the best practices mentioned above. """javascript import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import { terser } from 'rollup-plugin-terser'; import { resolve } from 'path'; const API_TARGET = process.env.API_TARGET || 'http://localhost:8000'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': resolve(__dirname, 'src'), // Alias for easy imports }, }, define: { __APP_VERSION__: JSON.stringify(process.env.npm_package_version), // Expose version }, server: { hmr: true, // Enable hot module replacement proxy: { '/api': { target: API_TARGET, changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), }, }, }, build: { minify: 'terser', // Enable minification sourcemap: true, // Generate sourcemaps for debugging rollupOptions: { plugins: [terser()], output: { chunkFileNames: 'js/[name]-[hash].js', // Chunk file naming entryFileNames: 'js/[name]-[hash].js', // Entry file naming assetFileNames: '[ext]/[name]-[hash].[ext]', // Asset file naming }, manualChunks(id) { // Vendor splitting if (id.includes('node_modules')) { return id .toString() .split('node_modules/')[1] .split('/')[0] .toString(); } }, }, //Target latest browsers - updated for performance target: 'esnext', // Target modern browser for smaller bundles // Disable brotliSize reporting if you do not use Brotli brotliSize: false, manifest: true, // Generate manifest file }, optimizeDeps: { esbuildOptions: { // Node.js polyfills for older browsers may be required // Define global used in your project define: { global: 'globalThis' }, // Enable/disable browser specific transform target: "esnext", } } }); """ This document provides a comprehensive guide to coding style and conventions for Vite projects and is designed to be integrated with AI coding assistance tools. Continuously reviewing and adapting these standards as Vite evolves is crucial to maintaining a high-quality codebase.