React Element /組件/JSX

  學習React的時候,你可能聽到最多的就是要先學習webpack, babel,要先學會配置而後才能學react 等等,一堆的配置就把咱們嚇着了,根本就沒有心情就學習react了。其實在最開始學習react, 想要了解React 是什麼的時候,咱們徹底不用配置,直接用script標籤 引入React就能夠了, 不過要注意,這裏須要引入兩個庫:React 和ReactDom。React 用來建立UI, ReactDom 負責把React 建立的UI 渲染到瀏覽器中. 初學react只需下面這個模版就能夠了。css

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
    <!-- 引入React 和 ReactDOM -->
    <script src="https://cdn.bootcss.com/react/16.1.0/umd/react.development.js"></script>
    <script src="https://cdn.bootcss.com/react-dom/16.1.0/umd/react-dom.development.js
    "></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      // react 代碼寫在這裏
    </script>
  </body>
</html>

  這裏,頁面中還增長了一個id 爲root 的div, 它的做用是告訴ReactDom 要把UI 渲染到什麼地方。注意,咱們這裏先不使用JSX語法,從而瞭解一下JSX的由來,或它底層的東西。好了,咱們開始學習React 吧. html

  React Elementreact

  在學習React的時候,遇到的第一個概念可能就是virtual DOM或React element, 它們其實對應的是瀏覽器DOM 或 HTML元素。在作JS 開發的時候, 你們都知道,DOM 操做很慢,咱們都會盡可能避免操做DOM, 那麼怎麼才能更快地操做DOM呢?React 提出了virtual DOM的概念, 由於這份DOM 不是在瀏覽器中,而是在內存中,瀏覽器中的DOM是真實的DOM,它能直接渲染出來,但內存中的DOM 又和瀏覽器的DOM 一致,因此叫virtual DOM, 同理,瀏覽器中的DOM是因爲一個個HTML element 組成,那麼做爲對應,virtual DOM 就是由一個個的React element 組成。概念老是抽象的,咱們寫一下代碼瞭解一下。webpack

  React element, 是由React 庫提供的createElement 函數建立而來,咱們建立一個React element,css3

let h1Elem = React.createElement('h1', {id: 'recipe', 'data-type': 'title'}, 'Hello World');

  你能夠看到 createElement 函數接受三個參數,第一個是type, 咱們要建立哪一種類型的React Element, 在這裏,咱們建立了h1 element. 第二個是properties, 這個element 有哪些屬性,在這裏有兩個屬性,一個是id, 一個是data-type. 第三個參數是children, 這個元素有哪些子元素。經過分析,咱們也能夠猜想出h1 大概長什麼樣子。 <h1 id=’recipe’  data-type=’title’ ></h1>es6

   這時咱們打開瀏覽器,刷新一下頁面,什麼也沒有,咱們建立了react element, 到底發生了什麼事情呢?這時咱們 console.log(h1Elem)  一下web

  咱們看到它只是一個Js 對象,擁有props,type等等屬性, 和html element 沒有任何關係, 可是經過它,咱們卻準確地描述了 html element 長什麼樣子,因此它是virtual, 用於指導構造真正的html element.數組

  當React element 多了起來,層層嵌套,就成了一個DOM 樹,這就是所謂virtual DOM. createElement 方法怎麼才能造成層層嵌套關係呢? 這主要在於這個函數能夠接受任意個參數,從第三個參數及其之後的參數,都會當成建立的element 的children 來對待。咱們建立一個ul--li 的結構瀏覽器

let ulElem = React.createElement(
        'ul', 
        null ,
        React.createElement('li', null, 'JS'),
        React.createElement('li', null, 'React'),
        React.createElement('li', null, 'Redux')
      );

   經過這個createElement 函數,咱們也能夠很清楚地知道它表明下面的ul-libabel

  當咱們用React 建立出虛擬Dom, 怎樣才能渲染到html 頁面中,造成真實的DOM呢?那就用到了react-dom 庫了,它裏面有一個render 方法, 能夠把虛擬DOM 轉化成真實的DOM,  用法以下

 ReactDOM.render(ulElem, document.getElementById('root'));

  它接受兩個參數,一個是要轉化的Element, 一個是渲染到什麼地方, 這時咱們刷新瀏覽器,能夠看到3個li, 和咱們用createElement 

