File organization structure
Basic Principles of File Organization
The file organization structure of a JavaScript project directly impacts the maintainability and readability of the code. A good file structure should follow modular principles, grouping related functionalities together while maintaining a reasonable level of depth. A typical project structure usually includes the following core directories:
src/
- Contains source codepublic/
- Contains static resourcestests/
- Contains test codeconfig/
- Contains configuration files
// Example: Basic project structure
project-root/
├── src/
│ ├── components/
│ ├── utils/
│ ├── styles/
│ └── index.js
├── public/
│ ├── index.html
│ └── favicon.ico
├── tests/
│ ├── unit/
│ └── integration/
└── package.json
Organizing Files by Feature
Feature-based organization (Functional Organization) groups files according to their role in the application. This structure is particularly suitable for small to medium-sized projects, where each feature module contains all its related files:
src/
├── auth/
│ ├── AuthService.js
│ ├── LoginForm.jsx
│ ├── RegisterForm.jsx
│ └── auth.test.js
├── products/
│ ├── ProductList.jsx
│ ├── ProductCard.jsx
│ ├── ProductApi.js
│ └── products.test.js
└── shared/
├── Button.jsx
└── Input.jsx
// Example: Internal structure of a feature module
// src/products/ProductApi.js
export const fetchProducts = async () => {
const response = await fetch('/api/products');
return response.json();
};
// src/products/ProductList.jsx
import { fetchProducts } from './ProductApi';
const ProductList = () => {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchProducts().then(setProducts);
}, []);
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
};
Organizing Files by Type
Type-based organization (Type-based Organization) groups files according to their file type. This structure is more common in large projects, especially when multiple technology stacks are used:
src/
├── components/
│ ├── Button.jsx
│ ├── Modal.jsx
│ └── Input.jsx
├── containers/
│ ├── App.jsx
│ └── MainLayout.jsx
├── services/
│ ├── ApiService.js
│ └── AuthService.js
├── hooks/
│ ├── useFetch.js
│ └── useForm.js
└── utils/
├── helpers.js
└── constants.js
// Example: Component usage in type-based grouping
// src/components/Button.jsx
const Button = ({ children, onClick }) => (
<button className="btn" onClick={onClick}>
{children}
</button>
);
// src/containers/MainLayout.jsx
import Button from '../components/Button';
const MainLayout = () => {
return (
<div>
<header>
<Button onClick={() => console.log('Clicked')}>
Menu
</Button>
</header>
</div>
);
};
Hybrid Organization Structure
In practice, projects often adopt a hybrid organization structure, combining the advantages of feature-based and type-based approaches. A common practice is to divide files by type at the top level and by feature within specific directories:
src/
├── components/
│ ├── common/
│ │ ├── Button.jsx
│ │ └── Input.jsx
│ └── features/
│ ├── auth/
│ │ ├── LoginForm.jsx
│ │ └── RegisterForm.jsx
│ └── products/
│ ├── ProductList.jsx
│ └── ProductCard.jsx
├── pages/
│ ├── HomePage.jsx
│ └── ProductPage.jsx
└── services/
├── ProductService.js
└── AuthService.js
Location of Test Files
There are two main patterns for organizing test files:
- Centralized: All test files are placed in a separate
tests
directory - Distributed: Test files are placed in the same directory as their corresponding implementation files
// Example of distributed test files
src/
├── components/
│ ├── Button.jsx
│ ├── Button.test.jsx
│ ├── Input.jsx
│ └── Input.test.jsx
└── services/
├── ApiService.js
└── ApiService.test.js
// Example of centralized test files
tests/
├── unit/
│ ├── Button.test.jsx
│ └── ApiService.test.js
└── integration/
├── authFlow.test.js
└── checkoutFlow.test.js
Organization of Style Files
The organization of style files also requires consideration. Common patterns include:
- CSS Modules: Each component has its own style file
- Global Styles: Shared styles are placed in a separate directory
- CSS-in-JS: Styles are written directly in the component files
// Example of CSS module structure
src/
├── components/
│ ├── Button/
│ │ ├── index.jsx
│ │ └── styles.module.css
│ └── Input/
│ ├── index.jsx
│ └── styles.module.css
└── styles/
├── base.css
├── variables.css
└── utilities.css
Management of Configuration Files
Project configuration files should be centrally managed to avoid scattering them throughout the project. Typical configurations include:
- Webpack/Babel configurations
- ESLint/Prettier configurations
- Environment variable configurations
- Project-specific configurations
config/
├── webpack/
│ ├── webpack.common.js
│ ├── webpack.dev.js
│ └── webpack.prod.js
├── jest.config.js
├── .eslintrc.js
└── .prettierrc.js
Management of Static Resources
Static resources such as images and fonts should be managed uniformly, typically placed in the public
or assets
directory:
public/
├── images/
│ ├── logo.png
│ └── background.jpg
├── fonts/
│ ├── roboto.woff
│ └── roboto.woff2
└── favicon.ico
// Example of static resource reference
function Header() {
return (
<header>
<img src="/images/logo.png" alt="Company Logo" />
</header>
);
}
Encapsulation of Third-Party Libraries
Encapsulation of third-party libraries should be centrally managed to avoid direct usage in the project:
// src/lib/axios.js
import axios from 'axios';
const instance = axios.create({
baseURL: process.env.API_BASE_URL,
timeout: 10000,
});
export default instance;
// Using the encapsulated axios in other files
import http from '../lib/axios';
const fetchUser = async (id) => {
const response = await http.get(`/users/${id}`);
return response.data;
};
Management of Type Definition Files
In TypeScript projects, the organization of type definition files is important:
src/
├── types/
│ ├── user.d.ts
│ ├── product.d.ts
│ └── api.d.ts
└── interfaces/
├── IUser.ts
└── IProduct.ts
// src/types/user.d.ts
interface User {
id: string;
name: string;
email: string;
}
// src/components/UserProfile.tsx
import { User } from '../types/user';
const UserProfile: React.FC<{ user: User }> = ({ user }) => {
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
};
Organization of Utility Functions
Utility functions should be categorized by functionality to avoid creating overly large utility files:
// src/utils/
├── arrayUtils.js
├── dateUtils.js
├── stringUtils.js
└── domUtils.js
// src/utils/dateUtils.js
export const formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
export const daysBetween = (date1, date2) => {
return Math.abs(date1 - date2) / (1000 * 60 * 60 * 24);
};
Organization of Route Configurations
In single-page applications, route configurations should be centrally managed:
// src/routes/
├── index.js
├── routes.js
└── PrivateRoute.js
// src/routes/routes.js
import { lazy } from 'react';
const Home = lazy(() => import('../pages/Home'));
const Products = lazy(() => import('../pages/Products'));
const routes = [
{
path: '/',
component: Home,
exact: true,
},
{
path: '/products',
component: Products,
},
];
export default routes;
Organization of State Management
State management code should be separated from components, especially when using Redux or similar libraries:
src/
├── store/
│ ├── actions/
│ │ ├── userActions.js
│ │ └── productActions.js
│ ├── reducers/
│ │ ├── userReducer.js
│ │ └── productReducer.js
│ ├── selectors/
│ │ ├── userSelectors.js
│ │ └── productSelectors.js
│ └── store.js
└── features/
├── user/
│ ├── userSlice.js
│ └── userApi.js
└── products/
├── productSlice.js
└── productApi.js
// Redux Toolkit example
// src/features/user/userSlice.js
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'user',
initialState: null,
reducers: {
setUser: (state, action) => action.payload,
clearUser: () => null,
},
});
export const { setUser, clearUser } = userSlice.actions;
export default userSlice.reducer;
Environment-Specific Files
Configuration for different environments should be managed through environment variables rather than creating multiple files:
// config.js
const config = {
development: {
apiUrl: 'http://localhost:3000/api',
},
production: {
apiUrl: 'https://api.example.com',
},
};
export default config[process.env.NODE_ENV || 'development'];
Organization of Documentation Files
Project documentation should be uniformly managed, including component documentation, API documentation, etc.:
docs/
├── components/
│ ├── Button.md
│ └── Input.md
├── api/
│ ├── auth.md
│ └── products.md
└── README.md
Consistency in Naming Conventions
File naming should follow consistent conventions. Common ones include:
PascalCase
: For React components, e.g.,Button.jsx
camelCase
: For utility functions, e.g.,formatDate.js
kebab-case
: For HTML files, e.g.,user-profile.html
// Good naming examples
components/
├── UserProfile.jsx
├── product-list.jsx
└── utils/
├── formatDate.js
└── dom-helpers.js
Special Considerations for Large Projects
For large projects, a more complex organizational structure may be needed:
src/
├── core/ # Core framework code
├── modules/ # Business modules
│ ├── module1/
│ └── module2/
├── platform/ # Platform-specific code
│ ├── web/
│ └── mobile/
└── shared/ # Shared code
├── components/
└── utils/
Automation Tools Assistance
Use tools to automatically maintain file structure, for example:
// Using Plop to generate component templates
// plopfile.js
module.exports = function(plop) {
plop.setGenerator('component', {
description: 'Create a new component',
prompts: [{
type: 'input',
name: 'name',
message: 'Component name?'
}],
actions: [{
type: 'add',
path: 'src/components/{{pascalCase name}}/index.jsx',
templateFile: 'plop-templates/component.hbs'
}]
});
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn