1. 程式人生 > >(轉)react.js快速入門教程

(轉)react.js快速入門教程

React 入門例項教程

作者: 阮一峰

現在最熱門的前端框架,毫無疑問是 React

上週,基於 React 的 React Native 釋出,結果一天之內,就獲得了 5000 顆星,受矚目程度可見一斑。

React 起源於 Facebook 的內部專案,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以後,發現這套東西很好用,就在2013年5月開源了。

由於 React 的設計思想極其獨特,屬於革命性創新,效能出眾,程式碼邏輯卻非常簡單。所以,越來越多的人開始關注和使用,認為它可能是將來 Web 開發的主流工具。

這個專案本身也越滾越大,從最早的UI引擎變成了一整套前後端通吃的 Web App 解決方案。衍生的 React Native 專案,目標更是巨集偉,希望用寫 Web App 的方式去寫 Native App。如果能夠實現,整個網際網路行業都會被顛覆,因為同一組人只需要寫一次 UI ,就能同時執行在伺服器、瀏覽器和手機(參見《也許,DOM 不是答案》)。

既然 React 這麼熱門,看上去充滿希望,當然應該好好學一下。從技術角度,可以滿足好奇心,提高技術水平;從職業角度,有利於求職和晉升,有利於參與潛力大的專案。但是,好的 React 教程卻不容易找到,這一方面因為這項技術太新,剛剛開始走紅,大家都沒有經驗,還在摸索之中;另一方面因為 React 本身還在不斷變動,API 一直在調整,至今沒釋出1.0版。

我學習 React 時,就很苦惱。有的教程討論一些細節問題,對入門沒幫助;有的教程寫得不錯,但比較短,無助於看清全貌。我斷斷續續學了幾個月,看過二十幾篇教程,在這個過程中,將對自己有幫助的 Demo 都收集下來,做成了一個庫 React Demos

下面,我就根據這個庫,寫一篇全面又易懂的 React 入門教程。你只需要跟著每一個 Demo 做一遍,就能初步掌握 React 。當然,前提是你必須擁有基本 JavaScript 和 DOM 知識,但是你讀完就會發現,React 所要求的預備知識真的很少。

零、安裝

React 的安裝包,可以到官網下載。不過,React Demos

已經自帶 React 原始碼,不用另外安裝,只需把這個庫拷貝到你的硬碟就行了。


$ git clone [email protected].com:ruanyf/react-demos.git

如果你沒安裝 git, 那就直接下載 zip 壓縮包

下面要講解的12個例子在各個 Demo 子目錄,每個目錄都有一個 index.html 檔案,在瀏覽器開啟這個檔案(大多數情況下雙擊即可),就能立刻看到效果。

需要說明的是,React 可以在瀏覽器執行,也可以在伺服器執行,但是本教程只涉及瀏覽器。一方面是為了儘量保持簡單,另一方面 React 的語法是一致的,伺服器的用法與瀏覽器差別不大。Demo13 是伺服器首屏渲染的例子,有興趣的朋友可以自己去看原始碼。

一、HTML 模板

使用 React 的網頁原始碼,結構大致如下。


<!DOCTYPE html>
<html>
  <head>
    <script src="../build/react.js"></script>
    <script src="../build/react-dom.js"></script>
    <script src="../build/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      // ** Our code goes here! **
    </script>
  </body>
</html>

上面程式碼有兩個地方需要注意。首先,最後一個 <script> 標籤的 type 屬性為 text/babel 。這是因為 React 獨有的 JSX 語法,跟 JavaScript 不相容。凡是使用 JSX 的地方,都要加上 type="text/babel"

其次,上面程式碼一共用了三個庫: react.jsreact-dom.jsBrowser.js ,它們必須首先載入。其中,react.js 是 React 的核心庫,react-dom.js 是提供與 DOM 相關的功能,Browser.js 的作用是將 JSX 語法轉為 JavaScript 語法,這一步很消耗時間,實際上線的時候,應該將它放到伺服器完成。


