React Style Guide中文翻譯


前言

本篇翻譯是重新翻譯React Style Guide的內容。 這篇翻譯遵循了在這個翻譯指引 Translation Guide中的翻譯規則,希望能提供更容易閱讀與理解的翻譯內容。

React Starter Kit是知名的"同構(isomorphic)"網路應用程式樣版文件(boilerplate)的專案。

React 風格指引

這份風格指引是來自額外增加於Airbnb React/JSX Guide。 你可以自由修改它來符合你的專案需求。

目錄

每個UI元件分別使用不同資料夾

  • 將每個主要UI元件與它的資源,放置在單獨的分隔資料夾中
    這讓你可以很容易的找到,對任何一個特定UI元素所使用的相關資源 (CSS、圖片、unit tests、本地化檔案等等)。當在重構(refactor)時,要移除這些元件也應該會很容易。
  • 避免存在多個元件間共享的CSS、圖片或其他資源檔案。
    這將讓你的程式碼更容易維護,也容易進行重構(refactor)。
  • 加入package.json到每個元件的資料夾中。
    這可以更容易參照到,其他在你的程式碼裡放在別處的元件。
    import Nav from '../Nav' vs import Nav from '../Nav/Nav.js'
/components/Navigation/icon.svg
/components/Navigation/Navigation.css
/components/Navigation/Navigation.js
/components/Navigation/Navigation.test.js
/components/Navigation/Navigation.ru-RU.css
/components/Navigation/package.json
// components/Navigation/package.json 
{
  "name:": "Navigation",
  "main": "./Navigation.js"
}

更多資訊請用google查詢component-based UI development.

建議使用函式型的元件

  • 建議儘可能的使用stateless(無狀態的)函式型元件。
    沒有state(狀態)的元件,比較容易寫成一個簡單的純函式。
// Bad
class Navigation extends Component {
  static propTypes = { items: PropTypes.array.isRequired };
  render() {
    return <nav><ul>{this.props.items.map(x => <li>{x.text}</li>)}</ul></nav>;
  }
}

// Better
function Navigation({ items }) {
  return (
    <nav><ul>{items.map(x => <li>{x.text}</li>)}</ul></nav>;
  );
}
Navigation.propTypes = { items: PropTypes.array.isRequired };

使用CSS模組(Modules)

  • 使用CSS模組
    這將可以使用簡短的CSS類別名稱,同時間也可避免衝突。
  • 保持CSS簡單而且是敘述式的。避免迴圈、mixins(混入)等等。
  • 請自由使用在CSS中的變數,透過precss 外掛於PostCSS
  • 建議使用CSS類別選擇器(selectors),而不要使用元素(element)與id選擇器(請見BEM)
  • 避免巢狀的CSS選擇器(selectors)(請見BEM)
  • 當有疑慮時,對元件的root元素使用.root { }類別名稱。
// Navigation.scss
@import '../variables.scss';

.root {
  width: 300px;
}

.items {
  margin: 0;
  padding: 0;
  list-style-type: none;
  text-align: center;
}

.item {
  display: inline-block;
  vertical-align: top;
}

.link {
  display: block;
  padding: 0 25px;
  outline: 0;
  border: 0;
  color: $default-color;
  text-decoration: none;
  line-height: 25px;
  transition: background-color .3s ease;

  &,
  .items:hover & {
    background: $default-bg-color;
  }

  .selected,
  .items:hover &:hover {
    background: $active-bg-color;
  }
}
// Navigation.js
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Navigation.scss';

function Navigation() {
  return (
    <nav className={`${s.root} ${this.props.className}`}>
      <ul className={s.items}>
        <li className={`${s.item} ${s.selected}`}>
          <a className={s.link} href="/products">Products</a>
        </li>
        <li className={s.item}>
          <a className={s.link} href="/services">Services</a>
        </li>
      </ul>
    </nav>
  );
}

Navigation.propTypes = { className: PropTypes.string };

export default withStyles(Navigation, s);

使用higher-order components

  • 使用higher-order components (HOC)來繼承(extend)已存在的React元件。
    以下是一個範例:
// withViewport.js
import React, { Component } from 'react';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';

function withViewport(ComposedComponent) {
  return class WithViewport extends Component {

    state = {
      viewport: canUseDOM ?
        {width: window.innerWidth, height: window.innerHeight} :
        {width: 1366, height: 768} // Default size for server-side rendering
    };

    componentDidMount() {
      window.addEventListener('resize', this.handleResize);
      window.addEventListener('orientationchange', this.handleResize);
    }

    componentWillUnmount() {
      window.removeEventListener('resize', this.handleResize);
      window.removeEventListener('orientationchange', this.handleResize);
    }

    handleResize = () => {
      let viewport = {width: window.innerWidth, height: window.innerHeight};
      if (this.state.viewport.width !== viewport.width ||
        this.state.viewport.height !== viewport.height) {
        this.setState({ viewport });
      }
    };

    render() {
      return <ComposedComponent {...this.props} viewport={this.state.viewport}/>;
    }

  };
};

export default withViewport;

// MyComponent.js
import React from 'react';
import withViewport from './withViewport';

class MyComponent {
  render() {
    let { width, height } = this.props.viewport;
    return <div>{`Viewport: ${width}x${height}`}</div>;
  }
}

export default withViewport(MyComponent);