← Назад к вопросам
Как взаимодействовать с модулем не находясь внутри него?
2.0 Middle🔥 161 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как взаимодействовать с модулем не находясь внутри него
Взаимодействие с модулем извне - это фундаментальная концепция модульной архитектуры. Это позволяет компонентам сотрудничать, не нарушая инкапсуляции и разделения ответственности.
1. Экспорт/Импорт модулей (ES6)
Основной механизм:
// math.js - модуль
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// app.js - используем модуль
import { add, subtract } from './math.js';
const result = add(5, 3); // 8
Модуль открывает public API через экспорты.
2. Named Exports (Именованные экспорты)
// user.service.js
export function getUser(id) {
return fetch(`/api/users/${id}`).then(r => r.json());
}
export function updateUser(id, data) {
return fetch(`/api/users/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
});
}
// component.jsx
import { getUser, updateUser } from './user.service';
function UserProfile({ userId }) {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
getUser(userId).then(setUser);
}, [userId]);
const handleUpdate = (data) => {
updateUser(userId, data).then(setUser);
};
return <div>{user?.name}</div>;
}
3. Default Export (Стандартный экспорт)
// Button.jsx
export default function Button({ children, onClick }) {
return <button onClick={onClick}>{children}</button>;
}
// app.jsx
import Button from './Button';
function App() {
return <Button onClick={() => alert('Clicked')}>Click me</Button>;
}
4. Инкапсуляция с Closure
// counter.js - приватные данные
const Counter = (() => {
let count = 0; // Приватная переменная
return {
increment() {
return ++count;
},
decrement() {
return --count;
},
getCount() {
return count;
}
};
})();
// app.js - используем public методы
Counter.increment(); // 1
Counter.increment(); // 2
Counter.getCount(); // 2
// Counter.count недоступен!
5. Dependency Injection
// logger.js - модуль, требующий зависимость
function createLogger(storage) {
return {
log(message) {
storage.save(message);
}
};
}
// app.js - внедряем зависимость
const fileStorage = {
save(data) {
console.log('Saving to file:', data);
}
};
const logger = createLogger(fileStorage);
logger.log('Error occurred'); // Saving to file: Error occurred
6. Pub/Sub паттерн (Event Emitter)
// eventBus.js - отдельный модуль для коммуникации
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(data));
}
}
}
export default new EventBus();
// Module A - отправляет событие
import eventBus from './eventBus';
function handleUserLogin(user) {
eventBus.emit('user:login', user);
}
// Module B - слушает событие
import eventBus from './eventBus';
eventBus.on('user:login', (user) => {
console.log(`Welcome, ${user.name}!`);
});
7. Context API (React)
// UserContext.js
const UserContext = React.createContext();
export function UserProvider({ children }) {
const [user, setUser] = React.useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
export function useUser() {
return React.useContext(UserContext);
}
// ProfileComponent.jsx - используем контекст
import { useUser } from './UserContext';
function Profile() {
const { user } = useUser();
return <div>{user?.name}</div>;
}
// LoginComponent.jsx
import { useUser } from './UserContext';
function Login() {
const { setUser } = useUser();
const handleLogin = () => {
setUser({ id: 1, name: 'John' });
};
return <button onClick={handleLogin}>Login</button>;
}
8. Сервисный слой
// services/api.service.js
class ApiService {
private baseUrl = 'https://api.example.com';
async request(endpoint, options = {}) {
const response = await fetch(`${this.baseUrl}${endpoint}`, options);
return response.json();
}
getUsers() {
return this.request('/users');
}
getUser(id) {
return this.request(`/users/${id}`);
}
}
export default new ApiService();
// components/UserList.jsx
import ApiService from '../services/api.service';
function UserList() {
const [users, setUsers] = React.useState([]);
React.useEffect(() => {
ApiService.getUsers().then(setUsers);
}, []);
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
9. Хуки для переиспользуемой логики
// hooks/useApi.js
function useApi(url) {
const [data, setData] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
React.useEffect(() => {
fetch(url)
.then(r => r.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
// components/Profile.jsx
import { useApi } from '../hooks/useApi';
function Profile({ userId }) {
const { data: user, loading } = useApi(`/api/users/${userId}`);
if (loading) return <p>Loading...</p>;
return <div>{user?.name}</div>;
}
10. Props Drilling для простых случаев
// Родитель передаёт функцию дочерним компонентам
function Parent() {
const [count, setCount] = React.useState(0);
return <Child onIncrement={() => setCount(count + 1)} />;
}
function Child({ onIncrement }) {
return <GrandChild onIncrement={onIncrement} />;
}
function GrandChild({ onIncrement }) {
return <button onClick={onIncrement}>+1</button>;
}
11. Интерфейсы (TypeScript)
// types/User.ts
export interface IUserService {
getUser(id: string): Promise<User>;
updateUser(id: string, data: Partial<User>): Promise<User>;
deleteUser(id: string): Promise<void>;
}
// services/UserService.ts
import { IUserService } from '../types/User';
export class UserService implements IUserService {
async getUser(id: string) { /* ... */ }
async updateUser(id: string, data) { /* ... */ }
async deleteUser(id: string) { /* ... */ }
}
// components/UserForm.tsx
import type { IUserService } from '../types/User';
interface UserFormProps {
userService: IUserService;
}
export function UserForm({ userService }: UserFormProps) {
// Используем контракт userService
return <div>...</div>;
}
12. Лучшие практики
Инкапсуляция
- Экспортируй только необходимое API
- Скрывай внутреннюю реализацию
Слабая связанность (Low coupling)
- Зависимости через параметры, не глобальные переменные
- Используй интерфейсы, не конкретные классы
Высокая когезия (High cohesion)
- Группируй связанную функциональность в один модуль
- Избегай смешивания разных ответственностей
Явные контракты
- TypeScript типы документируют API
- JSDoc комментарии описывают использование
Выводы
Взаимодействие с модулем извне основано на:
- Экспортах/Импортах - основной механизм
- Инкапсуляции - скрытие деталей реализации
- Dependency Injection - явная передача зависимостей
- Event Emitter - слабая связанность через события
- Интерфейсы - контракты между модулями
Правильная архитектура модулей делает код тестируемым, переиспользуемым и легким в поддержке.