學習 React.js : 概念和起步

Learning React.js: Getting Started and Concepts

By Ken Wheeler (@ken_wheeler)

#簡介

今天我們開始一個新系列的學習,學習 React,我們將集中在怎麼熟練並且有效的使用臉書的 React 庫上。在我們開始實際寫程序之前,還有更重要的一步,我們先講解一些基本概念,好了,我們開始吧。

##什麼是 React?

React 是臉書開發的一個 UI 庫,可以用來方便創建具有交互性,狀態性 & 重用性的 UI 組件。它已經被運用在了臉書的生產環境上了,並且 Instagram.com 也完全是用 React 來寫的。

它的一個獨一無二的買點是,它不僅能用在客戶端,還能在服務端渲染,並且可以配合著用。

它還有一個概念叫做虛擬DOM,它會基於狀態更新選擇性的進行渲染子節點。這使得你的組件通過最少的DOM操作保持更新。

##虛擬DOM是怎樣工作的?

想象一下,你有一個人類模型。它有一個人應該有的各種屬性,並且反映了這個人當前的狀態。React 差不多就是這樣對待 DOM 的。

現在再想一下,如果你讓這個對象發生一些改變,比如說加個小鬍子,然後再加點肌肉,再加雙 Steve Buscemi 那樣的眼睛。在 React 世界,當你做了這些更新之後,有兩件事情會發生。首先, React 執行"髒值檢查",確定發生了什麼改變。然後第二步是調和,把檢測到的更新結果反映到 DOM 上。

React 的方式,不會真的去再生一個小孩出來,重新把他們養大,而只是做個臉和手臂的整形。也就是說,如果你在輸入框裏面的文字發生改變,除非輸入框的父節點發生渲染,否則文字將會保持原狀。

由於 React 用的是假的 DOM 而不是真的那個,那麽這就讓有了一種新的可能。我們可以在服務端來渲染這個假的 DOM,然後,duang~服務端的 React View。

#開始

想要開始用 React 很簡單,只要去下載他們提供的 starter kit 就好:

React Starter Kit

你也可以叉他們提供的 JSFiddle:

React JSFiddle

##頁面設置

那麽我們來設置頁面,你需要導入 react.jsJSXTransformer.js,然後用 script 開始寫你的組件,記得把這個節點的 type 設置為 text/jsx

<!-- lang: js -->
<!DOCTYPE html>
<html>
  <head>
    <script src="build/react.js"></script>
    <script src="build/JSXTransformer.js"></script>
  </head>
  <body>
    <div id="mount-point"></div>
    <script type="text/jsx">
      // React Code Goes Here
    </script>
  </body>
</html>

在 React 裏面,組件必須加載到一個元素上,所以在例子裏面,我們可以用 div mount-point 來作為它的父容器。

當然這只是一個最簡單的起點,黨你實際上要寫點什麼的時候,最好用 Browerify 或者 webpack 這樣的工具來把你的組件拆分到不同的文件中。

#基礎

React 的基本單元模塊叫做組件(component),讓我們來寫一個:

<!-- lang: js -->
<script type="text/jsx">
    /** @jsx React.DOM */
    React.renderComponent(
        <h1>Hello,world!</h1>,document.getElementById('myDiv')
    );
</script>

如果你沒看之前的代碼,那你肯定一頭霧水不知道這個 javascript/HTML 到底做了什麼。

##JSX

這就是所謂的 JSX,它是一種 Javascript XML 語法轉換,可以讓你在你的 Javascript 裏面寫 類HTML。我之所以說 類HTML 是有多重含義。你僅僅是基於一個關聯對象來寫 XML 。

對於正常的 html 標籤,在 JSX 中,class 屬性變成了 className,而 for 則變成了 htmlFor,這是因為有些 Javascript 的保留字。你可以看這裏來瞭解更多的不同。

如果你不用 JSX,那麽這裏是不用它的一個版本:

<!-- lang: js -->
/** @jsx React.DOM */
React.renderComponent(
  React.DOM.h1(null,'Hello,world!'),document.getElementById('myDiv')
);

如果你願意,你可以看這裏,來瞭解更多支持的元素。

在你的第一個代碼片段裏面,你有沒有注意到在頂行的 /** @jsx React.DOM */ ?它非常重要,它告訴 React 我們用了 JSX 所以這段代碼需要轉換,所以你用 JSX 語法的時候,你需要把它包含進來。

##組件

黨使用上面的 renderComponent 方法,第一個參數是我們希望渲染的組件,第二個參數是將要掛載上去的DOM 節點。我們可以用 createClass 方法來創建自定義組件類。它可以使用對象作為參數。下面讓我們來創建一個:

<!-- lang: js -->
var MyComponent = React.createClass({
    render: function(){
        return (
            <h1>Hello,world!</h1>
        );
    }
});

創建一個類之後,我們可以在 document 中像這樣渲染它:

<!-- lang: js -->
React.renderComponent(
    <MyComponent/>,document.getElementById('myDiv')
);

屌不屌,嗯哼?

##Props

當我們使用我們定義的組件的時候,我們可以添加屬性,叫做 props,這些屬性在我們的組件仲可以通過 this.props 調用並且可以在我們的渲染方法裏面動態渲染數據:

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

React.renderComponent(<MyComponent name="Handsome" />,document.getElementById('myDiv'));

##Specs,生命週期 & 狀態

render 方法是創建組件唯一需要進行測試的部分,不過這裏有一些生命週期相關方法和測試我們可以用到,當我們在實際的組件中應該會有所幫助。

###生命週期方法

  • componentWillMout 執行一次,在客戶端和服務端將要執行渲染的時候
  • componentDidMount 執行一次,只在客戶端,當渲染完成之後
  • shouldComponentUpdate 返回值判定組件是否需要更新的
  • componentWillUnmount 在卸載之前執行

