JSX核心語(yǔ)法

認(rèn)識(shí)JSX

JSX是一種JavaScript的語(yǔ)法擴(kuò)展(eXtension)数苫,也在很多地方稱(chēng)之為JavaScript XML,因?yàn)榭雌鹁褪且欢蝀ML語(yǔ)法

它用于描述我們的UI界面缘挽,JSX會(huì)被解析為ReactRenderObject,所以JSX本質(zhì)就是JS對(duì)象, 所以JSX可以和JavaScript融合在一起使用

JSX其實(shí)是將HTML嵌入到JavaScript中的一種結(jié)構(gòu)語(yǔ)法

// JSX本質(zhì)就是對(duì)象,所以其可以作為變量來(lái)進(jìn)行使用
const msg = <h2>Hello World</h2>
 
ReactDOM.render(msg, document.getElementById('app'))

React為什么使用JSX

React認(rèn)為渲染邏輯本質(zhì)上與其他UI邏輯存在內(nèi)在耦合

  • 比如UI需要綁定事件(button捂刺、a元素等等)
  • 比如UI中需要展示數(shù)據(jù)狀態(tài),在某些狀態(tài)發(fā)生改變時(shí)募寨,又需要改變UI

他們之間是密不可分族展,所以React沒(méi)有講標(biāo)記分離到不同的文件中,而是將它們組合到了一起拔鹰,這個(gè)地方就是組件 (Component)

書(shū)寫(xiě)規(guī)范

  1. JSX的頂層只能有一個(gè)根元素仪缸,所以我們很多時(shí)候會(huì)在外層包裹一個(gè)div元素或Fragment組件
  2. 為了方便閱讀,我們通常在jsx的外層包裹一個(gè)小括號(hào)()列肢,但這僅僅是為了方便我們閱讀和JSX代碼換行恰画,不是必須的
  3. JSX中的標(biāo)簽可以是單標(biāo)簽宾茂,也可以是雙標(biāo)簽,但如果是單標(biāo)簽拴还,必須使用/來(lái)進(jìn)行閉合跨晴,也就是要使用嚴(yán)格模式來(lái)閉合標(biāo)簽

注釋

// JSX的{}中可以寫(xiě)JS語(yǔ)法,其中也就可以寫(xiě)JS注釋
{/* 這里是JSX的塊級(jí)注釋內(nèi)容 */}

{
  // 這里是JS的單行注釋內(nèi)容
  // 注意: //和{之間必須換行片林,否則無(wú)法正常解析}
}

嵌入值

JSX使用大括號(hào)語(yǔ)法來(lái)嵌入值端盆,在大括號(hào)中可以書(shū)寫(xiě)的是任意合法的JS變量或合法的JS表達(dá)式

變量

  • 當(dāng)變量是Number、String费封、Array類(lèi)型時(shí)焕妙,可以直接顯示
  • 當(dāng)變量是null、undefined弓摘、Boolean類(lèi)型時(shí)焚鹊,內(nèi)容為空
    • 如果希望可以顯示null、undefined韧献、Boolean末患,那么需要轉(zhuǎn)成字符串
    • 例如使用toString,String势决,拼接空字符串
    • 因?yàn)閡ndefined和null是無(wú)法調(diào)用函數(shù)的阻塑,所以推薦使用拼接空字符串的方式來(lái)進(jìn)行轉(zhuǎn)換
    • 對(duì)于Boolean類(lèi)型的值,無(wú)論值是true還是false果复,其都不會(huì)在界面上進(jìn)行顯示
  • 對(duì)象類(lèi)型不能作為子元素(not valid as a React child)
    • JSX會(huì)轉(zhuǎn)換為React.createElement陈莽,而其第三個(gè)參數(shù)children即使當(dāng)前元素所對(duì)應(yīng)的子元素
    • 而children數(shù)組中是不可以有對(duì)象的
    • 也就是說(shuō)對(duì)象不可以寫(xiě)在大括號(hào)語(yǔ)法中

表達(dá)式

class App extends React.Component {
  constructor() {
    this.state = {
      firstName: 'klaus',
      lastName: 'Wang',
      isLogin: false
     }
   }

