深刻理解React(一)JSX與虛擬DOM

初衷

使用 React 有一段時間了, 一直想找個時間寫一個 React 的系列文章。忙裏抽閒,完成了第一篇。寫這系列文章的初衷是總結這段時間的技術學習,以及給那些想學習 React 的同窗們一點幫助。我會盡可能以通俗易懂的語言闡述我對 React 的理解,但願能照顧到更多的新手。相信你們應該都明白一個道理,最可以帶領你進步的,並非比你強不少不少的大牛,而是恰好比你走得快那麼一步的腳印。javascript

爲何是React

這是2018年現代前端框架的使用狀況統計,圖片來自 JavaScript 如日中天,2018趨勢報告來啦!,數據僅供參考。能夠看到這是個前端框架三足鼎立的時代,選擇 React 並非由於其餘框架不夠好,而是 React 自己有其獨特的魅力,吸引着開發者的彙集。就比如喜歡一我的,並非由於其餘人很差才喜歡他,而是被他獨特的魅力所吸引才產生了感情。因此對新手來講,大可沒必要太過糾結於框架的選擇,框架只是個幫助咱們開發的工具而已。你只須要知道,React 自己足夠強大,可以讓你在大前端的世界裏走的更遠!

Hello World

咱們從最簡單的 Hello World 開始,進入 React 的世界,使用的是最新的 React 16版本。html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <div id="root"></div>
  
  // 引入react及react-dom庫
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  // 引入babel
  <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>

  // 在babel編譯的環境下執行
  <script type="text/babel">
    const vDom = <h1>Hello World</h1>
    const root = document.getElementById('root')
    ReactDOM.render(vDom, root)
  </script>
</body>

</html>
複製代碼

在使用 React 以前,咱們須要先引入 React 相關的 js 庫。前端

  • react.development.js 這是 React 的核心庫,用於構建頁面 UI
  • react-dom.development.js 這是 React 的 DOM 相關操做庫,能夠將你構建的 UI 渲染到瀏覽器中

除此以外,還需引入 babel 進行語法解析轉換。java

咱們來詳細解析下上面的 demo,其實 script 標籤中一共就寫了3句代碼,但其中包含了 React 的核心知識點——虛擬 DOM 和 JSX 。react

<script type="text/babel">
    const vDom = <h1>Hello World</h1>
    const root = document.getElementById('root')
    ReactDOM.render(vDom, root)
</script>
複製代碼

JSX

JSX 是 React 的靈魂,全稱 JavaScript XML。顧名思義,它可讓你在 JS 中使用 XML 標記的方式去直接聲明界面的 DOM,這是 React 獨有的語法糖。git

請看 HelloWorld 的例子github

const vDom = <h1>Hello World</h1> // 建立h1標籤,右邊千萬不能加引號
const root = document.getElementById('root') // 找到<div id="root"></div>節點
ReactDOM.render(vDom, root) // 把建立的h1標籤渲染到root節點上
複製代碼

這裏咱們在 JS 裏直接建立了一個<h1>Hello World</h1>標籤,並賦值給 vDom,這就是 JSX 語法。
若是咱們將 react 庫的引入註釋掉 算法

會發現瀏覽器中報錯
緣由就是原生JS語法並不支持直接聲明 DOM 標籤,這是 JSX 的語法,須要 React.js 庫的支持。

ReactDOM.render()這個是 react-dom 庫中的方法,用於將你建立好的 HTML 模板(虛擬 DOM 節點)插入到某個節點上,並渲染到頁面上。第一個參數是 HTML 模板,第二個參數是指定的 DOM 節點。瀏覽器

在上述 JSX 語法中有幾個值得注意的地方:bash

  • 右邊的 h1 標籤千萬不能加引號。若是加上引號的,JS 引擎會將其解釋爲字符串類型。其實從本質來講,<h1>Hello World</h1>這是一個對象,叫作虛擬 DOM 對象,後面會講到。
  • <script> 標籤的 type 屬性爲 text/babel。因爲使用了 JSX 這種特殊的語法,咱們不能再像往常同樣,使用<script type="text/javascript">來解析 JSX,而要藉助 babel 來進行解析、轉換成 JS。
  • 將 JSX 語法轉爲 JavaScript 語法,這一步很消耗時間。因此如今前端項目,都會使用工做流的形式來構建,不會在 html 頁面中直接引入 react、寫 js 代碼等等。

簡單來講,就是 JSX 賦予咱們在 JS 中直接建立 HTML 標籤的能力,由於 HTML 標籤實在是太弱小了。
固然咱們也能夠不使用 JSX 的語法建立 DOM 節點,直接使用 React 給咱們提供的普通 JS 寫法,React.createElement()API 來建立。

