React Element /組件/JSX


  初學react,想要了解React 是什么,直接用script標簽 引入React就可以了,不過需要引入兩個庫:React 和ReactDom,React 負責創建React element,ReactDom 則是負責把React創建出來的element, 通過調用DOM API, 創建出真實的DOM 元素,這樣瀏覽器就可能根據DOM渲染出頁面了。模板如下

<!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>

  React Element

  一個普通的JavaScript 對象,用來描述真實的DOM 長什么樣子,可以通過React 庫提供的createElement() 函數來創建。createElement 函數接受三個參數,第一個是type, 要創建什么類型的element,  第二個是properties, 這個element 有哪些屬性,第三個參數是children, 這個element有哪些子元素。

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

 這時打開瀏覽器, console.log(h1Elem)  一下, 看一看這個剛剛創建的React element 是不是一個普通的對象。

  它是一個Js 對象,擁有props,type等等屬性, 但是通過它,也確實描述了 一個DOM元素 長什么樣子,<h1 id='recipe', data-type='title'>Hello Word</h1>,所以它也稱之為virtual DOM, 用於指導構造真正的DOM 元素.

  當然,createElement 不僅能創建一個element, 還可以創建一個層層嵌套的element樹, 這主要在於函數可以接受任意個參數,第三個參數及其以后的參數,都會當成element 的children

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

   此時也可以console.log(ulElement), 也可以看到它是一個嵌套的js 對象。

      {
          type: 'ul',
          props: {
              children: [
                  {
                      type: 'li',
                      props: {children: 'JS'}
                  },
                  {
                      type: 'li',
                      props: {children: 'React'}
                  },
                  {
                      type: 'li',
                      props: {children: 'Redux'}
                  }
              ]
          }
      }

  通過ulElem,我們也可以很清楚地知道頁面上將要展示什么,就是下面的ul-li,

  創建完React element, 就需要用到ReactDom 了。因為React element只是js 對象,只是描述了真實的DOM長什么樣子,最后還是要創建真實的DOM,否則瀏覽器無法渲染。ReactDom提供了一個render 方法, 可以把React element(虛擬DOM)轉化成真實的DOM

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

  第一個參數是React element, 第二個參數是渲染到什么地方, ReactDom render 相當於執行下面的操作

let h1 = document.createElement('h1');
h1.setAttribute('id', 'recipe');
h1.setAttribute('data-type', 'title');
h1.textContent = 'Hello World';
document.getElementById('root').appendChild(h1);

  這時我們刷新瀏覽器,可以h1. 如果React element 像ul 這樣包含children的話,ReactDom render 將執行遞歸操作,依次創建DOM。

  React Component

  當我們寫大量的React element,你會發現有些代碼可以共用,就像上面創建的ulElem,可以用到很多地方,這時我們就想把它們封裝起來,這就形成了React component組件。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 組件數據

  以上創建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’ >Hello World</h1>, 如果我能在代碼中直接寫h1 就好了。這就是JSX 語法, 可以直接在js 代碼中寫類html 的語法。React 把createElement 函數作了進一步的封裝,提供了JSX語法。

  在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 會把里面的內容進行求值計算。只要寫表達式,我們都要用{} 括起來。向組件中傳遞一個數字1,我們就要寫 num = {1}, 傳遞一個布爾值,就要寫 bool={false}。字符串除外,它可以直接寫。比如可以直接寫如name="sam"。

// 組件添加了三個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>
    )
    }
  }

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM