react基础(二)—— jsx使用

149 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

前言

大家好呀,我是L同学。在上篇文章中,我们初次体验了react的使用。在接下来的文章中,我将会对react中的知识点进行详细介绍。

在这篇文章中,我们将学习什么是jsx以及jsx的使用。

jsx介绍

jsx是一种javascript的扩展语法,也在很多地方称它为JavaScript XML,因为看起来就是一段XML语法。它用来描述我们的UI界面,并且它可以和javascript融合在一起使用。

jsx是嵌入到javascript中的一种结构语法。

<body>
    
  <div id="app"></div>

  <script src="../react/react.development.js"></script>
  <script src="../react/react-dom.development.js"></script>
  <script src="../react/babel.min.js"></script>

  <script type="text/babel">
    // jsx语法
    const element = <h2>Hello World</h2>;
    ReactDOM.render(element, document.getElementById("app"));

  </script>

</body>

需要注意的是,必须有type="text/babel",如果去掉会报语法错误。

image.png 另外,jsx中还有一些书写规范需要注意:

  • jsx的顶层只能有一个根元素
  • jsx中的标签可以是单标签,也可以是双标签。如果是单标签,必须以/>结尾

jsx的使用

jsx中的注释

jsx中使用 {/* */} 进行注释。

  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {}
      }

      render() {
        return (
          <div>
            {/* 这是一段注释 */}
          <h2>hello react</h2>
          </div>
        )
      }
    }

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

jsx嵌入变量

在jsx中可以嵌入变量,变量的类型不同,显示情况也会不同。

  • 当变量是Number、String、Array类型时,在{}中可以显示内容
  • 当变量是null、undefined、Boolean类型时,在{}中不显示内容

如果希望显示null、undefined、Boolean,那么就需要把它们转为字符串。可以和空字符串进行拼接、使用toString方法、String()方法将它们转换成字符串。

  • 对象类型不能作为子元素
  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          // 1. 在{}中可以正常显示内容
          name: 'haha',
          age: 20,
          names: ['aaa', 'bbb', 'ccc'],
          // 2. null、undefined、Boolean类型在{}中不能显示
          test1: null,
          test2: undefined,
          flag: true,
          // 3. 对象不能作为jsx的子类
          friend: {
            name: 'xixi',
            age: 20
          }
        }
      }

      render() {
        return (
          <div>
          <h2>{this.state.name}</h2>  
          <h2>{this.state.age}</h2>
          <h2>{this.state.names}</h2>

          <h2>{this.state.test1 + ''}</h2>
          <h2>{this.state.test2 + ''}</h2>
          <h2>{this.state.flag.toString()}</h2>

          {/*运算表达式*/}
          <h2>{this.state.flag ? '你好呀' : null}</h2>
          <h2>{this.state.friend}</h2>
          </div>
        )
      }
    }

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

  </script>

jsx嵌入表达式

在jsx中可以嵌入表达式,可以是运算表达式,也可以是三元运算符,还可以执行一个函数。

  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
           firstName: 'haha',
           lastName: 'xixi',
           isLogin: false
        }
      }

      render() {
        const {firstName, lastName, isLogin} = this.state
        return (
          <div>
            {/* 1. 运算表达式 */}
            <h2>{firstName + ' ' + lastName}</h2>
            <h2>{1 + 2}</h2>
            
            {/* 2. 三元运算符 */}
            <h2>{isLogin ? '欢迎光临' : '请先登陆'}</h2>

            {/* 3. 调用一个函数 */}
            <h2>{this.getFullName()}</h2>
          </div>
        )
      }

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

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

jsx绑定属性

我们知道img标签都有src属性,并且我们经常从服务器获取图片地址然后渲染到页面上,这是就需要给图片动态绑定src属性。还有些元素我们也需要给它们绑定clss。那么这些属性需要动态绑定,我们该怎么做呢?

接下来我们学习jsx如何绑定属性。学习如何绑定普通属性、绑定class、绑定style。

注意点

jsx语法和javascript语法是混在一起的。jsx不能使用javascript中的关键字,例如定义类的class,想要给标签加类名,应为className。还有html的label元素有for属性,它在javascript中也是关键字,所以在jsx中应使用htmlFor。

  <script type="text/babel">
    function getSizeImage(imgUrl, size) {
      return imgUrl + `?param=${size}x${size}`
    }

    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          title: '我是标题',
          imgUrl: 'http://p2.music.126.net/L8IDEWMk_6vyT0asSkPgXw==/109951163990535633.jpg',
          link: 'http://www.baidu.com',
          active: true
        }
      }

      render() {
        const { title, imgUrl, link, active } = this.state
        return (
          <div>
            {/* 1. 绑定普通属性 */}
            <h2 title={title}>我是标题</h2>
            {/* 单标签必须加/ */}
            <img src={getSizeImage(imgUrl, 140)} alt="" />

            {/* 2. 绑定class */}
            {/* 绑定class时不能使用class写类名,因为在javascript中class是关键字 */}
            <div className='box title'>我是div元素1</div>
            <div className={'box title' + active ? 'active' : ''}>我是div元素2</div>
            {/* 在label标签中不能使用for,因为在javascript中for是关键字 */}
            <label htmlFor=""></label>
            {/* 3.绑定style */}
            <div style={{ color: 'red', fontSize: '50px' }}>我是div3</div>
          </div>
        )
      }

    }

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