   render() {
     // 對(duì)象的解構(gòu),可以使用常量來(lái)進(jìn)行接收
     // 因?yàn)槊恳淮谓缑嫠⑿碌臅r(shí)候虽抄,其都會(huì)重新執(zhí)行一遍render函數(shù)
     // 也就是會(huì)生成一個(gè)新的函數(shù)作用域走搁,因此解構(gòu)出的變量可以使用常量來(lái)進(jìn)行接收
     const { firstName, lastName, isLogin } = this.state

     return (
       // 雖然JSX可以使用Fragment進(jìn)行包裹(也就是使用<></>來(lái)進(jìn)行包裹)
       // 但是Fragment只能在腳手架中使用,在CDN引入的時(shí)候無(wú)法使用
       <div>
         {/* JSX中可以書(shū)寫(xiě)合法的JS表達(dá)式 */}
         <div>{ firstName + ' ' + lastName }</div>

         {/*
            JSX中可以編寫(xiě)三目運(yùn)算符
            在實(shí)際開(kāi)發(fā)中迈窟,LoginOut和LoginIn可能對(duì)應(yīng)著是變量
             為了避免變量為null或undefined的時(shí)候私植,在界面顯示出現(xiàn)bug
             隨意null和undefined在JSX中一般不會(huì)顯示在界面上
          */}
          <div>{ isLogin ? 'LoginOut' : 'LoginIn' }</div>

          {/*
             邏輯運(yùn)算符
               JSX中,會(huì)出現(xiàn)許多通過(guò)邏輯運(yùn)算符來(lái)決定渲染內(nèi)容的情況
               所以JSX中如果需要顯示的是boolean類(lèi)型的時(shí)候车酣,無(wú)論結(jié)果為true還是false
               其值都不會(huì)顯示曲稼,以避免出現(xiàn)顯示bug
               例如: isLogin && 'welcome'中 如果isLogin的值為false
                    那么表達(dá)式的結(jié)果為false,此時(shí)在界面上顯示false明顯是不合理的
           */}
           <div>{ isLogin && 'welcome' }</div>

           {/*
             函數(shù)調(diào)用
               JSX中可以進(jìn)行函數(shù)的調(diào)用
               也可以使用一些方法湖员,例如map, filter, some等
           */}
           <div>{ this.getFullName() }</div>
         </div>
       )
     }

      getFullName() {
        return this.state.firstName + ' ' + this.state.lastName
      }
    }

ReactDOM.render(<App />, document.getElementById('app'))

綁定屬性

普通屬性

{/* 可以使用大括號(hào)語(yǔ)法來(lái)綁定普通屬性 */}
<a href={ this.state.link }>google</a>

class

{/*
  class是js的關(guān)鍵字贫悄,而JSX本質(zhì)就是all in js
  所以為了避免HTML書(shū)寫(xiě)和JS關(guān)鍵字沖突
  JSX為對(duì)應(yīng)的HTML屬性起了別名
     class -> className
     for -> htmlFor
 */}
<h2 className={`titile ${this.state.isActive && 'active'}`}>Lorem</h2>

style

{/*
   第一個(gè)大括號(hào)表示內(nèi)部使用JS語(yǔ)法
   第二個(gè)大括號(hào)表示里面的值是一個(gè)對(duì)象

   style中的屬性值 如果是字符串需要加上引號(hào)
   因?yàn)槭窃趯?duì)象中,如果不加引號(hào)娘摔,會(huì)被解析為對(duì)象
*/}
<h2 style={{
  color: '#fff',
  backgroundColor: 'skyblue',
  'font-size': '20px'
}}>Lorem</h2>

事件綁定

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 100
    }
  }

  // render方法在編譯后窄坦,是通過(guò)App組件的實(shí)例對(duì)象來(lái)進(jìn)行調(diào)用的
  // 所以在render函數(shù)中,this的指向是正確的
  render() {
    return (
      <div>
        <button onClick={ this.getCount }>click me</button>
      </div>
    )
  }

  getCount() {
    // 在React中,對(duì)HTML生事件進(jìn)行了二次封裝
    // 而HTML原生事件是所有組件都可以調(diào)用的
    // 所以React在封裝HTML原生事件的時(shí)候鸭津,并不知道具體的調(diào)用者
    // 所以React在執(zhí)行傳入的原生事件回調(diào)的時(shí)候彤侍,使用了`call(undefined)`
    // 因此在React中 默認(rèn)情況下原生HTML事件回調(diào)中的this是undefined
    // 我們?cè)谑褂玫臅r(shí)候,需要對(duì)this的指向進(jìn)行修正
    console.log(this)  // => undefined
  }
}