$ babel src --out-dir build

上面命令可以將 src 子目錄的 js 檔案進行語法轉換,轉碼後的檔案全部放在 build 子目錄。

二、ReactDOM.render()

ReactDOM.render 是 React 的最基本方法,用於將模板轉為 HTML 語言,並插入指定的 DOM 節點。


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

上面程式碼將一個 h1 標題,插入 example 節點(檢視 demo01),執行結果如下。

三、JSX 語法

上一節的程式碼, HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的語法,它允許 HTML 與 JavaScript 的混寫(檢視 Demo02 )。


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

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

上面程式碼體現了 JSX 的基本語法規則:遇到 HTML 標籤(以 < 開頭),就用 HTML 規則解析;遇到程式碼塊(以 { 開頭),就用 JavaScript 規則解析。上面程式碼的執行結果如下。

JSX 允許直接在模板插入 JavaScript 變數。如果這個變數是一個數組,則會展開這個陣列的所有成員(檢視 demo03 )。


var arr = [
  <h1>Hello world!</h1>,
  <h2>React is awesome</h2>,
];
ReactDOM.render(
  <div>{arr}</div>,
  document.getElementById('example')
);

上面程式碼的arr變數是一個數組,結果 JSX 會把它的所有成員,新增到模板,執行結果如下。

四、元件

React 允許將程式碼封裝成元件(component),然後像插入普通 HTML 標籤一樣,在網頁中插入這個元件。React.createClass 方法就用於生成一個元件類(檢視 demo04)。


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

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

上面程式碼中,變數 HelloMessage 就是一個元件類。模板插入 <HelloMessage /> 時,會自動生成 HelloMessage 的一個例項(下文的"元件"都指元件類的例項)。所有元件類都必須有自己的 render 方法,用於輸出元件。

注意,元件類的第一個字母必須大寫,否則會報錯,比如HelloMessage不能寫成helloMessage。另外,元件類只能包含一個頂層標籤,否則也會報錯。


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

上面程式碼會報錯,因為HelloMessage元件包含了兩個頂層標籤:h1p

元件的用法與原生的 HTML 標籤完全一致,可以任意加入屬性,比如 <HelloMessage name="John"> ,就是 HelloMessage 元件加入一個 name 屬性,值為 John。元件的屬性可以在元件類的 this.props 物件上獲取,比如 name 屬性就可以通過 this.props.name 讀取。上面程式碼的執行結果如下。

新增元件屬性,有一個地方需要注意,就是 class 屬性需要寫成 classNamefor 屬性需要寫成 htmlFor ,這是因為 classfor 是 JavaScript 的保留字。

五、this.props.children

this.props 物件的屬性與元件的屬性一一對應,但是有一個例外,就是 this.props.children 屬性。它表示元件的所有子節點(檢視 demo05)。


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
);

上面程式碼的 NoteList 元件有兩個 span 子節點,它們都可以通過 this.props.children 讀取,執行結果如下。

這裡需要注意, this.props.children 的值有三種可能:如果當前元件沒有子節點,它就是 undefined ;如果有一個子節點,資料型別是 object ;如果有多個子節點,資料型別就是 array 。所以,處理 this.props.children 的時候要小心。

React 提供一個工具方法 React.Children 來處理 this.props.children 。我們可以用 React.Children.map 來遍歷子節點,而不用擔心 this.props.children 的資料型別是 undefined 還是 object。更多的 React.Children 的方法,請參考官方文件

六、PropTypes

元件的屬性可以接受任意值,字串、物件、函式等等都可以。有時,我們需要一種機制,驗證別人使用元件時,提供的引數是否符合要求。

元件類的PropTypes屬性,就是用來驗證元件例項的屬性是否符合要求(檢視 demo06)。


