React從0到1系列第二章——React的使用方式和基礎API介紹

1、React簡介

由於以前作了挺久的Vue開發。如今開始學習React也是並不難的。首先要對React的總體脈絡要有個大概的瞭解。(注:這裏只作學習的記錄和總結,有些可能會遺漏,後期有了新的感悟也會慢慢完善,一些基礎的語法的使用仍是要看官方網站爲主)。javascript

從上圖能夠得知React分爲三大致系:css

  1. React.js
  2. ReactNative
  3. ReactVR

學習的順序也是React.js->ReactNative->ReactVR按部就班的。html

React的基礎大致包括下面這些概念:

  1. 組件
  2. JSX
  3. Virtual DOM
  4. Data Flow

React.js不是一個框架,它只是一個庫。它只提供 UI (view)層面的解決方案。在實際的項目當中,它並不能解決咱們全部的問題,須要結合其它的庫,例如 ReduxReact-router 等來協助提供完整的解決方法。前端

2、React開發環境的搭建

2.一、在瀏覽器中編寫React

<!DOCTYPE html>
<html> <head>  <meta charset="UTF-8">  <!--https://cdnjs.com/-->  <script src="./js/react.development.js"></script>  <script src="./js/react-dom.development.js"></script>  <script src="./js/browser.min.js"></script>  <script src="./js/prop-types.min.js"></script> </head> <body>  <div id="root"></div>  <!--凡是使用 JSX 的地方,都要加上 type="text/babel"-->  <script type="text/babel">  class Hello extends React.Component {  render() {  return (<h1>Hello,{this.props.name}</h1>)  }  }  ReactDOM.render(  <Hello name="zhangsan" />,  document.getElementById('root')  );  </script> </body> </html> 複製代碼

上面代碼一共用了四個庫:react.jsreact-dom.jsbrowser.jsprop-types.min.js,它們必須首先加載。vue

  1. react.jsReact的核心庫。
  2. react-dom.js:負責 Web頁面的 DOM操做。
  3. browser.js:將 JSX語法轉爲 JavaScript語法。
  4. prop-types.min.js:是傳入的 props類型的驗證。自 React v15.5起, React.PropTypes已移入另外一個包中,使用 prop-types庫代替。

須要注意的是從Babel 6.0開始,再也不直接提供瀏覽器直接編譯的JS版本,而是要用構建工具構建出來。這裏只作演示用。建議學習開發的時候也不要使用瀏覽器直接引入的方式。java

2.二、 搭建本地開發環境

  1. 安裝 node.js
  2. 安裝 npm
  3. 全局安裝官方推薦腳手架工具 create-react-app,相似於 Vuevue-cli
npm install -g create-react-app
// 在任意文件夾中使用create-react-app新建項目 create-react-app demo 複製代碼

安裝成功以後,npm start 或者 yarn start就能夠啓動項目了。node

create-react-app生成的目錄結構以下:react

demo/
 README.md  package.json  yarn.lock  .gitignore  node_modules/  public/  favicon.ico  index.html  ...  src/  App.css  App.js  App.test.js  index.css  index.js  logo.svg  serviceWorker.js 複製代碼

package.json安裝的React依賴介紹以下:webpack

package.jsondependencies能夠看出來腳手架工具默認安裝了React須要的依賴。下面就介紹這些核心依賴的做用:git

  1. react:是 React的核心庫。
  2. react-dom:負責 Web頁面的 DOM操做。
  3. react-scripts:生成項目全部的依賴。例如 babelcss-loader, webpack等從開發到打包前端工程化所須要的 react-scripts都幫咱們作好了。

如今就能夠在App.js裏編寫咱們本身的代碼了,以下(已修改生成的源碼):

在項目根目錄執行npm start(若安裝了yarn可以使用yarn start),打開http://localhost:3000就能夠看到首頁了:

建議你們參考Create React App 中文文檔

3、Virtual DOM 介紹

要想理解JSX的由來,就要先介紹一下Virtual DOM

一個真實頁面對應一個DOM樹。在傳統頁面的開發模式中,每次須要更新頁面時,都要手動操做DOM 來進行更新。DOM操做很是昂貴。並且這些操做DOM的代碼變得難以維護。

React把真實DOM樹轉換成JavaScript對象樹,也就是Virtual DOM。以下圖:

