JSX核心语法

认识JSX

JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法

它用于描述我们的UI界面,JSX会被解析为ReactRenderObject,所以JSX本质就是JS对象, 所以JSX可以和JavaScript融合在一起使用

JSX其实是将HTML嵌入到JavaScript中的一种结构语法

// JSX本质就是对象,所以其可以作为变量来进行使用
const msg = <h2>Hello World</h2>
 
ReactDOM.render(msg, document.getElementById('app'))

React为什么使用JSX

React认为渲染逻辑本质上与其他UI逻辑存在内在耦合

  • 比如UI需要绑定事件(button、a元素等等)
  • 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI

他们之间是密不可分,所以React没有讲标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件 (Component)

书写规范

  1. JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div元素或Fragment组件
  2. 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),但这仅仅是为了方便我们阅读和JSX代码换行,不是必须的
  3. JSX中的标签可以是单标签,也可以是双标签,但如果是单标签,必须使用/来进行闭合,也就是要使用严格模式来闭合标签

注释

// JSX的{}中可以写JS语法,其中也就可以写JS注释
{/* 这里是JSX的块级注释内容 */}

{
  // 这里是JS的单行注释内容
  // 注意: //和{之间必须换行,否则无法正常解析}
}

嵌入值

JSX使用大括号语法来嵌入值,在大括号中可以书写的是任意合法的JS变量或合法的JS表达式

变量

  • 当变量是Number、String、Array类型时,可以直接显示
  • 当变量是null、undefined、Boolean类型时,内容为空
    • 如果希望可以显示null、undefined、Boolean,那么需要转成字符串
    • 例如使用toString,String,拼接空字符串
    • 因为undefined和null是无法调用函数的,所以推荐使用拼接空字符串的方式来进行转换
    • 对于Boolean类型的值,无论值是true还是false,其都不会在界面上进行显示
  • 对象类型不能作为子元素(not valid as a React child)
    • JSX会转换为React.createElement,而其第三个参数children即使当前元素所对应的子元素
    • 而children数组中是不可以有对象的
    • 也就是说对象不可以写在大括号语法中

表达式

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

   render() {
     // 对象的解构,可以使用常量来进行接收
     // 因为每一次界面刷新的时候,其都会重新执行一遍render函数
     // 也就是会生成一个新的函数作用域,因此解构出的变量可以使用常量来进行接收
     const { firstName, lastName, isLogin } = this.state

     return (
       // 虽然JSX可以使用Fragment进行包裹(也就是使用<></>来进行包裹)
       // 但是Fragment只能在脚手架中使用,在CDN引入的时候无法使用
       <div>
         {/* JSX中可以书写合法的JS表达式 */}
         <div>{ firstName + ' ' + lastName }</div>

         {/*
            JSX中可以编写三目运算符
            在实际开发中,LoginOut和LoginIn可能对应着是变量
             为了避免变量为null或undefined的时候,在界面显示出现bug
             随意null和undefined在JSX中一般不会显示在界面上
          */}
          <div>{ isLogin ? 'LoginOut' : 'LoginIn' }</div>

          {/*
             逻辑运算符
               JSX中,会出现许多通过逻辑运算符来决定渲染内容的情况
               所以JSX中如果需要显示的是boolean类型的时候,无论结果为true还是false
               其值都不会显示,以避免出现显示bug
               例如: isLogin && 'welcome'中 如果isLogin的值为false
                    那么表达式的结果为false,此时在界面上显示false明显是不合理的
           */}
           <div>{ isLogin && 'welcome' }</div>

           {/*
             函数调用
               JSX中可以进行函数的调用
               也可以使用一些方法,例如map, filter, some等
           */}
           <div>{ this.getFullName() }</div>
         </div>
       )
     }

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

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

绑定属性

普通属性

{/* 可以使用大括号语法来绑定普通属性 */}
<a href={ this.state.link }>google</a>

class

{/*
  class是js的关键字,而JSX本质就是all in js
  所以为了避免HTML书写和JS关键字冲突
  JSX为对应的HTML属性起了别名
     class -> className
     for -> htmlFor
 */}
<h2 className={`titile ${this.state.isActive && 'active'}`}>Lorem</h2>

style

{/*
   第一个大括号表示内部使用JS语法
   第二个大括号表示里面的值是一个对象

   style中的属性值 如果是字符串需要加上引号
   因为是在对象中,如果不加引号,会被解析为对象
*/}
<h2 style={{
  color: '#fff',
  backgroundColor: 'skyblue',
  'font-size': '20px'
}}>Lorem</h2>

