阿里云主机折上折
  • 微信号
Current Site:Index > Component design principles

Component design principles

Author:Chuan Chen 阅读数:10383人阅读 分类: 前端综合

Component development standards and design principles form the foundation for building high-quality frontend applications. Good standards enhance code maintainability and readability, while sound design principles ensure component reusability and extensibility. From naming conventions to state management, from style isolation to API design, each step must strictly adhere to best practices.

Naming Conventions

Component names should use PascalCase and match the filename. For base components, a unified prefix should be added for better identification:

// Correct example
import Button from './BaseButton';
import IconClose from './IconClose';

// Incorrect example
import btn from './button';

CSS class names should follow the BEM methodology, using double underscores for elements and double hyphens for modifiers:

/* Correct example */
.menu__item--active {
  color: #1890ff;
}

/* Incorrect example */
.menu .active-item {
  color: red;
}

Single Responsibility Principle

Each component should address only one specific problem. When a component handles too many functions, it should be split:

// Overly complex component
function UserProfile({ user, onEdit, onDelete }) {
  return (
    <div>
      <Avatar src={user.avatar} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
      <button onClick={onEdit}>Edit</button>
      <button onClick={onDelete}>Delete</button>
    </div>
  );
}

// Split into single-purpose components
function UserAvatar({ src, alt }) {
  return <Avatar src={src} alt={alt} />;
}

function UserActions({ onEdit, onDelete }) {
  return (
    <>
      <button onClick={onEdit}>Edit</button>
      <button onClick={onDelete}>Delete</button>
    </>
  );
}

Controlled vs. Uncontrolled Components

Form components must clearly distinguish between controlled and uncontrolled modes. Controlled components fully manage state via props:

// Controlled input
function ControlledInput({ value, onChange }) {
  return <input value={value} onChange={onChange} />;
}

Uncontrolled components use refs to access DOM element values, suitable for scenarios where form data is only needed upon submission:

// Uncontrolled input
function UncontrolledInput({ defaultValue }) {
  const inputRef = useRef(null);
  
  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };

  return (
    <>
      <input ref={inputRef} defaultValue={defaultValue} />
      <button onClick={handleSubmit}>Submit</button>
    </>
  );
}

Style Isolation Solutions

Avoid global style pollution by adopting CSS Modules or styled-components:

// CSS Modules example
import styles from './Button.module.css';

function Button() {
  return <button className={styles.primary}>Click</button>;
}

// styled-components example
const StyledButton = styled.button`
  background: ${props => props.primary ? '#1890ff' : '#fff'};
  padding: 8px 16px;
`;

Component API Design

Component props should follow the principle of minimal exposure, with type constraints using PropTypes or TypeScript:

interface ModalProps {
  visible: boolean;
  title?: string;
  onClose: () => void;
  width?: number | string;
}

function Modal({ visible, title, onClose, width = '50%' }: ModalProps) {
  return (
    <div className="modal" style={{ width }}>
      {title && <h2>{title}</h2>}
      <button onClick={onClose}>×</button>
    </div>
  );
}

State Management Strategies

Choose appropriate state management based on component hierarchy. Use useState for local state and Context for cross-component state:

// Create Context
const ThemeContext = createContext('light');

function App() {
  const [theme, setTheme] = useState('dark');
  
  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
      <button onClick={() => setTheme('light')}>Toggle Theme</button>
    </ThemeContext.Provider>
  );
}

// Consume Context
function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div className={theme}>Current Theme: {theme}</div>;
}

Performance Optimization Techniques

Use React.memo to avoid unnecessary renders and implement virtual scrolling for large lists:

// Optimize with memo
const MemoComponent = memo(function MyComponent({ data }) {
  return <div>{data}</div>;
});

// Virtual list example
import { FixedSizeList as List } from 'react-window';

function VirtualList({ items }) {
  return (
    <List
      height={400}
      itemCount={items.length}
      itemSize={50}
      width={300}
    >
      {({ index, style }) => (
        <div style={style}>Row {items[index]}</div>
      )}
    </List>
  );
}

Error Boundary Handling

Use error boundary components to catch JavaScript errors in child component trees:

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    logErrorToService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <FallbackUI />;
    }
    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <UnstableComponent />
</ErrorBoundary>

Accessibility Requirements

Ensure components comply with WAI-ARIA standards, providing necessary information for screen readers:

function AccessibleButton({ children }) {
  return (
    <button
      aria-label="Close dialog"
      aria-pressed={false}
      onClick={handleClick}
    >
      {children}
    </button>
  );
}

Documentation Standards

Each component should include a README with:

  • Basic usage examples
  • Detailed props description
  • Event handling instructions
  • Style override guidelines
  • Notes and edge cases
## Button Component

### Basic Usage
```jsx
<Button type="primary">Confirm</Button>

Props

Prop Type Default Description
type 'primary'|'default' 'default' Button type
disabled boolean false Disabled state

## Testing Strategy

Components should include unit tests and interaction tests to validate core functionality:

```jsx
// Jest test example
test('Button renders with correct text', () => {
  const { getByText } = render(<Button>Submit</Button>);
  expect(getByText('Submit')).toBeInTheDocument();
});

// Interaction test example
test('Button triggers onClick', () => {
  const handleClick = jest.fn();
  const { getByRole } = render(<Button onClick={handleClick} />);
  fireEvent.click(getByRole('button'));
  expect(handleClick).toHaveBeenCalled();
});

Version Compatibility

Component libraries must specify supported browser ranges and polyfill strategies:

// browserslist config in package.json
{
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version"
    ]
  }
}

Internationalization Support

Component text content should support multilingual switching, avoiding hardcoding:

// Using i18n solution
function Greeting() {
  const { t } = useTranslation();
  return <h1>{t('welcome_message')}</h1>;
}

// Date localization
new Date().toLocaleDateString(navigator.language);

Theme Customization Solutions

Implement dynamic theming via CSS variables or theme Providers:

// CSS variables solution
:root {
  --primary-color: #1890ff;
}

.button {
  background: var(--primary-color);
}

// JS theme solution
const theme = {
  colors: {
    primary: '#1890ff'
  }
};

<ThemeProvider theme={theme}>
  <App />
</ThemeProvider>

Component Composition Patterns

Enable flexible composition via children props or render props:

// Children composition
function Card({ children }) {
  return <div className="card">{children}</div>;
}

<Card>
  <CardHeader />
  <CardBody />
</Card>

// Render props pattern
function Toggle({ render }) {
  const [on, setOn] = useState(false);
  return render({ on, toggle: () => setOn(!on) });
}

<Toggle render={({ on, toggle }) => (
  <button onClick={toggle}>{on ? 'ON' : 'OFF'}</button>
)} />

Type Definition Files

Provide TypeScript type declarations for JavaScript components:

// types/component.d.ts
declare module 'ui-library' {
  interface ButtonProps {
    size?: 'small' | 'medium' | 'large';
    variant?: 'primary' | 'secondary';
  }

  export const Button: React.FC<ButtonProps>;
}

Build Output Standards

Component libraries should output multiple module formats:

// rollup.config.js
export default {
  input: 'src/index.js',
  output: [
    { file: 'dist/index.esm.js', format: 'es' },
    { file: 'dist/index.cjs.js', format: 'cjs' },
    { file: 'dist/index.umd.js', format: 'umd', name: 'ComponentLib' }
  ]
};

Release Process Control

Adopt semver versioning standards and follow commit message conventions:

# Standard version release command
npm version patch -m "fix: Resolve button click area issue"
npm publish

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:代码复用原则

下一篇:组件命名规范

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.