每次數據更新後,從新計算Virtual DOM,並和上一次生成的Virtual DOM作對比,對發生 變化的部分作批量更新。VirtualDOM不只提高了React的性能,並且它最大的好處其實還在於方便和其餘平臺集成(好比react-native是基於Virtual DOM渲染出的原生控件)。

所以在Virtual DOM輸出的時候,是輸出Web DOM,仍是Android控件,仍是iOS控件,由平臺自己決定。

3.一、DOM 元素

Web頁面是由一個個HTML元素嵌套組合而成的。當使用JavaScript來描述這些元素的時候,這些元素能夠簡單地被表示成純粹的JSON對象。好比,咱們如今須要描述一個按鈕(button),用HTML 語法表示很是簡單:

<button class="primary">
 <em>submit!</em> </button> 複製代碼

其中包括了元素的類型和屬性。若是轉成JSON對象,會包括元素的類型以及屬性:

{
 type: 'button',  props: {  className: 'primary',  children: [{  type: 'em',  props: {  children: 'submit'  }  }]  } } 複製代碼

上面的JS對象表達了一個按鈕功能。在表達還不怎麼複雜的結構時,書寫就已經很難受了。這讓咱們想起使用HTML編寫結構時的簡潔。因此JSX語法爲此應運而生。假如咱們使用JSX 語法來從新表達上述button元素,只需下面這麼寫:

ReactDOM.render(
 <button className="primary">  <em>submit!</em>  </button>  , document.getElementById('root')) 複製代碼

4、JSX介紹

JSXHTML語法直接加入到JavaScript代碼中,會讓代碼更加直觀並易於維護。經過編譯器轉換到純JavaScript後由瀏覽器執行。JSX在產品打包階段都已經編譯成了純JavaScript

注意:JSXJavaScript語言的一種語法擴展,長得像HTML,但並非HTML。儘管JSX是第三方標準,但這套標準適用於任何一套框架。如今已所有采用BabelJSX編譯器來實現對JSX語法的編譯。

class Test extends React.Component {
 constructor(props) {  super(props)  }  render() {  return (  <div>  <h1 className='title'>Hello,React</h1>  </div>  )  } } ReactDOM.render(<Test />, document.getElementById('root')) 複製代碼

上面的代碼通過編譯之後會變成以下:

class Test extends React.Component {
 constructor(props) {  super(props)  }  render() {  return (  React.createElement(  'div',  null,  React.createElement(  'h1',  { className: 'title' },  'Hello,React'  )  )  )  } } ReactDOM.render(<Test />, document.getElementById('root')) 複製代碼

React.createElement會構建一個JavaScript對象來描述HTML結構的信息,包括標籤名、屬性、還有子元素等。這樣的代碼就是合法的JavaScript代碼了。

因此從JSX到頁面通過了以下圖的過程:

4.一、使用 HTML 標籤

ReactDOM.render(<div className="foo">Hello</div>, document.getElementById('root'))
複製代碼

HTML裏的classJSX裏要寫成className,由於classJS 裏是保留關鍵字。同理某些屬性好比for要寫成htmlFor

4.二、使用 JavaScript 表達式