事件绑定

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

  // render方法在编译后,是通过App组件的实例对象来进行调用的
  // 所以在render函数中,this的指向是正确的
  render() {
    return (
      <div>
        <button onClick={ this.getCount }>click me</button>
      </div>
    )
  }

  getCount() {
    // 在React中,对HTML生事件进行了二次封装
    // 而HTML原生事件是所有组件都可以调用的
    // 所以React在封装HTML原生事件的时候,并不知道具体的调用者
    // 所以React在执行传入的原生事件回调的时候,使用了`call(undefined)`
    // 因此在React中 默认情况下原生HTML事件回调中的this是undefined
    // 我们在使用的时候,需要对this的指向进行修正
    console.log(this)  // => undefined
  }
}

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

{/*
   使用bind进行绑定的时候,可以返回一个拥有正确this指向的新函数
   但是如果在jsx中多次调用同样函数的时候,需要多次重复使用bind方法来修正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
    }

    // 我们可以在构造函数中 一次性对某个方法进行this的修正
    // 但是这就意味着所有的函数都需要在构造器中使用bind方法修正this指向
    // 而实际开发中,组件内部的函数是很多的,如果都在构造器中修正this指向
    // 必然会导致构造函数过于繁琐,不利于维护
    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>
        {/* 绑定事件的时候,传入的其实是对应事件的引用地址 */}
        <button onClick={ this.getCount }>click me</button>
      </div>
    )
  }

  // 在定义函数的时候,使用class fields定义类的成员变量
  // 并且在定义函数的时候,使用箭头函数
  // 而类中的方法,本质上都是通过类的实例去进行调用的
  // 所以类的成员变量的上层作用域是App类
  // 因此在这里, 我们可以获取正确的this指向
  getCount = () => {
    console.log(this.state.count)
  }
}

解决方法三 -- 使用箭头函数 --- 推荐

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

  render() {
    return (
      <div>
        {/*
              在调用函数的时候在外层包裹一层箭头函数
              1. 如果在事件调用的时候传入箭头函数,其上层作用域是render函数,所以拥有正确的this指向
              2. 在箭头函数的执行体中,是去调用我们实际需要执行的函数,此时不是引用地址,而是具体的函数调用
                 所以使用这种方法进行事件的绑定 便于我们进行函数参数的传递
            */}
        <button onClick={ () => { this.getCount() }}>click me</button>
      </div>
    )
  }

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

参数传递

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

  render() {
    return (
      <div>
        <button onClick={ this.handleClick }>click me</button>
        {/*
           使用箭头函数修正this后,默认的事件对象是传递给外层的箭头函数的
           所以在箭头函数体中进行函数实际调用的时候,
           事件对象作为第几个参数传入还是不进行传入就可以由我们自己进行主动控制
         */}
        <button onClick={ e => { this.calcCount(2, e) }}>click me</button>
      </div>
    )
  }

  // 默认情况下,JSX在进行事件绑定的时候,会默认将事件参数传入
  // ps: 这个参数并不是原生事件对象,而是React基于原生事件对象二次封装得到的合成对象
  // 原生的事件对象位于 e.nativeEvent
  handleClick(e) {
    console.log(e)
  }

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

条件渲染

某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容

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

  // 和渲染UI相关的函数,写在render函数的上边
  renderText() {
    // 使用if进行条件判断,适合于逻辑比较复杂的情况
    if (this.state.isLogin) {
      return '欢迎回来'
    } else {
      return '请先登录'
    }
  }

  render() {
    return (
      <div>
        <h1>{ this.renderText() }</h1>
        {/* 使用三目运算符来进行条件分支的判断 */}
        <button onClick={ () => this.changeLoginStatus() }>{ this.state.isLogin ? '退出' : '登录' }</button>
        <hr />
        {/* 通过逻辑运算符来实现条件分支的判断 */}
        { this.state.isLogin && <p>您已经成功登录</p> }
      </div>
    )
  }

  // 和渲染UI无关,但是和业务逻辑有关的函数,写在render函数的后边
  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中,我们可以通过map,filter,slice等一系列的JavaScript函数来帮助我们实现列表渲染

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

  render() {
    return (
      // 以列表形式展示出nums中所有的偶数数据
      <ul>
        {
          this.state.nums.filter(num => num % 2 === 0).map(num => <li>{num}</li>)
        }
      </ul>
    )
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容