解決方法1 -- 使用bind方法

{/*
   使用bind進(jìn)行綁定的時(shí)候逆趋,可以返回一個(gè)擁有正確this指向的新函數(shù)
   但是如果在jsx中多次調(diào)用同樣函數(shù)的時(shí)候盏阶,需要多次重復(fù)使用bind方法來(lái)修正this指向
*/}
<button onClick={ this.getCount.bind(this) }>click me</button>
<button onClick={ this.getCount.bind(this) }>click me</button>
class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 100
    }

    // 我們可以在構(gòu)造函數(shù)中 一次性對(duì)某個(gè)方法進(jìn)行this的修正
    // 但是這就意味著所有的函數(shù)都需要在構(gòu)造器中使用bind方法修正this指向
    // 而實(shí)際開(kāi)發(fā)中,組件內(nèi)部的函數(shù)是很多的闻书,如果都在構(gòu)造器中修正this指向
    // 必然會(huì)導(dǎo)致構(gòu)造函數(shù)過(guò)于繁瑣般哼,不利于維護(hù)
    this.getCount = this.getCount.bind(this)
  }

  render() {
    return (
      <div>
        <button onClick={ this.getCount }>click me</button>
        <button onClick={ this.getCount }>click me</button>
      </div>
    )
  }

  getCount() {
    console.log(this.state.count)
  }
}

解決方法二 -- 使用class field

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 100
    }
  }

  render() {
    return (
      <div>
        {/* 綁定事件的時(shí)候,傳入的其實(shí)是對(duì)應(yīng)事件的引用地址 */}
        <button onClick={ this.getCount }>click me</button>
      </div>
    )
  }

  // 在定義函數(shù)的時(shí)候惠窄,使用class fields定義類(lèi)的成員變量
  // 并且在定義函數(shù)的時(shí)候,使用箭頭函數(shù)
  // 而類(lèi)中的方法漾橙,本質(zhì)上都是通過(guò)類(lèi)的實(shí)例去進(jìn)行調(diào)用的
  // 所以類(lèi)的成員變量的上層作用域是App類(lèi)
  // 因此在這里杆融, 我們可以獲取正確的this指向
  getCount = () => {
    console.log(this.state.count)
  }
}

解決方法三 -- 使用箭頭函數(shù) --- 推薦

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 100
    }
  }

  render() {
    return (
      <div>
        {/*
              在調(diào)用函數(shù)的時(shí)候在外層包裹一層箭頭函數(shù)
              1. 如果在事件調(diào)用的時(shí)候傳入箭頭函數(shù),其上層作用域是render函數(shù)霜运,所以擁有正確的this指向
              2. 在箭頭函數(shù)的執(zhí)行體中脾歇,是去調(diào)用我們實(shí)際需要執(zhí)行的函數(shù),此時(shí)不是引用地址淘捡,而是具體的函數(shù)調(diào)用
                 所以使用這種方法進(jìn)行事件的綁定 便于我們進(jìn)行函數(shù)參數(shù)的傳遞
            */}
        <button onClick={ () => { this.getCount() }}>click me</button>
      </div>
    )
  }

  getCount() {
    console.log(this.state.count)
  }
}

參數(shù)傳遞

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 100
    }
  }

  render() {
    return (
      <div>
        <button onClick={ this.handleClick }>click me</button>
        {/*
           使用箭頭函數(shù)修正this后藕各,默認(rèn)的事件對(duì)象是傳遞給外層的箭頭函數(shù)的
           所以在箭頭函數(shù)體中進(jìn)行函數(shù)實(shí)際調(diào)用的時(shí)候,
           事件對(duì)象作為第幾個(gè)參數(shù)傳入還是不進(jìn)行傳入就可以由我們自己進(jìn)行主動(dòng)控制
         */}
        <button onClick={ e => { this.calcCount(2, e) }}>click me</button>
      </div>
    )
  }

  // 默認(rèn)情況下蝴光,JSX在進(jìn)行事件綁定的時(shí)候梗脾,會(huì)默認(rèn)將事件參數(shù)傳入
  // ps: 這個(gè)參數(shù)并不是原生事件對(duì)象,而是React基于原生事件對(duì)象二次封裝得到的合成對(duì)象
  // 原生的事件對(duì)象位于 e.nativeEvent
  handleClick(e) {
    console.log(e)
  }

  calcCount(step, e) {
    console.log(this.state.count * step)
    console.log(e)
  }
}