建立的元素一一對應。

  React Component

  當咱們寫大量的React element  的時候,你會發現有些代碼能夠共用,就像咱們上面建立的ulElem,能夠用到不少地方,這時咱們就想把它們封裝起來,這就造成了React component, 組件。在js 中代碼封裝有兩種方法,一種是函數,一種是對象,相應的React 也提供了建立組件的兩種方法,一種是類式,類式就是對象封裝,一種是函數式。

  類式,則是利用es6 class 語法, 咱們全部的組件都繼承自React.Component,在render 函數中返回React Element

      class Content extends React.Component {

        render() {
          return React.createElement('ul', null ,
            React.createElement('li', null, 'JS'),
            React.createElement('li', null, 'React'),
            React.createElement('li', null, 'Redux')
          );
        }
      }

  函數式,就是一個函數,返回React element

      const Content = () => {
        return  React.createElement('ul', null, 
          React.createElement('li', null, 'JS'),
          React.createElement('li', null, 'React'),
          React.createElement('li', null, 'Redux')
        )
      }

  咱們聲明瞭組件之後,怎麼使用這個組件呢? 這時要注意,咱們的組件名稱(Content),只是至關於createElement 函數中的第一個參數type, 它不是一個React element, 它只是返回一個React element, 咱們仍然須要調用React.createElement 來建立一個React element

      // 咱們建立的組件名Content, 只是至關於createElement函數中的第一個參數type, 它是至關於h1的type, 而不是一個React Elemnet. 利用這個type,仍須要建立組件。
      let content = React.createElement(Content, null, null);
      
      ReactDOM.render(content, document.getElementById('root'));

  這時頁面中一樣顯示三個li, 表示成功。

  總結: 建立組件,只是把一段能夠重用的React element 進行封裝,從而建立一個自定義的type, 而後再利用該type, 隨處均可以建立element 元素,進而達到重用的目的。

  React 組件數據

  這時,你會發現咱們組件全部的數據都是硬綁定的,能不能把數據和 UI的建立分開,從而使咱們的組件更具備通用性,不一樣的數據渲染出不一樣的內容? 這是能夠的,當咱們利用組件建立react element 的時候,第二個參數是null, 能夠再回顧一下代碼

let content = React.createElement(Content, null, null);

  咱們能夠利用這個參數向組件內傳遞數據,由於這個參數就是表示這個組件的屬性,它的形式也是一個鍵值對的形式,好比咱們把JS, React ,Redux 數據提出來,造成一個數組,

const item = ['JS', 'React', 'Redux'];
// 向組件中傳遞一個數組數據 item 
let content = React.createElement(Content, {item: item}, null);

  那麼咱們組件中怎麼獲取到這個數據呢?這要分兩種狀況,類式的組件則是經過this.props獲取的,而函數式組件,則是經過傳參的形式獲取

  先看類式的組件, 在組件中經過this.props 來獲取, 咱們能夠把this.props 找印出來看一下

      class Content extends React.Component {
        render() {
          // 打印props;
          console.log(this.props);  // {item: Array(3), children: null}
          return React.createElement('ul', null ,
            React.createElement('li', null, 'JS'),
            React.createElement('li', null, 'React'),
            React.createElement('li', null, 'Redux')
          );
        }
      }

  能夠看到this.props 獲取到了咱們傳遞的item 數組,那咱們就能夠直接使用了數組數據了,這時經過數組的map 渲染li

class Content extends React.Component {
  render() {
    return React.createElement('ul', null ,
      // this.props.item 獲取到傳遞過來的數據
      this.props.item.map((item, index) => 
        React.createElement('li', {key:index}, item)
      )
    );
  }
}

  函數式的組件,則是它自動會獲取props做爲參數,組件中直接使用props.items 獲取到數據