var MyTitle = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,
  },

  render: function() {
     return <h1> {this.props.title} </h1>;
   }
});

上面的Mytitle元件有一個title屬性。PropTypes 告訴 React,這個 title 屬性是必須的,而且它的值必須是字串。現在,我們設定 title 屬性的值是一個數值。


var data = 123;

ReactDOM.render(
  <MyTitle title={data} />,
  document.body
);

這樣一來,title屬性就通不過驗證了。控制檯會顯示一行錯誤資訊。


Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.

更多的PropTypes設定,可以檢視官方文件

此外,getDefaultProps 方法可以用來設定元件屬性的預設值。


var MyTitle = React.createClass({
  getDefaultProps : function () {
    return {
      title : 'Hello World'
    };
  },

  render: function() {
     return <h1> {this.props.title} </h1>;
   }
});

ReactDOM.render(
  <MyTitle />,
  document.body
);

上面程式碼會輸出"Hello World"。

七、獲取真實的DOM節點

元件並不是真實的 DOM 節點,而是存在於記憶體之中的一種資料結構,叫做虛擬 DOM (virtual DOM)。只有當它插入文件以後,才會變成真實的 DOM 。根據 React 的設計,所有的 DOM 變動,都先在虛擬 DOM 上發生,然後再將實際發生變動的部分,反映在真實 DOM上,這種演算法叫做 DOM diff ,它可以極大提高網頁的效能表現。

但是,有時需要從元件獲取真實 DOM 的節點,這時就要用到 ref 屬性(檢視 demo07 )。