jsx绑定事件和this处理

在react中,我们需要经常绑定事件,例如点击事件。我们需要在{}中传入一个事件处理函数,这个函数会在事件发生时被执行。

我们需要知道,在render函数中,this是当前对象,我们可以通过this.state拿到数据。

现在页面中有一个按钮,点击按钮我想要获取到state中的message数据。以下代码会出现什么问题呢?

  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          message: '我是一条消息'
        }
      }

      render() {
        return (
          <div>
          <button onClick={this.btnClick}>按钮1</button>
          </div>
        )
      }

      btnClick() {
        console.log(this);
        console.log(this.state.message);
      }
    }

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

点击按钮,我们发现报了错误。打印btnClick这个函数的this,我们发现this指向的是undefined。这是为什么呢? image.png

因为btnClick函数并不是我们主动调用的,当button发生改变时,react内部调用了btnClick。react内部发现我们点击了按钮之后,会对这个btnClick做了回调,执行btnClick。而它内部调用时,并不知道如何绑定正确的this,所以绑定了this是undefined,做了btnClick.call(undefined,事件对象event)。

那么我们应该怎么解决这个this问题呢?

this绑定问题可以阅读我之前写得关于this指向的系列文章 javascript中的this指向总结(一)

  • 方法一:使用bind绑定this
      render() {
        return (
          <div>
          <button onClick={this.btnClick.bind(this)}>按钮1</button>
          <button onClick={this.btnClick.bind(this)}>按钮2</button>
          <button onClick={this.btnClick.bind(this)}>按钮3</button>
          </div>
        )
      }

但是这样写,存在多个按钮的话,需要给多个按钮btnClick使用bind。

我们可以这样写。

      constructor() {
        super()
        this.state = {
          message: '我是一条消息'
        }

        this.btnClick = this.btnClick.bind(this)
      }

      render() {
        return (
          <div>
            {/* 
              <button onClick={this.btnClick.bind(this)}>按钮1</button>
              <button onClick={this.btnClick.bind(this)}>按钮2</button>
              <button onClick={this.btnClick.bind(this)}>按钮3</button>
            */}

            <button onClick={this.btnClick}>按钮1</button>
            <button onClick={this.btnClick}>按钮2</button>
            <button onClick={this.btnClick}>按钮3</button>
          </div>
        )
      }
  • 方法二: 定义函数时,使用箭头函数 这种方法是ES6中给对象增加了属性: class fields

我们需要知道,箭头函数永远不绑定this。

    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          message: '我是一条消息',
          counter: 100
        }

        this.btnClick = this.btnClick.bind(this)
      }

      render() {
        return (
          <div>
            {/* 
              <button onClick={this.btnClick.bind(this)}>按钮1</button>
              <button onClick={this.btnClick.bind(this)}>按钮2</button>
              <button onClick={this.btnClick.bind(this)}>按钮3</button>
            */}

            {/* 方法一 bind绑定this */}
            <button onClick={this.btnClick}>按钮1</button>
            <button onClick={this.btnClick}>按钮2</button>
            <button onClick={this.btnClick}>按钮3</button>

            {/* 方法二 定义函数时,使用箭头函数 */}
            <button onClick={this.increment}>+1</button>
          </div>
        )
      }

      btnClick() {
        console.log(this);
        console.log(this.state.message);
      }

      increment = () => {
        console.log(this);
        console.log(this.state.counter);
      }
    }

我们可以看到结果正确打印出来了。

image.png

  • 方法三 事件监听时传入箭头函数,在剪头函数中调用需要执行的函数(推荐) 当我们点击按钮的时候,就会执行{}里的箭头函数,箭头函数的函数体里面的代码就会执行。我们知道箭头函数的函数体是不绑定this的,它就会从上层作用域找,找到render里面的this。render中的this指向的是对象。然后调用decrement,进行了隐式绑定。所以decrement中的this就是render中的this。

我们还可以往需要执行的函数中传递参数。

    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          message: '我是一条消息',
          counter: 100
        }

        this.btnClick = this.btnClick.bind(this)
      }

      render() {
        return (
          <div>
            {/* 
              <button onClick={this.btnClick.bind(this)}>按钮1</button>
              <button onClick={this.btnClick.bind(this)}>按钮2</button>
              <button onClick={this.btnClick.bind(this)}>按钮3</button>
            */}

            {/* 方法一 bind绑定this */}
            <button onClick={this.btnClick}>按钮1</button>
            <button onClick={this.btnClick}>按钮2</button>
            <button onClick={this.btnClick}>按钮3</button>

            {/* 方法二 定义函数时,使用箭头函数 */}
            <button onClick={this.increment}>+1</button>

            {/* 方法三 事件监听时传入一个箭头函数,在箭头函数中调用需要执行的函数 */}
            <button onClick={() => { this.decrement('haha') }}>-1</button>
          </div>
        )
      }

      btnClick() {
        console.log(this);
        console.log(this.state.message);
      }

      increment = () => {
        console.log(this);
        console.log(this.state.counter);
      }

      decrement(name) {
        console.log(this);
        console.log(this.state.counter, name);
      }
    }

点击按钮后我们能获取到counter和name的值。

image.png