// 自動獲取props做爲參數。
const Content = (props) => {
  return  React.createElement('ul', null, 
    props.item.map((item, index) => 
      React.createElement('li', {key:index}, item)
    )
  )
}

  JSX

  在上面的代碼中,咱們每建立一個React Element 都要調用一次React.createElement 函數,很是繁瑣,而且它想表達式的意思只是一個類html的元素,再來看一下咱們建立的h1Elem element 

let h1Elem = React.createElement('h1', {id: 'recipe', 'data-type': 'title'}, 'Hello World');

  它實際上表達的意思就是 <h1 id=’recipe’  data-type=’title’ ></h1>, 若是我能在代碼中直接寫h1 就行了。這就是能夠了,它就是JSX 語法, 能夠直接在js 代碼中寫類html 的語法。React 把createElement 函數做了進一步的封裝,提供了JSX語法。爲何能直接寫呢?其實createElement 函數,和html 元素有一一對應的關係。

  在createElement 函數中,它的第一個參數是type,表示建立什麼類型,而在html中,表示什麼類型直接用html 標籤,<h1></h1>  它就表示h1 類型, 第二個參數表示屬性,元素有哪些屬性,而在html標籤中,有什麼屬性,就直接寫在它的標籤中,有多少,寫多少, 如 <h1 id='recipe' class='title'></h1>. 第三個參數是children, 在html中表示children更簡單,直接寫在兩個標籤內部的內容都是children. 這樣一一對應之後,就能夠理解JSX 寫法的用意了,在心理上寫起來就比較舒服了,由於明白了。

  對於組件來講,它也是同樣的,由於組件名稱,只是一個type, 仍然須要調用createElement 函數來 建立React Element 元素, 只要使用createElement 的地方,咱們均可以使用類html 語法,如Content組件,<Content></Content>  就表示建立了一個element了。它的屬性和children和上面的h1 用法一致。對於Content 組件,若是沒有 chilren 屬性,能夠直接寫單標籤<Content />.   如今用JSX的語法來書寫Content 組件。

class Content extends React.Component {
  render() {
    return (
      <ul>
        {
          this.props.item.map((item, index) => 
            <li key={index}>{item}</li>
          )
        }
      </ul>  
    )
  }
}

// 向組件中傳遞一個數組數據 item 
let content = <Content item ={item}></Content>

  不管是在組件屬性,仍是元素屬性中,咱們都使用了{}, 如key={index}. 在jsx 中,{}裏面的全部東西都看成js表達式進行解析, React 會把裏面的內容進行求值計算。只要寫表達式,咱們都要用{} 括起來。 在JSX 中還有另一種屬性,就是字符串,它能夠直接寫如name="sam",除此以外,全部的屬性都要用{} 括起來。好比咱們向組件中傳遞一個數字1,咱們就要寫 num = {1}, 傳遞一個布爾值,就要寫 bool={false},  咱們傳遞一個字符串,一個布爾值, 一個數字體驗一下。

// 組件添加了三個p, 有來接受數據
class Content extends React.Component {
  render() {
    return (
      <section>
        <ul>
          {
            this.props.item.map((item, index) => 
              <li key={index}>{item}</li>
            )
          }
        </ul>  
        <p>name 的值是{this.props.name},類型是 {typeof this.props.name}</p>
        <p>bool 的值是{this.props.bool},類型是 {typeof this.props.bool}</p>
        <p>num 的值是{this.props.num},類型是 {typeof this.props.num}</p>
      </section>
    )
  }
}
// 向組件中另外傳遞 字符串name ,布爾值bool, 一個數字num。
let content = <Content item ={item} name="sam" bool={false} num={1}></Content>

  這時刷新瀏覽器,能夠看到報錯了,首先,JSX語法,瀏覽器是不支持的,咱們要把它轉換成JS, 因此引入babel庫,在head 標籤中引入

    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

  其次咱們要告訴babel, 咱們的代碼須要轉譯,因此在寫react 代碼的script 標籤上添加一個type 屬性