var MyComponent = React.createClass({
  handleClick: function() {
    this.refs.myTextInput.focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

上面程式碼中,元件 MyComponent 的子節點有一個文字輸入框,用於獲取使用者的輸入。這時就必須獲取真實的 DOM 節點,虛擬 DOM 是拿不到使用者輸入的。為了做到這一點,文字輸入框必須有一個 ref 屬性,然後 this.refs.[refName] 就會返回這個真實的 DOM 節點。

需要注意的是,由於 this.refs.[refName] 屬性獲取的是真實 DOM ,所以必須等到虛擬 DOM 插入文件以後,才能使用這個屬性,否則會報錯。上面程式碼中,通過為元件指定 Click 事件的回撥函式,確保了只有等到真實 DOM 發生 Click 事件之後,才會讀取 this.refs.[refName] 屬性。

React 元件支援很多事件,除了 Click 事件以外,還有 KeyDownCopyScroll 等,完整的事件清單請檢視官方文件

八、this.state

元件免不了要與使用者互動,React 的一大創新,就是將元件看成是一個狀態機,一開始有一個初始狀態,然後使用者互動,導致狀態變化,從而觸發重新渲染 UI (檢視 demo08 )。


var LikeButton = React.createClass({
  getInitialState: function() {
    return {liked: false};
  },
  handleClick: function(event) {
    this.setState({liked: !this.state.liked});
  },
  render: function() {
    var text = this.state.liked ? 'like' : 'haven\'t liked';
    return (
      <p onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </p>
    );
  }
});

ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

上面程式碼是一個 LikeButton 元件,它的 getInitialState 方法用於定義初始狀態,也就是一個物件,這個物件可以通過 this.state 屬性讀取。當用戶點選元件,導致狀態變化,this.setState 方法就修改狀態值,每次修改以後,自動呼叫 this.render 方法,再次渲染元件。

由於 this.propsthis.state 都用於描述元件的特性,可能會產生混淆。一個簡單的區分方法是,this.props 表示那些一旦定義,就不再改變的特性,而 this.state 是會隨著使用者互動而產生變化的特性。

九、表單

使用者在表單填入的內容,屬於使用者跟元件的互動,所以不能用 this.props 讀取(檢視 demo9 )。


var Input = React.createClass({
  getInitialState: function() {
    return {value: 'Hello!'};
  },
  handleChange: function(event) {
    this.setState({value: event.target.value});
  },
  render: function () {
    var value = this.state.value;
    return (
      <div>
        <input type="text" value={value} onChange={this.handleChange} />
        <p>{value}</p>
      </div>
    );
  }
});

ReactDOM.render(<Input/>, document.body);

上面程式碼中,文字輸入框的值,不能用 this.props.value 讀取,而要定義一個 onChange 事件的回撥函式,通過 event.target.value 讀取使用者輸入的值。textarea 元素、select元素、radio元素都屬於這種情況,更多介紹請參考官方文件

十、元件的生命週期

元件的生命週期分成三個狀態:

  • Mounting:已插入真實 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真實 DOM

React 為每個狀態都提供了兩種處理函式,will 函式在進入狀態之前呼叫,did 函式在進入狀態之後呼叫,三種狀態共計五種處理函式。

  • componentWillMount()
  • componentDidMount()
  • componentWillUpdate(object nextProps, object nextState)
  • componentDidUpdate(object prevProps, object prevState)
  • componentWillUnmount()

此外,React 還提供兩種特殊狀態的處理函式。

  • componentWillReceiveProps(object nextProps):已載入元件收到新的引數時呼叫
  • shouldComponentUpdate(object nextProps, object nextState):元件判斷是否重新渲染時呼叫

這些方法的詳細說明,可以參考官方文件。下面是一個例子(檢視 demo10 )。


var Hello = React.createClass({
  getInitialState: function () {
    return {
      opacity: 1.0
    };
  },

  componentDidMount: function () {
    this.timer = setInterval(function () {
      var opacity = this.state.opacity;
      opacity -= .05;
      if (opacity < 0.1) {
        opacity = 1.0;
      }
      this.setState({
        opacity: opacity
      });
    }.bind(this), 100);
  },

  render: function () {
    return (
      <div style={{opacity: this.state.opacity}}>
        Hello {this.props.name}
      </div>
    );
  }
});

ReactDOM.render(
  <Hello name="world"/>,
  document.body
);

上面程式碼在hello元件載入以後,通過 componentDidMount 方法設定一個定時器,每隔100毫秒,就重新設定元件的透明度,從而引發重新渲染。

另外,元件的style屬性的設定方式也值得注意,不能寫成


style="opacity:{this.state.opacity};"

而要寫成


style={{opacity: this.state.opacity}}

這是因為 React 元件樣式是一個物件,所以第一重大括號表示這是 JavaScript 語法,第二重大括號表示樣式物件。

十一、Ajax

元件的資料來源,通常是通過 Ajax 請求從伺服器獲取,可以使用 componentDidMount 方法設定 Ajax 請求,等到請求成功,再用 this.setState 方法重新渲染 UI (檢視 demo11 )。


var UserGist = React.createClass({
  getInitialState: function() {
    return {
      username: '',
      lastGistUrl: ''
    };
  },

  componentDidMount: function() {
    $.get(this.props.source, function(result) {
      var lastGist = result[0];
      if (this.isMounted()) {
        this.setState({
          username: lastGist.owner.login,
          lastGistUrl: lastGist.html_url
        });
      }
    }.bind(this));
  },

  render: function() {
    return (
      <div>
        {this.state.username}'s last gist is
        <a href={this.state.lastGistUrl}>here</a>.
      </div>
    );
  }
});

ReactDOM.render(
  <UserGist source="https://api.github.com/users/octocat/gists" />,
  document.body
);

上面程式碼使用 jQuery 完成 Ajax 請求,這是為了便於說明。React 本身沒有任何依賴,完全可以不用jQuery,而使用其他庫。

我們甚至可以把一個Promise物件傳入元件,請看Demo12


ReactDOM.render(
  <RepoList
    promise={$.getJSON('https://api.github.com/search/repositories?q=javascript&sort=stars')}
  />,
  document.body
);

上面程式碼從Github的API抓取資料,然後將Promise物件作為屬性,傳給RepoList元件。

如果Promise物件正在抓取資料(pending狀態),元件顯示"正在載入";如果Promise物件報錯(rejected狀態),元件顯示報錯資訊;如果Promise物件抓取資料成功(fulfilled狀態),元件顯示獲取的資料。


var RepoList = React.createClass({
  getInitialState: function() {
    return { loading: true, error: null, data: null};
  },

  componentDidMount() {
    this.props.promise.then(
      value => this.setState({loading: false, data: value}),
      error => this.setState({loading: false, error: error}));
  },

  render: function() {
    if (this.state.loading) {
      return <span>Loading...</span>;
    }
    else if (this.state.error !== null) {
      return <span>Error: {this.state.error.message}</span>;
    }
    else {
      var repos = this.state.data.items;
      var repoList = repos.map(function (repo) {
        return (
          <li>
            <a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}
          </li>
        );
      });
      return (
        <main>
          <h1>Most Popular JavaScript Projects in Github</h1>
          <ol>{repoList}</ol>
        </main>
      );
    }
  }
});

十二、參考連結

(完)

相關推薦

(react.js快速入門教程

React 入門例項教程 作者: 阮一峰 現在最熱門的前端框架,毫無疑問是 React 。 上週,基於 React 的 React Native 釋出,結果一天之內,就獲得了 5000 顆星,受矚目程度可見一斑。 React 起源於 Facebook 的內部專

React.js快速入門教程

目錄: 1. 簡介 1. Hello React! 1. 虛擬DOM 1. React元件 1. 輪子來了:JSX 1. 使用JSX 簡介 React是Facebook開源的一個用於構建使用者介面的Javascript庫,已經 應

doodoo.js快速入門教程?

node Doodoo.js -- 中文最佳實踐Node.js Web快速開發框架。支持Koa.js, Express.js中間件,支持模塊化,插件,鉤子機制,可以直接在項目裏使用 ES6/7(Generator Function, Class, Async & Await)等特性 htt

doodoo.js快速入門教程

touch es6 odoo node 框架 創建 init fun http Doodoo.js -- 中文最佳實踐Node.js Web快速開發框架。支持Koa.js, Express.js中間件,支持模塊化,插件,鉤子機制,可以直接在項目裏使用 ES6/7(Gener

【全開源+免費更新】doodoo.js快速入門教程

簡介 ​ Doodoo.js -- 中文最佳實踐Node.js快速開發框架。支援Koa.js, Express.js中介軟體,支援模組機制,外掛機制,鉤子機制,讓開發 Node.js 專案更加簡單、高效、靈活。   特性 支援koa全部中介軟體 支援使用 ES6

21分鐘 MySQL 入門教程

感覺寫的比較好,簡單的mysql入門,與大家分享,中間加了一些自己的標註 MySQL的相關概念介紹 MySQL 為關係型資料庫(Relational Database Management System), 這種所謂的"關係型"可以理解為"表格"的概念, 一個關係

小程序開發快速入門教程(附源碼

五分鐘上手-微信小程序 1:用沒有註冊過微信公眾平臺的郵箱註冊一個微信公眾號, 申請帳號 ,點擊 https://mp.weixin.qq.com/wxopen/waregister?action=step1 根據指引填寫信息和提交相應的資料,就可以擁有自己的小程序帳號。註冊完成之後開始登

react.js入門到精通(五——組件之間的數據傳遞

efault def 通道 span 代碼 serve urn div mar 一、組件之間在靜態中的數據傳遞 從上面的代碼我們可以看出,兩個組件之間的數據是分離,但如果我們有某種需求,將數據從一個組件傳到另一個組件中,該如何實現? 場景設計: 將Home.js中的Home

Koa快速入門教程(一

Koa 是由 Express 原班人馬打造的,致力於成為一個更小、更富有表現力、更健壯的 Web 框架,採用了async和await的方式執行非同步操作。 Koa有v1.0與v2.0兩個版本,隨著node對async和await的支援,Koa2的正式釋出,本文Koa均

springBoot快速入門教程(第一篇

Springboot是什麼? 1、springboot是在spring的基礎上進一步封裝,讓以前繁雜的配置全部簡化,從而快速構建專案。 2、對主流框架無配置整合,提高開發效率。 3、內建tomcat無需先部署在執行 4、與雲端計算天然整合 快速搭建一個簡單的springboot

【Android】Android快速入門教程(五——2——logcat控制檯

文章目錄 一、logcat是什麼? 二、logcat有什麼用? 三、我該怎麼列印自己要的日誌內容 一、logcat是什麼? 一般情況可以在下圖所示位置可以找到logcat控制檯,如果找不到的,可以在Android studi

SciKit-learn快速入門教程和例項(二

一,sklearn的常用屬性和功能 繼續上次對波士頓房價預測的討論,瞭解模型的屬性和功能。 #首先匯入庫,使用的是線性迴歸 from sklearn import datasets from sklearn.linear_model import LinearRegr

Scikit-learn快速入門教程和例項(一

一,什麼是SKlearn SciKit learn的簡稱是SKlearn,是一個python庫,專門用於機器學習的模組。 以下是它的官方網站,文件等資源都可以在裡面找到http://scikit-learn.org/stable/#。

MiniUI快速入門教程(三CRUD

CRUD包括:查詢、編輯、新增、刪除等資料操作。 效果圖如下: 一:建立DataGrid 首先,我們建立一個數據表格: <div id="datagrid1" class="mini-datagrid" style="width:800px;height:280px;" url="../da

小程式開發快速入門教程(附原始碼

五分鐘上手-微信小程式image我們可以在選單 “設定”-“開發設定” 看到小程式的 AppID 了 。小程式的 AppID 相當於小程式平臺的一個身份證,後續你會在很多地方要用到 AppID (注意這裡要區別於服務號或訂閱號的 AppID)。有了小程式帳號之後,我們需要一個工具來開發

spring cloud快速入門教程(四路由閘道器Zuul

現在服務也統一註冊管理了,配置也統一管理了,我們就可以瘋狂的開發各項微服務了,是不是還覺得少了點什麼?前端怎麼訪問到相應服務?這就用到路由網關了。 路由閘道器就是整個微服務的統一入口,看看第一張的架構圖,專案的前端做成了動靜分離,靜態檔案、html頁面、css檔案和js檔案

spring cloud快速入門教程(六程序間呼叫和微服務負載均衡(Feign

RestTemplate是不是很簡單粗暴呢?還有更粗暴的,那就是Feign,很多人都用過Dubbo,Feign的用法跟他類似。 我們複用userService那個module去呼叫productService微服務中的getProduct介面,引入Feign的依賴包: &

react.js入門到精通(一

web端三大框架react、vue和angular,下面是對react.js的一些總結。 一、環境搭建 1、npm搭建專案 推薦使用npm搭建專案環境,如果網速過慢,可是使用cnpm進行專案的搭建(cnpm是淘寶的npm映象,與npm有些差異,有些模組無法下載或無法正常

Numpy快速入門教程(三:SciPy,Matplotlib

首先宣告本篇部落格是本人學習CS231n的學習筆記,分享給大家當作參考。 SciPy Numpy提供了高效能的多維陣列,以及計算和運算元組的基本工具。SciPy基於Numpy,提供了大量的計算和運算元組的函式,這些函式對於不同型別的科學和工程計算非常有用。

Prometheus 入門教程(一:Prometheus 快速入門

# 文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/ZXlBPHGcWeYh2hjBzacc3A Prometheus 是任何一個高階工程師必須要掌握的技能。那麼如何從零部署一套 Prometheus 監控系統呢?本篇文章將從 Prometheus 的原理講