1. 程式人生 > >React開發環境安裝配置和基礎入門

React開發環境安裝配置和基礎入門

React基礎入門

ReactDOM.render()

將指定的模板或節點內容插入指定的節點(通常是一個div)

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

以上表示,將一個h1標籤插入一個id為“example”的div節點內

<head>
  <link rel="stylesheet" href="./src/css/style.css"/>
</head>
<div id="example"
>
</div> <script src="./src/bundle.js"></script>

執行結果是

<head>
  <link rel="stylesheet" href="./src/css/style.css"/>
</head>
<div id="example">hello, world</div>
<script src="./src/bundle.js"></script>

JSX

本質是語法糖,經過編譯後最終生成的是js程式碼
特點是支援類xml語法並且支援與js程式碼混寫

var names = ['Alice', 'Emily', 'Kate'];

ReactDOM.render(
  <div>
  {
    names.map(function (name) {
      return <div>Hello, {name}!</div>
    })
  }
  </div>,
  document.getElementById('example')
);

尖括號<>內容屬於jsx語法內容;大括號{}內容屬於js程式碼內容
如果jsx內包含一個js陣列物件變數,將自動呼叫其遍歷器獲取其全部成員

var arry = ["hello","world"];
<ul>
  {arr.map(item => (
  <li>{item+"zph"}</li>
  ))}
</ul>

另一個例子

class ShoppingList extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>Shopping List for {this.props.name}</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

//等同於
return React.createElement('div', {className: 'shopping-list'},
  React.createElement('h1', /* ... h1 children ... */),
  React.createElement('ul', /* ... ul children ... */)
);
// Example usage: <ShoppingList name="Mark" />

元件

元件本質是一個包含render方法的物件(類)。每次使用元件將生成一個新的物件例項

var MyComponent = React.createClass({
  render: function() {
    return <h1>Hello {this.props.name}</h1>;
  }
});

ReactDOM.render(
  <MyComponent name="John" />,
  document.getElementById('example')
);

es6的module形式,向外暴露一個class類

export default class MyComponent extends React.Component{
  constructor(){
    super();    
  }
  render(){
    return(
      <h1>Hello {this.props.name}</h1>;
    );
  }
}

js保留字問題:
class使用 className代替
for 使用htmlFor代替

this.props.children

  • 如果當前元件沒有子節點 返回值是 undefined
  • 如果只有一個子節點,返回值資料型別是 object
  • 如果有多個子節點,返回值資料型別就是 array
var NotesList = React.createClass({
  render: function() {
    return (
      <ol>
      {
        React.Children.map(this.props.children, function (child) {
          return <li>{child}</li>;
        })
      }
      </ol>
    );
  }
});

ReactDOM.render(
  <NotesList>
    <span>hello</span>
    <span>world</span>
  </NotesList>,
  document.body
);

PropTypes型別檢查

React v15.5版本之後React.PropTypes使用方式已經移除,可使用 prop-types庫來代替

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};
//本質是指定一個名為propTypes的靜態屬性,也可在類中使用static關鍵字的形式使用

一些基本型別的檢查

  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

// Anything that can be rendered: numbers, strings, elements or an array
  // (or fragment) containing these types.
  optionalNode: PropTypes.node,

  // A React element.
  optionalElement: PropTypes.element,

  // You can also declare that a prop is an instance of a class. This uses
  // JS's instanceof operator.
  optionalMessage: PropTypes.instanceOf(Message),

  // You can ensure that your prop is limited to specific values by treating
  // it as an enum.
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // An object that could be one of many types
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // An array of a certain type
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // An object with property values of a certain type
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // An object taking on a particular shape
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // You can chain any of the above with `isRequired` to make sure a warning
  // is shown if the prop isn't provided.
  requiredFunc: PropTypes.func.isRequired,

  // A value of any data type
  requiredAny: PropTypes.any.isRequired,

以上是指定型別,如果需要指定是不可預設,可在後面追加isRequired,如
optionalArray: PropTypes.array.isRequeired 更詳細的可參考官方文件內容

defaultProps 設定預設值

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

// Specifies the default values for props:
Greeting.defaultProps = {
  name: 'Stranger'
};

// Renders "Hello, Stranger":
ReactDOM.render(
  <Greeting />,
  document.getElementById('example')
);