<script type="text/babel">

  這時再刷新瀏覽器,沒有問題了,咱們也得到了屬性,而且它的類型也是對的,num 是Number, 字符串是String。

  可是這裏有一個問題,就是若是咱們要傳遞不少屬性,這麼 一個一個列出來,很是麻煩,怎麼辦呢? 這時可使用對象,但若是用對象進行傳值,又不符合 屬性名=屬性值的寫法,這時要用到es6中的擴展運算符..., React 對es6 中的擴展運算符(…)進行擴展,它能運用到對象上,對對象進行分割。{…obj}; var obj = {name:」sam」, num: 1}  , …obj  => name=」sam」 , num= 1, 注意,...obj 是一個表達式,仍須要把它用{} 括起來

<script type="text/babel">

  // 組件添加了三個p, 有來接受數據
  class Content extends React.Component {
    render() {
      return (
        <section>
          <ul>
            {
              this.props.item.map((item, index) => 
                <li key={index}>{item}</li>
              )
            }
          </ul>  
          <p>name 的值是{this.props.name},類型是 {typeof this.props.name}</p>
          <p>bool 的值是{this.props.bool},類型是 {typeof this.props.bool}</p>
          <p>num 的值是{this.props.num},類型是 {typeof this.props.num}</p>
        </section>
      )
    }
  }

  // 要把傳遞的屬性寫到一個對象中,
  const obj = {
    item : ['JS', 'React', 'Redux'],
    name: 'sam', 
    bool: false,
    num: 1
  }

  // 把對象進行分割
  let content = <Content {...obj}></Content>
  ReactDOM.render(content, document.getElementById('root'));
</script>

  在Content 組件中,你可能發現外層包了一個section, 這是由於全部的組件都返回一個單一的根節點,主要仍是createElemet 函數第一個屬性type 是一個值, 不能接受多個值。

你可能還發現全部的組件名是大寫,仍是由於createElement 的第一個參數type, type 有兩種類型,一種是html原有的類型如h1, 一種是自定義的類型,就是component, 當咱們傳入時,React  沒法區分這兩種類型,因此它用大小寫進行區分。若是是小寫,它就覺得是html原有的類型,若是是大寫,就是自定義類型。若是咱們組件使用了小寫,React 按html原有的類型進行渲染,可是它又找不到這個類型,因此什麼都不會渲染,組件名必須大寫。

   在JSX語法中, 咱們還要注意如下兩點:

  html 屬性關鍵字: 如 咱們能夠給html元素添加屬性<h1 class="book">, 但class 在js 中是關鍵字, 因此class 要變成 className. 因爲JSX 最終會轉換成原生js 函數,因此js中的一些關鍵字在JSX中是不能用的,如class, for. 但在JSX 的類html 模版中,html 元素屬性中又有class 和for, 這就衝突了。React 對html 元素中有衝突的屬性進行了從新命名,for 變成了htmlFor, class 變成了className.  全部變量的命名都要用 駝峯命名法。如label 元素 <label htmlFor=」input」 className=」text」></label>

  樣式:在JSX 中,給一個html 元素添加樣式有兩種方法,一種是上面提到的className,  它的取值是一個樣式名字符串,一種是內聯樣式style, 它的取值必須是一個對象。 <div className=」col-md-3」 style ={style}></div> ,style 是組件內部定義的一個對象變量。 由於render 是一個函數,裏面能夠聲明變量

  class Content extends React.Component {
    render() {
    // 定義樣式變量
  var inlineStyle = {
      color: 'green' ,
   // css3 一些屬性有些須要帶瀏覽器廠商前綴,這時廠商前綴首字母必須大寫, 全部的樣式都是字符串
   WebkitFilter: blur('5px')
  } 

    return (
      <section>
        <p style={inlineStyle}>name 的值是{this.props.name},類型是 {typeof this.props.name}</p>
      </section>
    )
    }
  }
相關文章
相關標籤/搜索