條件渲染

某些情況下淀衣,界面的內(nèi)容會(huì)根據(jù)不同的情況顯示不同的內(nèi)容膘魄,或者決定是否渲染某部分內(nèi)容

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      isLogin: true
    }
  }

  // 和渲染UI相關(guān)的函數(shù)乌逐,寫(xiě)在render函數(shù)的上邊
  renderText() {
    // 使用if進(jìn)行條件判斷,適合于邏輯比較復(fù)雜的情況
    if (this.state.isLogin) {
      return '歡迎回來(lái)'
    } else {
      return '請(qǐng)先登錄'
    }
  }

  render() {
    return (
      <div>
        <h1>{ this.renderText() }</h1>
        {/* 使用三目運(yùn)算符來(lái)進(jìn)行條件分支的判斷 */}
        <button onClick={ () => this.changeLoginStatus() }>{ this.state.isLogin ? '退出' : '登錄' }</button>
        <hr />
        {/* 通過(guò)邏輯運(yùn)算符來(lái)實(shí)現(xiàn)條件分支的判斷 */}
        { this.state.isLogin && <p>您已經(jīng)成功登錄</p> }
      </div>
    )
  }

  // 和渲染UI無(wú)關(guān)创葡,但是和業(yè)務(wù)邏輯有關(guān)的函數(shù)浙踢,寫(xiě)在render函數(shù)的后邊
  changeLoginStatus() {
    this.setState({
      isLogin: !this.state.isLogin
    })
  }
}

模擬v-show

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      isLogin: true
    }
  }

  render() {
    return (
      <div>
        <button onClick={ () => this.changeLoginStatus() }>change login status</button>
        <h2 style={{ display: this.state.isLogin ? 'block' : 'none' }} >登錄成功</h2>
      </div>
    )
  }

  changeLoginStatus() {
    this.setState({
      isLogin: !this.state.isLogin
    })
  }
}

列表渲染

在React中,我們可以通過(guò)map灿渴,filter洛波,slice等一系列的JavaScript函數(shù)來(lái)幫助我們實(shí)現(xiàn)列表渲染

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      nums: [10, 23, 35, 122, 78, 4331, 23, 62]
    }
  }

  render() {
    return (
      // 以列表形式展示出nums中所有的偶數(shù)數(shù)據(jù)
      <ul>
        {
          this.state.nums.filter(num => num % 2 === 0).map(num => <li>{num}</li>)
        }
      </ul>
    )
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市骚露,隨后出現(xiàn)的幾起案子蹬挤,更是在濱河造成了極大的恐慌,老刑警劉巖荸百,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闻伶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡够话,警方通過(guò)查閱死者的電腦和手機(jī)蓝翰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)光绕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人畜份,你說(shuō)我怎么就攤上這事诞帐。” “怎么了爆雹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵停蕉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我钙态,道長(zhǎng)慧起,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任册倒,我火速辦了婚禮蚓挤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘驻子。我一直安慰自己灿意,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布崇呵。 她就那樣靜靜地躺著缤剧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪域慷。 梳的紋絲不亂的頭發(fā)上荒辕,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音芒粹,去河邊找鬼兄纺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛化漆,可吹牛的內(nèi)容都是我干的估脆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼座云,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疙赠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起朦拖,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤圃阳,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后璧帝,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捍岳,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锣夹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片页徐。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖银萍,靈堂內(nèi)的尸體忽然破棺而出变勇,到底是詐尸還是另有隱情,我是刑警寧澤贴唇,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布搀绣,位于F島的核電站,受9級(jí)特大地震影響戳气,放射性物質(zhì)發(fā)生泄漏链患。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一瓶您、第九天 我趴在偏房一處隱蔽的房頂上張望锣险。 院中可真熱鬧,春花似錦览闰、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至锻拘,卻和暖如春油吭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背署拟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工婉宰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人推穷。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓心包,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親馒铃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蟹腾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容