Refs

直接獲取真實的DOM節點而非virtural DOM
使用場景:獲取焦點、觸發動畫、整合第三方DOM庫等
元件標籤內使用ref關鍵字定義一個callback回撥,此回撥將在元件載入或解除安裝完成後立即執行。回撥函式引數是dom節點,如

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // Explicitly focus the text input using the raw DOM API
    this.textInput.focus();
  }

  render() {
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={(input) => { this.textInput = input; }} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

方法元件中使用ref

function CustomTextInput(props) {
  // textInput must be declared here so the ref callback can refer to it
  let textInput = null;

  function handleClick() {
    textInput.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

避免使用String Refs 如,this.refs.textInput 此種使用方式以後將會被廢棄

this.state

  • state被更新後將重新呼叫元件的render方法
  • 作用域只針對當前的元件不會汙染其他元件
  • 初始化state操作可放置於constructor建構函式中
  • 呼叫setState({key:value})修改state

this.props

用於給獲取傳給當前元件的資料 如 <component key=value/> 給當前元件傳遞了key:value資料,當前元件可使用this.props.key 獲取value值

傳遞當前全部的props到子元件

<component {...this.props}/>

子元件更新父元件內容

通過給子元件傳函式

react-mixin

公用功能或程式碼

生命週期

React router

以下內容基於react-router 4.x版本

參考文件

存在多個Repository:
* react-router: React Router 提供核心的路由元件與函式
* react-router-dom: 用於 DOM 繫結的 React Router
* react-router-native: 用於 React Native 的 React Router
* react-router-redux: React Router 和 Redux 的整合
* react-router-config: 靜態路由配置幫助助手

web網站應用只需安裝react-router-dom

react-router-dom暴露出react-router中暴露的物件與方法,因此只需安裝並引用react-router-dom即可,react-router-dom比react-router多了<Link>、 <BrowserRouter>

$ npm install --save react-router-dom

對於網頁專案,存在<BrowserRouter><HashRouter>兩種元件

區別:

?每個路由器都會建立一個history物件並用其保持追蹤當前location並且在有變化時對網站進行重新渲染

<Router/>

Router元件的children可以放任內容(元件或普通標籤),但只可有唯一的子元素
如,

<Router>
    <div>
      <ul>
        <li><Link to="/">首頁</Link></li>
        <li><Link to="/about">關於</Link></li>
        <li><Link to="/topics">主題列表</Link></li>
      </ul>

      <hr/>

      <Route exact path="/" component={Home}/>
      <Route path="/about" component={About}/>
      <Route path="/topics" component={Topics}/>
    </div>
  </Router>

Router是所有路由元件共用的底層介面,一般我們的應用並不會使用這個介面,而是使用高階的路由:

  • <BrowserRouter>:使用 HTML5 提供的 history API 來保持 UI 和 URL 的同步;
  • <HashRouter>:使用 URL 的 hash (例如:window.location.hash) 來保持 UI 和 URL 的同步;
  • <MemoryRouter>:能在記憶體儲存你 “URL” 的歷史紀錄(並沒有對位址列讀寫);
  • <NativeRouter>:為使用React Native提供路由支援;
  • <StaticRouter>:從不會改變地址;

<Route/>

當一個location匹配Route的path時,渲染某些UI內容

<Router>
  <div>
    <Route exact path="/" component={Home}/>
    <Route path="/news" component={NewsFeed}/>
  </div>
</Router>

Route的一些屬性:
* path(string): 路由匹配路徑。(沒有path屬性的Route 總是會 匹配);
* exact(bool):為true時,則要求路徑與location.pathname必須完全匹配;
* strict(bool):true的時候,有結尾斜線的路徑只能匹配有斜線的location.pathname

Route的三種不同的渲染內容的方式:
<Route component><Route render><Route render>

<Route component>的優先順序要比<Route render>高,所以不要在同一個中同時使用這兩個屬性。

  • to(string/object):要跳轉的路徑或地址;
  • replace(bool):為 true 時,點選連結後將使用新地址替換掉訪問歷史記錄裡面的原地址;為 false 時,點選連結後將在原有訪問歷史記錄的基礎上新增一個新的紀錄。預設為 false;
// to為string
<Link to="/about">關於</Link>

// to為obj
<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: { fromDashboard: true }
}}/>

// replace
<Link to="/courses" replace />

<NavLink><Link>的一個特定版本, 會在匹配上當前 URL 的時候會給已經渲染的元素新增樣式引數,元件屬性:

  • activeClassName(string):設定選中樣式,預設值為 active;
  • activeStyle(object):當元素被選中時, 為此元素新增樣式;
  • exact(bool):為 true 時, 只有當地址完全匹配 class 和 style 才會應用;
  • strict(bool):為 true 時,在確定位置是否與當前 URL 匹配時,將考慮位置 pathname 後的斜線;
  • isActive(func):判斷連結是否啟用的額外邏輯的功能;
// activeClassName選中時樣式為selected
<NavLink
  to="/faq"
  activeClassName="selected"
>FAQs</NavLink>

// 選中時樣式為activeStyle的樣式設定
<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}
>FAQs</NavLink>