JSX遇到HTML標籤(以<開頭),就用HTML規則解析。遇到代碼塊(以 { 開頭),就用 JavaScript規則解析。

const names = ['zhangsan', 'lisi', 'wangwu']
ReactDOM.render(  <div>  {  names.map((name,key) => {  return <div key={key}>Hello,{name}</div>  })  }  </div>,  document.getElementById('root') ) 複製代碼

JSX容許直接在模板插入JavaScript變量。若是這個變量是一個數組,則會展開這個數組的全部成員。

const names = [<div key="1">zhangsan</div>, <div key="2">lisi</div>]  ReactDOM.render(  <div>  {names}  </div>,  document.getElementById('root') ) 複製代碼

4.三、 註釋

JSX裏使用註釋也很簡單,就是沿用JavaScript,惟一要注意的是在一個組件的子元素位置使用註釋要用 {} 包起來。

const App = (
 <Nav>  {/* 節點註釋 */}  <Person  /* 多行  註釋 */  name={window.isLoggedIn ? window.name : ''}  />  </Nav> ) 複製代碼

4.四、 HTML轉義

React會將全部要顯示到DOM的字符串轉義,防止XSS。因此若是JSX中含有轉義後的實體字符好比 &copy (©) 最後顯示到 DOM中不會正確顯示,由於React自動把&copy中的特殊字符轉義了。可使用dangerouslySetInnerHTML來實現。

ReactDOM.render(
 <div dangerouslySetInnerHTML={{ __html: '&copy 2020' }} />,  document.getElementById('root') ) 複製代碼

4.五、自定義 HTML 屬性

若是在JSX中使用的屬性不存在於HTML的規範中,這個屬性會被忽略。若是要使用自定義屬性,能夠用data-前綴。可訪問性屬性的前綴aria-也是支持的。

ReactDOM.render(<div data-attr="abc">content</div>, document.getElementById('root'))
複製代碼

5、組件化

React認爲組件是和模板緊密關聯的,組件模板和組件邏輯分離讓問題複雜化了。React 容許將代碼封裝成組件(component),而後像插入普通 HTML 標籤同樣,在網頁中插入這個組件。一個React應用就是構建在React 組件之上的。Component(組件)能夠是類組件(class component)、函數式組件(function component)。

組件有三個核心概念,React組件基本上由組件的構建方式組件內的屬性狀態生命週期方法組成。以下圖:

  • props(屬性)
  • states(狀態)
  • 生命週期方法

注意:組件生成的 HTML 結構只能有一個單一的根節點。在React中,數據是自頂向下單向流動的,即從父組件到子組件。

5.一、組件構建方法

官方在React組件構建上提供了3種不一樣的方法:React.createClassES6 classes 和無狀態 函數(stateless function)。

官方在React@15.5.0後不推薦用React.createClass,建議使用ES6 class,這裏不作具體介紹。

ES6 classes

class Test extends React.Component {
 constructor(props) {  super(props)  }  render() {  return (<h1>Hello,React</h1>)  } } 複製代碼

無狀態組件

能夠用純函數來定義無狀態的組件(stateless function),這種組件沒有狀態,沒有生命週期,只是簡單的接受props渲染生成DOM結構。無狀態組件很是簡單,開銷很低。好比使用箭頭函數定義:

const Hello = (props) => <div>Hello {props.name}</div>
 ReactDOM.render(<Hello name="張三" />, document.getElementById('root')) 複製代碼

5.二、props

props就是組件的屬性,由外部經過JSX屬性傳入設置,一旦初始設置完成,就能夠認爲this.props 是不可更改的,因此不要輕易更改設置this.props裏面的值。

class Hello extends React.Component {
 constructor(props) {  super(props)  }  render() {  return (  <h1>Hello,{this.props.name}</h1>  )  } }  // PropTypes 驗證,若傳入的props type不是string將提示錯誤 Hello.propTypes = {  name: PropTypes.string }  // Prop 初始值,若是 prop 沒有傳入值將會使用 default 值 lisi Hello.defaultProps = {  name: 'lisi' }  ReactDOM.render(<Hello name="張三" />, document.getElementById('root'))  複製代碼

5.三、state

state是組件的當前狀態,用this.state來存取state。一旦狀態(數據)更改,組件就會自動調用 render從新渲染UI,這個更改的動做會經過this.setState方法來觸發。

下面代碼是一個1000毫秒就會加一的累加器:

class Timer extends React.Component {
 constructor(props) {  super(props)  // 須要自行綁定 this context  this.tick = this.tick.bind(this)  this.state = {  count: 0  }  }  // 累加器方法,每一秒會使用 setState() 更新內部 state,讓 Component 從新 render  tick() {  this.setState({ count: this.state.count + 1 })  }  // 生命週期函數  componentDidMount() {  this.interval = setInterval(this.tick, 1000);  }  // 生命週期函數  componentWillUnmount() {  clearInterval(this.interval);  }  render() {  return (  <div>Count: {this.state.count}</div>  )  } } ReactDOM.render(<Timer />, document.getElementById('root')) 複製代碼

5.四、生命週期方法

每一個組件都包含都包含組件生命週期方法,在運行過程當中特定的階段執行這些方法。

組件的生命週期分爲四類,共有10個方法:

  1. 掛載
  2. 更新
  3. 卸載
  4. 錯誤處理

下面會依次介紹這幾類組件的生命週期所調用的函數。

掛載

當組件實例被建立並插入DOM中時,其生命週期調用順序以下:

  • constructor()
  • static getDerivedStateFromProps():會在調用 render方法以前調用,而且在初始掛載及後續更新時都會被調用。
  • render():是 class組件中惟一必須實現的方法。
  • componentDidMount():在組件掛載後(插入 DOM樹中)當即調用。

更新

當組件的propsstate發生變化時會觸發更新。組件更新的生命週期調用順序以下:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate():當 propsstate發生變化時, shouldComponentUpdate() 會在渲染執行以前被調用。
  • render()
  • getSnapshotBeforeUpdate():在最近一次渲染輸出(提交到 DOM節點)以前調用。
  • componentDidUpdate():會在更新後會被當即調用。首次渲染不會執行此方法。

卸載

當組件從DOM中移除時會調用以下方法:

  • componentWillUnmount():在組件卸載及銷燬以前直接調用。

錯誤處理

當渲染過程,生命週期,或子組件的構造函數中拋出錯誤時,會調用以下方法:

  • static getDerivedStateFromError():今生命週期會在後代組件拋出錯誤後被調用。
  • componentDidCatch():今生命週期在後代組件拋出錯誤後被調用。

這裏只作簡單介紹,後面會單獨寫文章詳細記錄生命週期的使用方式。

6、事件處理

React裏面綁定事件的方式和在HTML中綁定事件相似,使用駝峯式命名指定要綁定的onClick屬性爲組件定義的一個方法。下面是一個數字累加的小例子:

class AddCount extends React.Component {
 constructor(props) {  super(props)  this.state = {  count: 0  }  }  handleClick(e) {  this.setState({ count: this.state.count + 1 });  }  render() {  return (  <div>  <p>Count: {this.state.count}</p>  <button onClick={this.handleClick.bind(this)}>Click me</button>  </div>  )  } } ReactDOM.render(<AddCount />, document.getElementById('root')) 複製代碼

注意要顯式調用bind(this)將事件函數上下文綁定要組件實例上,這也是React推崇的原則:沒有黑科技,儘可能使用顯式的容易理解的JavaScript代碼。

7、 DOM操做

有時候咱們避免不了要直接操做DOMReact也提供了幾種咱們能夠直接操做DOM的方式。

findDOMNode

當組件加載到頁面上以後(mounted),能夠經過react-dom提供的findDOMNode()方法拿到組件對應的DOM元素。

class Test extends React.Component {
 constructor(props) {  super(props)  }  componentDidMount() {  const el = ReactDOM.findDOMNode(this)  // 123456  console.log(el.textContent)  }  render() {  return (  <div>123456</div>  )  } } ReactDOM.render(<Test />, document.getElementById('root')) 複製代碼

注意:findDOMNode() 不能用在無狀態組件上。findDOMNode 僅在組件始終返回永不更改的單個 DOM 節點時才起做用。在<React.StrictMode>嚴格模式下,這個方法已經被官方棄用,因此在開發中不要使用這個方法。

Refs

另一種方式就是經過在要引用的DOM元素上面設置一個ref屬性指定一個名稱,而後經過 this.refs.name來訪問對應的DOM元素。

class Test extends React.Component {
 constructor(props) {  super(props)  }  componentDidMount() {  this.refs.textInput.focus()  }  render() {  return (  <div>  <input ref="textInput" />  </div>  )  } } ReactDOM.render(<Test />, document.getElementById('root'))  複製代碼
  1. 不要在 render或者 render以前訪問 refs
  2. 不要濫用 refs。好比只是用它來按照傳統的方式操做界面 UI:找到 DOM -> 更新 DOM

參考

https://zh-hans.reactjs.org/

http://huziketang.mangojuice.top/books/react/

https://www.bilibili.com/video/BV1g4411i7po?p=2

https://www.jianshu.com/p/c6040430b18d

https://www.zhihu.com/question/336664883/answer/790855896

https://book.douban.com/subject/26918038/

https://www.kancloud.cn/digest/babel/217110

http://www.ruanyifeng.com/blog/2015/03/react.html

https://kdchang.gitbooks.io/react101/content/

https://github.com/mocheng/react-and-redux/issues/99

https://juejin.im/entry/587de1b32f301e0057a28897

https://www.html.cn/create-react-app/docs/getting-started/

本文使用 mdnice 排版

相關文章
相關標籤/搜索