const vDom = React.createElement(
  'h1', // 第一個參數是標籤名,例如h一、span、table...
  { className: 'hClass', id: 'hId' }, // 第二個參數是個對象,裏面存着標籤的一些屬性,例如id、class等,由於class是保留字,因此要寫成className的形式
  'hello world' // 第三個參數是節點中的文本
)
const root = document.getElementById('root')
ReactDOM.render(vDom, root)
複製代碼

能夠看到和以前的 JSX 寫法const vDom = <h1>Hello World</h1>效果是同樣的

在使用 React.createElement()這種寫法時,咱們並不須要用 babel 進行解析,由於這自己就是 JS 的語法,JS 引擎能夠解析。
其實本質上,JSX 就是爲了簡化直接調用 React.createElement() API 的一顆語法糖而已,他執行最終會被 babel 解析轉換爲 React.createElement()的形式,因此推薦使用 JSX 的語法來建立頁面的 UI(也就是 HTML 的一些 DOM)。

JSX的優勢

  • 類 XML 語法結構清晰
  • 加強 JS 語義
  • 屏蔽 DOM 操做
  • ···

咱們之前在操做時,須要通過如下流程
建立節點 -> 找到插入位置 -> 插入節點,如果還有子節點,還須要繼續添加,一切都須要手動實現。
而使用 React 的 JSX 語法只須要const vDom = xxx 而後ReactDOM.render(vDom, root)就能夠。
換句話說,之前都是過程式操做,你不只知道要作什麼,還須要本身手動去實現。而如今變成了聲明式操做,就比如在下命令同樣,你只須要下命令建立怎樣的 DOM 節點,而後下命令插入,中間的過程所有都由 React 框架幫你自動實現,讓開發者能夠徹底屏蔽 DOM 的操做。

JSX基本語法規則

  • 遇到 HTML 標籤(以 < 開頭),就用 HTML 規則解析
  • 遇到代碼塊{}或括號(),就用 JavaScript 規則解析

咱們把 Hello World 的例子提高下

<script type="text/babel">
    let title = 'Hello World'
    const vDom = (
      <div>
        <h1>{title}</h1>
      </div>
    )
    const root = document.getElementById('root')
    ReactDOM.render(vDom, root)
</script>
複製代碼

上述代碼在執行時,當遇到<div>的左箭頭括號時,使用 HTML 的解析規則,當遇到{title}時,採用 JS 的規則解析,也就是獲取變量 title 的值。值得一提的是,若是須要建立嵌套的 HTML 結構,推薦使用()括號括起來。

虛擬DOM

  • 咱們在寫前端頁面時,手動操做 DOM,繁瑣又容易出錯,在大規模應用下維護起來也很困難。
  • 而且每次修改 DOM,瀏覽器的 DOM 樹都須要重繪重排,效率十分低下。
  • 既然 DOM 手動操做太繁瑣且效率低下,那就在每次狀態更新時從新渲染整個頁面
  • 每次都從新渲染整個頁面效率十分低下,因此就加上一個虛擬 DOM
  • 每次修改時,先在虛擬 DOM 上更新,最後在批量更新整個頁面,這樣頁面只須要一次大的更新,效率很高

虛擬 DOM 是在 DOM 的基礎上創建的一個抽象層,咱們對 DOM 中的數據和狀態所作的任何改動,都會被自動且高效的同步到虛擬 DOM,最後再批量同步到 DOM 中。React 會在內存中維護一個虛擬 DOM 樹,當咱們對這個樹進行讀或寫的時候,其實是對虛擬 DOM 進行的。當數據變化時,React 會自動更新虛擬 DOM 樹,而後拿新的虛擬 DOM 樹和舊的虛擬 DOM 樹進行對比(當中有 DOM diff算法,這個以後再說),把不一樣的虛擬節點放到一個隊列裏,最終在渲染時一次批量更新這些隊列中的虛擬節點到真實 DOM 中,這是對 DOM 渲染效率上的一個質的提高。

一點小結

React 以 JS 爲中心,以 JSX 的獨特語法糖將"HTML"放到了 JS 裏,而 JS 遠比 HTML 要強大。所以,與其加強 HTML 讓其擁有邏輯,不如加強 JS 讓其支持標籤化,這樣一來既豐富了 JS 操控領域,又提高了頁面渲染的性能。因此,你如果新手也不要緊,只要你 JS 能力足夠強,相信必定能在 React 的世界裏策馬奔騰!

最後推薦幾個 React 的社區
React 官方文檔:react.docschina.org/
React China:react-china.org/
React 開源中國社區:www.oschina.net/translate/t…

個人 github 地址:github.com/FightingHao 若是有什麼不懂的地方,歡迎評論,你們一塊兒探討 React 有關的知識!

相關文章
相關標籤/搜索