// 當event id為奇數的時候,啟用連結
const oddEvent = (match, location) => {
  if (!match) {
    return false
  }
  const eventID = parseInt(match.params.eventID)
  return !isNaN(eventID) && eventID % 2 === 1
}

<NavLink
  to="/events/123"
  isActive={oddEvent}
>Event 123</NavLink>

<Switch/>

該元件用來渲染匹配地址的第一個<Route>或者<Redirect>

思考如下內容:

<Router>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Router>

如果現在的URL是/about,那麼<About>, <User>,<NoMatch>都會被渲染,因為它們都與路徑(path)匹配。這種設計,允許我們以多種方式將多個組合到我們的應用程式中,例如側欄(sidebars),麵包屑(breadcrumbs),bootstrap tabs等等。

然而,偶爾我們只想選擇一個來渲染。如果我們現在處於/about,我們也不希望匹配/:user(或者顯示我們的 “404” 頁面 )。此時需要 Switch 的方法來實現:

<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>

現在,如果我們處於/about,<Switch>將開始尋找匹配的<Route><Route path="/about"/> 將被匹配, <Switch>將停止尋找匹配並渲染<About>。同樣,如果我們處於/michael,<User>將被渲染。

react樣式使用

內聯樣式寫法

import React from 'react';
export default class ComponentHeader extends React.Component {

constructor(){
    super();
    this.state ={
        miniHeader: false //預設載入的時候還是高(不是 mini)的頭部
    };
};

  switchHeader(){
    this.setState({
        miniHeader: !this.state.miniHeader
    });
  };

    render() {
        const styleComponentHeader = {
            header: {
                backgroundColor: "#333333",
                color: "#FFFFFF",
                "padding-top": (this.state.miniHeader) ? "3px" : "15px",
                paddingBottom: (this.state.miniHeader) ? "3px" : "15px"
            },
            //還可以定義其他的樣式
        };
        return (
            <header style={styleComponentHeader.header} className="smallFontSize"
            onClick={this.switchHeader.bind(this)}>
                <h1>這裡是頭部</h1>
            </header>
        )
    }
}

引用樣式表形式

通過import css檔案的方式,元件可使用className引用樣式

css模組化

要解決的問題:
- 全域性汙染
- 命名混亂

需要安裝以下modules:
css-loader style-loader

可選的 babel-plugin-react-attrs 用於解決className,htmlFor等名稱衝突問題

配置webpack.config

在webpack.config.js檔案loaders節點新增以下內容即可
下面是新增的 css 的 loader,也即是 css 模組化的配置

{
  test: /\.css$/,
  loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
}

匯入css
var footerCss = require("../../footer.css")
使用css
<footer className = {footerCss.miniFooter}/>

全域性樣式:在css檔案中定義中使用global顯式指定此樣式將作為全域性樣式使用。預設樣式是loacal無需顯式
:local(.myclass)(color:red)
:global(.btn)(color:green)

JSX樣式和css互轉

Ant Design

類比另一個UI框架:material-UI
Ant Design 官網

Antd使用方式:

下面是使用 ant-design 的webpack.config配置檔案需要修改的loaders節點內容
{ test: /\.css$/, loader: 'style-loader!css-loader' }
主頁引入樣式
import 'antd/dist/antd.css'
元件中引入需要的控制元件
import { Input } from 'antd';