###Specs

  • getInitialState 返回狀態初期值
  • getDefaultProps 設置退避 props 值,如果 沒有可用 props
  • mixins 一個對象數組,用來擴展當前組件的功能

###State

每個組件都有一個 state 對象和一個 props 對象。State 通過 setState 來設置。調用 setState 會觸發 UI 更新,它是 React 交互的基礎。如果我們想在所有交互開始之前設置一個初始狀態,我們可以用 getInitialState 方法。下面,讓我們看看我們是如何設置組件的狀態的:

<!-- lang: js -->
var MyComponent = React.createClass({
    getInitialState: function(){
        return {
            count: 5
        }
    },render: function(){
        return (
            <h1>{this.state.count}</h1>
        )
    }
});

##事件

React 還有一個內建跨瀏覽器事件系統。這些事件可以作為組件的屬性來追加也可以觸發方法。讓我們來通過事件來創建一個計數器:

<!-- lang: html -->
<div id="mount-point"></div>

<!-- lang: js -->
/** @jsx React.DOM */

var Counter = React.createClass({
  incrementCount: function(){
    this.setState({
      count: this.state.count + 1
    });
  },getInitialState: function(){
     return {
       count: 0
     }
  },render: function(){
    return (
      <div class="my-component">
        <h1>Count: {this.state.count}</h1>
        <button type="button" onClick={this.incrementCount}>Increment</button>
      </div>
    );
  }
});

React.renderComponent(<Counter/>,document.getElementById('mount-point'));

<!-- lang: css -->
body { 
  background: #bdc3c7;
  padding: 40px; 
}
.counter {
  width: 300px;
  margin: auto;
  background: #9b59b6;
  color: white;
  padding: 20px;
  text-align: center;
  h1 {
    margin: 0;
    padding: 20px;
    font-size: 36px;
  }
  button {
    background: #f1c40f;
    border: 0;
    Box-shadow: 0px 5px 0px darken(#f1c40f,20%);
    padding: 20px;
    width: 100%;
    outline: none;
    border-radius: 3px;
    color: darken(#8e44ad,10%);
    font-weight: bold;
  }
}

##單向數據流

在 React 中,英勇的數據通過 state 和 props 對象單向傳播,和其他的雙向綁定的庫完全不一樣,比如說 Angular。意思也就是說,在多組件模式下,一個共通父組件將負責管理狀態以及將它通過 props 向下傳播。

如果需要,你的狀態可以通過使用 setState 方法來更新,以確保 UI 的刷新被執行。結果值會被傳播到子組件,通過使用屬性。也就是說子組件可以用 this.props 來訪問的那些。

下面用一個例子來解釋這些概念:

<!-- lang: js -->
/** @jsx React.DOM */
var FilteredList = React.createClass({
  filterList: function(event){
    var updatedList = this.state.initialItems;
    updatedList = updatedList.filter(function(item){
      return item.toLowerCase().search(
        event.target.value.toLowerCase()) !== -1;
    });
    this.setState({items: updatedList});
  },getInitialState: function(){
     return {
       initialItems: [
         "Apples","Broccoli","Chicken","Duck","Eggs","Fish","Granola","Hash Browns"
       ],items: []
     }
  },componentWillMount: function(){
    this.setState({items: this.state.initialItems})
  },render: function(){
    return (
      <div className="filter-list">
        <input type="text" placeholder="Search" onChange={this.filterList}/>
      <List items={this.state.items}/>
      </div>
    );
  }
});

var List = React.createClass({
  render: function(){
    return (
      <ul>
      {
        this.props.items.map(function(item) {
          return <li key={item}>{item}</li>
        })
       }
      </ul>
    )  
  }
});

React.renderComponent(<FilteredList/>,document.getElementById('mount-point'));

<!-- lang: html -->
<div id="mount-point"></div>

<!-- lang: css -->
* {
  Box-sizing: border-Box;
}

body {
  padding: 20px;
  background: #2c3e50;
}

.filter-list {
  margin: auto;
  width: 300px;
  background: #3498db;
  border-radius: 5px;
  border: 1px solid darken(#3498db,20%);
  input {
    width: 100%;
    display: block;
    padding: 10px;
    border-radius: 5px 5px 0px 0px;
    border: 0;
    font-size: 24px;
    &:focus {
      outline: none;
    }
  }
  ul {
    margin: 0;
    padding: 0;
    li {
      list-style-type: none;
      margin: 0;
      color: white;
      padding: 10px 20px;
      border-top: 1px solid darken(#3498db,20%);
      &:hover {
        background: #2980b9;
      }
    }
  }
}

#總結

我們已經介紹了一些 React 的基礎知識了,花點時間去看看 React API 和學一下 JSX

在學習 React 的下一張,我們將結合 Express 來創建一個在服務端渲染的應用,就像在客戶端一樣,然後使用 socket.io 來保持兩邊同步。

敬請期待!

相关文章

导入moment 使用方式 年月日,时分秒 星期几 相对时间 7天后 2小时后 明天 将毫秒转换成年月日
@ 一、前言 为什么介绍redux-actions呢? 第一次见到主要是接手公司原有的项目,发现有之前的大佬在处理...
十大React Hook库 原文地址:https://dev.to/bornfightcompany/top-10-react-hook-libraries-4065 原文...
React生命周期 React的生命周期从广义上分为挂载、渲染、卸载三个阶段,在React的整个生命周期中提供很...
React虚拟DOM的理解 Virtual DOM是一棵以JavaScript对象作为基础的树,每一个节点可以将其称为VNode,用...
React中JSX的理解 JSX是快速生成react元素的一种语法,实际是React.createElement(component, props, ....