前言
在Jq,原生javascript時期,在寫頁面時,往往強調的是內容結構,層疊樣式,行爲動作要分離,三者之間分工明確,不要耦合在一起
然而在React出現後,一切皆js,對於在JS裏面寫HTML代碼,剛開始是非常反感的,甚至有違背當初的原則
但是,對於原先那種僅僅是把三種語言技術放在了三種不同文件位置裏進行分開管理了,實際上,它並沒有實現邏輯上的分離
既然前端UI顯示就是HTML,CSS,javascript,那麼把實現一個功能的所有代碼放在一個文件裏管理,也是一種宏觀上代碼的封裝,模塊化處理.
使用JSX並不是倒退,它只是一個語法糖而已,雖然在React中,不強制要求使用JSX,但是官方卻推薦使用.
因爲在javascript代碼中將JSX和UI放在一起在視覺上有輔助作用,另外,它還可以使React顯示跟過有用的錯誤和警告信息
下面就一起來學習下JSX吧,當你習慣它之後呢,並不會嗤之以鼻,反而會引以爲愛的!
正文從這裏開始~
JSX是什麼?
全稱: javascript and XML
定義: 可拓展(自定義)標記性語言,基於javascript,融入了XML,我們可以在js中書寫xml,使用JSX可以很好的描述UI在頁面中應該呈現它應有的交互形式
類似下面的這種,非官方提供的普通標籤,而是自己定義具有一定含義的特殊標籤
<person>
<name></name>
<age></age>
<height></height>
<weight></weight>
</person>
其實,你可把這些自定義的標籤,稱爲組件,頁面中的某一部分,具有獨立的功能和邏輯. 實現組件化的好處,不言而喻,下面來看看React的JSX是怎麼樣的
當你用create-react-app
腳手架,初始化一個react應用後,在入口文件index.js中最後一行代碼,ReactDOM.render()
函數調用的第一個實參數的寫法<App />
import React from 'react'; // 引入react.js庫,並用import關鍵字定義實例化了一個React對象
import ReactDOM from 'react-dom'; // 引入react-dom.js,同上,實例化了ReactDOM對象
ReactDOM.render(<App />, document.getElementById('root')) // 將App組件通過ReactDOM.render()函數,掛載到root根節點上,插入到頁面中去
爲了更好的理解,你可以在index.js中,將代碼更改成如下
const element = <App />;
const container = documnent.getElementById("root");
ReactDOM.render(element, container);
其中ReactDOM是react-dom的一個實例對象,它是用來將虛擬dom轉換爲真實DOM的,ReactDOM實例化對象下的一個render方法,接收兩個實際參數,第一個實參數,是要渲染的組件,第二個實參數,是該組件的掛載點,將該組件渲染到什麼位置上,上面是渲染到根節點root上
ReactDOM.render(要渲染的組件, 組件要掛載的位置);
其中類似這種JSX:
const element = <h1 title="h1標籤">Hello,itclanCoder</h1>
// 等價於
var element = React.createElement('h1',{title:"h1標籤"}, "hello, itclanCoder" );
// React.createElement() 會預先執行一些檢查,以幫助你創建了這麼一個對象,但是在實際中不要這麼寫,它是會報錯的
var element = {
type: 'h1',
props: {
title: "h1標籤",
children: "hello,itclanCoder"
}
}
這些對象被稱爲 “React 元素”。它們描述了你希望在屏幕上看到的內容。React通過讀取這些對象,然後使用它們來構建 DOM 以及保持隨時更新
注意:
- React中並沒有模板語言(類似Vue的template的),但是它具有JavaScript的全部的功能
- 可以在JS中書寫XML(HTML) 只能有且僅有一個頂層元素 ,當然也可以藉助React提供的
Fragment
(也叫佔位符)這個內置組件將你寫的JSX子元素給包裹起來,可以包含子節點 ,也支持插值表達式 {表達式} - 爲了便於閱讀,return返回的jsx內容,用一個圓括號()包裹起來,這樣可以將JSX拆分爲多行。這樣做不是強制要求的,但是這可以避免遇到自動插入分號陷阱
如下代碼所示:
import React from "react";
import ReactDOM from "react-dom";
class Acomponent extends React.Component {
render() {
// return 後面帶着一個圓括號,只是爲了換行顯示,根節點最頂層只能是一個元素
return (
<div>
<h1 title="我是川川">一個靠前排的90後帥小夥</h1>
<h2>歡迎關注微信itclanCoder公衆號</h2>
</div>
)
}
}
// 或者使用React提供的Fragement佔位符組件也可以,但是先引入
import React, { Componnet, Fragment } from "react";
import ReactDOM from "react-dom";
class Acomponent extends Component {
render() {
return (
<Fragment>
<h1 title="我是川川">一個靠前排的90後帥小夥</h1>
<h2>歡迎關注微信itclanCoder公衆號</h2>
</Fragment>
)
}
}
下面來具體來看看JSX是如何使用的
JSX的具體使用
- 在JSX中嵌入表達式{ 表達式 }
雙大括號內可以是變量,字符串,數組,函數調用, 但是不可以是對象,也不支持 if,for語句
例如:你在插值表達式裏寫對象:它是會報錯的
{ {name: "川川", age: "一個靠前排的90後帥小夥"} }
錯誤如下所示:
Objects are not valid as a React child (found: object with keys {name, age}). If you meant to render a collection of children, use an array instead
該錯誤的意思是:對象無效作爲React子對象(找到:具有鍵{name,age}的對象)。如果您要渲染子集合,請使用數組
當然如果是數組的話,它會自動的給拼接起來,本質上是通過數組中的join("")方法處理後的結果
{ ["川川", "全宇宙最帥"]} //川川全宇宙最帥
當然對於在JSX裏面寫if,for語句也是會報錯的
<li>
{
if(this.isBtn) { <Button />
}
</li>
其實,JSX 也是一個表達式,它雖然不支持if,for語句,但是它在if,for循環的代碼塊中是可以使用JSX的,將JSX賦值給變量,把JSX當作參數傳入,以及從函數中返回JSX
function getMessage(user) {
if (user) {
return <h1>Hello, { formatName(user) }!</h1>;
}
return <h1>Hello, itClanCoder.</h1>;
}
注意:布爾類型、Null 以及 Undefined 將會被忽略,false, null, undefined, true是合法的子元素。但它們並不會被渲染。以下的 JSX 表達式渲染結果都是相同的
<div />
<div></div>
<div>{ false }</div>
<div>{ null }</div>
<div>{ undefined }</div>
<div>{ true }</div>
具體作用: 這有助於在特定條件來渲染其他的 React 元素。例如,在以下 JSX 中,僅當 isBtn 爲 true 時,纔會渲染 <Button />
<div>
{ isBtn && <Button /> }
<Content />
</div>
有一點需要注意的是:有一些false值,例如:數字0,仍然會被React渲染,例如:如下所示
<div>
{
this.aBtns.length &&
<Button content="我是按鈕" />
}
</div>
要解決這個問題,確保 && 之前的表達式總是布爾值,就可以了
反之,如果你想渲染 false、true、null、undefined 等值,你需要先將它們轉換爲字符串:
轉換字符串有如下三種方法
- 對象.toString(),注意此方法,針對數據類型是null對象,及undefined,不適用
- 用空字符串拼接:variable+'';此方法比較普遍,但是可讀性有些差
- 用String(variable):用String字符串對象方法進行轉化,推薦使用
<div>
<p>對象.toString(){ myVariable.toString() }</p>
<p>用空字符串拼接{ myVariable + '' }</p>
<p>用String(variable){ String(myVariable) }</p>
</div>
當然,插值表達式中變量也可以用Es6中的反引號
hello, { `${String(false)}` } // false
介紹了那麼多,依然還是不夠的,下面來看看JSX的原理,理解了這個,你將會知道一個JSX究竟是怎麼工作,以及怎麼渲染到頁面上去的
JSX原理
頁面中的DOM元素結構都可以用javascript對象來描述,包括的信息有,標籤名,屬性,子元素,事件對象
在JS裏面,一切皆對象,對象的特點就是,含有屬性或者方法,,其實任何東西,都可以用對象去描述
例如:如下的JSX結構
<div class="input-wrap">
<input
type="text"
autocomplete="off"
value=""
id="mq"
class="input"
title="請輸入搜索文字" />
<button>搜索</button>
</div>
假如用對象來描述一下上面的信息
{
tagName: 'div',
attribute: { className: 'input-wrap'},
children: [
{
tagName: 'input',
attribute: {
type: "text",
autocomplete:"off",
value:"",
id:"mq",
class:"input",
title:"請輸入搜索文字"
}
},
{
tagName: "button",
attribute: null,
children: '搜索'
}
]
}
當直接把這個HTML段代碼寫在React中,它背後其實是通過React.createElement()方法進行創建的,創建類似這樣的
{
type: 'div',
props: {className: 'input-wrap' },
children: [
{
type: 'input',
props: {
type:'text',
autocomplete:"off",
value:"",id:"mq",
class:"input",
title:"請輸入搜索文字"
},
{
type: 'button',
props: null,
children: '搜索'
}
]
}
你可以聯想一下原生javascript的document.createElement()與JQ中的$("")創建一個js對象與jQ對象的,而在React中,React就是一個實例化對象,更深層次探討的話,React也是基於原型對象構建出來的
儘管React與前兩者不同,但是筆者仍然覺得有類似,異曲同工之妙,例如React下面的createElement方法,仍然與原生document文檔對象下面的創建元素方法相同
如果原生javascript紮實的話,便不難理解React在這做了一層轉化
既然js對象描述的UI(DOM)信息與HTML所展示的結構信息是一樣的,那爲什麼不用Js對象來代替呢,因爲用對象字面量寫的方式太繁瑣了,又臭又長的,結構又不清晰,如果用HTML來展示UI信息,那麼就簡單多了
React.js 就把js語法擴展了一下,讓 Js語言能夠支持這種直接在Js代碼裏面編寫類似 HTML 標籤結構的語法,這樣寫起來就方便很多了。編譯的過程會把類似 HTML 的 JSX 結構轉換成 JavaScript 的對象結構
上面的代碼:
import React from 'react'
import ReactDOM from 'react-dom'
class Search extends React.Component {
render () {
return (
<div class="input-wrap">
<input
type="text"
autocomplete="off"
value=""
id="mq"
class="input"
title="請輸入搜索文字" />
<button>搜索</button>
</div>
)
}
}
ReactDOM.render(
<Search />,
document.getElementById('root')
)
經過babel編譯,Babel 會把 JSX 轉譯成一個名爲 React.createElement()
函數調用
如下所示
import React from 'react'
import ReactDOM from 'react-dom'
class Search extends React.Component {
render () {
return (
React.createElement(
"div",
{className: 'input-wrap'},
React.createElement(
"input",
{ type:'text',
autocomplete:"off",
value:"",
id:"mq",
class:"input",
title:"請輸入搜索文字"
}
),
React.createElement(
'button',
null,
"搜索"
)
)
)
}
}
ReactDOM.render(
React.createElement(Search, null),
document.getElementById('root')
);
換言之,如果你自定義的一個組件:例如:<ElButton bgColor="green">綠色按鈕</ElButton>
import React from 'react';
import ReactDOM from 'react-dom'
class ElButton extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<button
style={ {color: this.props.bgColor} }>{ this.props.children }</button>
)
}
}
ReactDOM.render(<ElButton bgColor="green">綠色按鈕</ElButton>, document.getElementById('roo
經過Babel的轉化後
React.createElement(
ElButton,
{ bgColor: green},
children: "綠色按鈕"
)
最終渲染到瀏覽器上,結果如下所示
在編譯之後,JSX 表達式會被轉爲普通 JavaScript 函數調用,並且對其取值後得到 JavaScript 對象
React.createELmenet會構建一個js對象來描述你的HTML結構信息,包括標籤名,屬性,子元素以及事件對象等
使用React一定要引入React庫,引入這個是爲了解析識別JSX語法糖(React.createElement()函數的替代)
當然另一方面也是爲了創建虛擬DOM(所謂虛擬DOM,它就是一個JS對象,是用它來描述真實的DOM,上面的例子,已經很明白了)
而引入react-dom的原因就是,爲了將虛擬DOM轉換爲真實DOM,然後把這個DOM元素插入到頁面中,這正是ReactDOM.render()
做的事情,把組件渲染並且構造 DOM 樹,然後插入到頁面上某個特定的元素上
所以在你編寫一個組件的時候,一開始就要引入react.js和react-dom這兩個文件的
當使用JSX到最終展現頁面結構到瀏覽器上:經歷瞭如下過程:如果你在代碼中進行斷言一下,就非常清晰這一過程了
所以歸納一下:JSX其實就是javascript對象,是用來描述UI結構信息的,JSX語法並不是真實的DOM, 使用JSX是爲了方便開發人員寫代碼更簡單,簡潔
當然實際開發中,我們並不會去用React.createElement()去創建元素,不是說它不能完成需求,只是因爲它寫起來比較雞肋,代碼維護起來非常麻煩,可讀性差
相比於寫JS,寫HTML還是比較容易吧,但也是不簡單的哦,因爲寫JS比較燒腦,容易掉頭髮呀,HTML本身是不具備邏輯可言的
然而,到手的人民幣的高低,更多的是取決於你的JS水平...,這個的確是滴,JS水平高,真的是可以直接喊高價的
小結
JSX 是 JavaScript 語言的一種語法擴展,長得像 HTML,但並不是 HTML,附加了原生HTML標籤不具備的能力,例如:自定義屬性,以及後續的組件傳值
UI界面顯示什麼樣,取決於JSX對象結構,換句話說,取決於render()函數裏面的return關鍵字後面返回的JSX結構
引入React.js庫是爲了解析識別JSX語法,同時創建虛擬DOM,而引入react-dom是爲了渲染組件,將組件掛載到特定的位置上,同時將虛擬DOM轉換爲真實DOM,插入到頁面中
總結
本文主要講述了JSX是什麼?以及JSX的一些注意事項,JSX的具體使用,嵌入表達式,最重要的是JSX的原理,在使用JSX中,react是如何將jsx語法糖裝換爲真實DOM,並渲染到頁面中的,當然,JSX仍然還有一些注意事項,邊邊角角的知識的,限於篇幅,貪多嚼不爛,我們下次再繼續了