• JSX基础语法



    认识JSX语法

    // 1. 定义根组件
    const element = <div>Hello World</div>
    
    // 2. 渲染根组件
    const root = ReactDOM.createRoot(document.querySelector("#root"))
    root.render(element)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上述代码中element变量的声明右侧赋值的标签语法其实就是一段JSX的语法。

    • 它不是一段字符串(因为没有使用引号包裹)
    • 它看起来是一段HTML元素,但是我们能在js中直接给一个变量赋值HTML吗?其实是不可以的,如果我们将type = "text/babel" 去除掉,那么就会出现语法错误。

    JSX是什么

    JSX是一种JavaScript的语法扩展,也在很多地方称之为JavaScript XML,因为看起来就是一段XML语法,它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用,它不同于Vue中的模块语法,不需要专门学习模块语法中的一些指令(比如v-if、v-for、v-bind等)

    为什么Rect选择了JSX

    React认为渲染逻辑本质上与其他UI逻辑存在内在耦合。比如UI需要绑定事件(button、a原生等等)、比如UI中需要展示数据的状态、比如在某些状态发生改变时,又需要改变UI,它们之间是密不可分的,所以React没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件。

    image-20231024111707288

    JSX书写规范

    • JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div元素(或者Fragment)
    • 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写
    • JSX中的标签可以是单标签,也可以是双标签(如果是单标签,必须以/>结尾)
    return (
        <div>
            <div>
                <h2>{message}</h2>
            </div>
            <div>哈哈哈</div>
        </div>
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSX注释编写

    return (
        <div>
            {/* JSX的注释写法*/}
            <h2>{message}</h2>
        </div>
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    JSX的基本使用

    1. JSX嵌入变量作为子元素

      • 情况一:当变量是Number、String、Array类型时,可以直接显示
      • 情况二:当变量是null、undefined、Boolean类型时,内容为空
        • 如果希望可以显示null、undefined、Boolean,那么需要转成字符串
        • 转换的方式有很多,比如toString方法、和空字符串拼接、String等方式
      • 情况三:Object对象类型不能作为子元素
    2. JSX嵌入表达式

      • 运算表达式
      • 三元运算符
      • 执行一个函数
    3. JSX绑定属性

      • 比如元素都会有title属性
      • 比如img元素会有src属性
      • 比如a元素会有href属性
      • 比如元素可能需要绑定class
      • 比如元素使用内联样式style

    JSX的事件绑定

    如果原生DOM有一个监听事件,我们可以如何操作?

    • 方式一:获取DOM元素,添加监听事件
    • 方式二:在HTML原生中,直接绑定onclick

    在React中是如何操作呢?

    • React事件的命名采用小驼峰(cameClass),而不是纯小写
    • 我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行
    <body>
      <div id="root"></div>
      <script type="text/babel">
    
        // 类组件
        class App extends React.Component {
          // 组件数据
          constructor() {
            super()
            this.state = {
              message: "Hello World"
            }
          }
    
          // 组件方法
          btnClick() {
            // 内部完成了两件事:
            // 1. 将state中的message值修改掉
            // 2. 自动重新执行render函数
            this.setState({
              message: "Hello React"
            })
          }
    
          // 渲染内容
          render() {
            // 简单数据
            // return 

    Hello World

    // 复杂数据 return ( <div> <h2>{this.state.message}</h2> <button onClick={this.btnClick.bind(this)}>修改文本</button> </div> ) } // 为什么这里要bind一下? 因为类中的方法中的this默认指向undefined, 所以要将render函数中的this给方法 } // 将组件渲染到界面上 const container = document.getElementById('root'); const root = ReactDOM.createRoot(container); root.render(<App />); </script> </body>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    这种数据的数据是定义在当前对象的state中的,我们可以通过构造函数中的this.state={定义的数据},然后当我们数据发生变化时,我们可以调用this.setState来更新数据,并且通知React进行update操作,在进行update操作时,会重新调用render函数,并且使用最新的数据来渲染界面。

    this绑定问题

    上述代码还存在this绑定问题

    在事件执行后,我们可能需要获取当前类的对象中相关的属性,这个时候需要用到this。

    在类中直接定义一个函数,并且将这个函数绑定到元素的onClick事件上,当前这个函数的this指向是谁呢?

    默认情况下是undefined,因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(比如说button对象),但是因为React并不是直接渲染成真实DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象,那么在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个undefined。

    我们在绑定的函数中,可能想要使用当前对象,比如执行this.setState函数,就必须拿到当前对象的this,我们就需要在传入函数时,给这个函数直接绑定this:

    • 方案一:bind给btnClick显式绑定this
    // 写法一
    <button onClick={this.btnClick.bind(this)}>修改文本</button>
    
    // 写法二
    constructor() {
      super()
      this.state = {
        message: "Hello World"
      }
      this.btnClick = this.btnClick.bind(this)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 方案二:使用ES6 class fields 语法
    • 方案三:事件监听时传入箭头函数(推荐)
    <body>
      <div id="root"></div>
      <script type="text/babel">
        /*
        this的四种绑定规则:
        1. 默认绑定 独立执行 
        2. 隐式绑定 作为对象中的方法执行
        3. 显式绑定 call apply bind
        4. new绑定
        */
    
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              counter: 100
            }
          }
    
          btn1Click() {
            console.log("btn1Click")
            this.setState({ counter: this.state.counter + 1 })
          }
    
          // 所有实例都会有btn2Click这个字段,将一个箭头函数赋值给btn2Click,而箭头函数没有this,就会去上层作用域中查找,找到的是当前类的作用域的this,而this正好指向当前实例
          btn2Click = () => {
            console.log("btn2Click")
            this.setState({ counter: this.state.counter + 1 })
          }
    
          btn3Click = () => {
            console.log("btn3Click")
            this.setState({ counter: this.state.counter + 1 })
          }
    
          render() {
            return (
              <div>
                <h2>当前计数:{this.state.counter}</h2>
    
                {/*1. this绑定方式一:bind绑定*/}
                <button onClick={this.btn1Click.bind(this)}>按钮1</button>
    
                {/*2. this绑定方式二:ES6 class fields*/}
                <button onClick={this.btn2Click}>按钮2</button>
    
                {/*3. this绑定方式三:直接传入一个箭头函数(重要)*/}
                <button onClick={() => this.btn3Click()}>按钮3</button>
                {/*怎么做的?形成一个隐式绑定,this是当前实例,btn3Click作为当前实例的方法被调用*/}
              </div>
            )
          }
    
        }
    
        const container = document.getElementById('root');
        const root = ReactDOM.createRoot(container);
        root.render(<App />);
      </script>
    </body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    参数传递问题

    • 情况一:获取event对象
      • 很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)
      • 那么默认情况下,event对象有被直接传入,函数就可以获取到event对象
    • 情况二:获取更多参数
      • 有更多参数时,最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数
    <body>
      <div id="root"></div>
      <script type="text/babel">
    
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              counter: 100
            }
          }
    
          btn1Click(event) {
            console.log("btn1Click", event)
          }
    
          btn2Click(event, name, age) {
            console.log("btn2Click", event, name, age)
          }
    
          render() {
            return (
              <div>
                <h2>当前计数:{this.state.counter}</h2>
    
                {/*1. event参数的传递 */}
                <button onClick={this.btn1Click.bind(this)}>按钮1</button>
                <button onClick={(event) => this.btn1Click(event)}>按钮1</button>
    
                {/*2. 额外的参数传递*/}
                <button onClick={this.btn2Click.bind(this,)}>按钮2</button>
                <button onClick={(event) => this.btn1Click(event, "zy", 18)}>按钮2</button>
    
              </div>
            )
          }
    
        }
    
        const container = document.getElementById('root');
        const root = ReactDOM.createRoot(container);
        root.render(<App />);
      </script>
    </body>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    JSX的条件渲染

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

    • 在vue中,我们会通过指令来控制:比如v-if、v-show
    • 在React中,所有的条件判断都和普通的JavaScript代码一致

    常见的条件渲染方式

    • 方式一:条件判断语句,适合逻辑较多的情况
    • 方式二:三元运算符,适合逻辑比较简单
    • 方式三:与运算符&&,适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染
    <body>
      <div id="root"></div>
      <script type="text/babel">
    
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              isReady: true
            }
          }
    
          render() {
            const { isReady } = this.state
    
            let showElement = null
            if (isReady) {
              showElement = <h2>准备开始比赛吧</h2>
            } else {
              showElement = <h2>请提前做好准备!</h2>
            }
            return (
              <div>
                {/*1. 方式一:根据条件给变量赋值不同的内容*/}
                <div>{showElement}</div>
    
                {/*2. 方式二:三元运算符*/}
                <div>{isReady ? <button>开始战斗!</button> : <h3>准备</h3>}</div>
    
                {/*3. 方式三:&&逻辑与运算*/}
                {/*场景:当某一个值,有可能为undefined时,使用&&进行条件判断*/}
                <div>{info && <div>{info.name + " " + info.desc}</div>}</div>
              </div >
            )
          }
    
        }
    
        const container = document.getElementById('root');
        const root = ReactDOM.createRoot(container);
        root.render(<App />);
      </script>
    </body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    JSX的列表渲染

    真实开发中我们会从服务器请求到大量数据,数据会以列表的形式存储:

    • 比如歌手、歌曲、排行榜列表的数据
    • 比如商品、购物车、评论列表的数据
    • 比如好友消息、动态、联系人列表数据

    在React中并没有像Vue模块语法中的v-for指令,而且需要我们通过JavaScript代码的方式组织数据,转成JSX,那到底如何展示列表呢?在React中,展示列表最多的方式就是使用数组的map高阶函数。很多时候我们在展示一个数据之前,需要先对它进行一些处理:比如过滤掉一些内容:filter函数;比如截取数组中的一部分内容:slice函数。

    <body>
      <div id="root"></div>
      <script type="text/babel">
    
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              students: [
                { id: 1, name: "zy", score: 99 },
                { id: 2, name: "lgc", score: 88 },
                { id: 3, name: "mxy", score: 100 },
                { id: 4, name: "jack", score: 66 },
                { id: 5, name: "kobe", score: 92 },
              ]
            }
          }
    
          render() {
            const { students } = this.state
            return (
              <div>
                <h2>学生列表数据</h2>
                <div className="list">
                  {
                    //  选出成绩大于90分的人
                    students.filter(item => item.score > 90).map(item => {
                      return (
                        <div className="item" key={item.id}>
                          <h2>学号:{item.id}</h2>
                          <h2>姓名:{item.name}</h2>
                          <h2>分数:{item.score}</h2>
                        </div>
                      )
                    })
                  }
                </div>
              </div>
            )
          }
    
        }
    
        const container = document.getElementById('root');
        const root = ReactDOM.createRoot(container);
        root.render(<App />);
      </script>
    </body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    JSX的原理和本质

    image-20231025111055241

    JSX的本质

    实际上,jsx仅仅只是React.createElement(component, props, ...children)函数的语法糖,所有的jsx最终都会被转换成React.createElement的函数调用

    React.createElement需要传递三个参数:

    1. 参数一:type
      • 当前ReactElement的类型
      • 如果是标签元素,那么就使用字符串表示“div”
      • 如果是组件元素,那么就直接使用组件的名称
    2. 参数二:config
      • 所有jsx中的属性都在config中以对象的属性和值的形式存储
      • 比如传入className作为元素的class
    3. 参数三:children
      • 存放在标签中的内容,以children数组的方式进行存储
      • 如果是多个元素,React内部有对它们进行处理

    image-20231025160745269

    虚拟DOM的创建过程

    我们通过React.createElement最终创建出来一个ReactElement对象,这个ReactElement对象是什么作用呢?React为什么要创建它呢?这是因为React利用ReactElement对象组成了一个JavaScript的对象树,JavaScript的对象树就是虚拟DOM。

    如何查看ReactElement的树结构呢?我们可以将之前的就实现返回结果进行打印:

    image-20231025161606611

    jsx-虚拟DOM-真实DOM

    image-20231025161728352

    案例练习

    要求:

    1. 在界面上以表格的形式,显示一些书籍的数据
    2. 在底部显示书籍的总价格
    3. 点击+或者-可以增加或减少书籍(如果为1,那么不能继续-)
    4. 点击移除按钮,可以将书籍移除(当所有书籍移除完毕时,显示:购物车为空~)

    image-20231025163055858

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      <title>购物车</title>
      <!-- 添加依赖 -->
      <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
      <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
      <!-- babel -->
      <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    
      <style>
        table {
          border-collapse: collapse;
          text-align: center;
        }
    
        thead {
          background-color: #f2f2f2;
        }
    
        td,
        th {
          padding: 10px 16px;
          border: 1px solid black;
        }
      </style>
    </head>
    
    <body>
      <div id="root"></div>
    
      <script src="./data.js"></script>
    
      <script type="text/babel">
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              books: books
            }
          }
    
          format(price) {
            return "¥" + Number(price).toFixed(2);
          }
    
          // 方法
          increment(index) {
            const newBooks = [...this.state.books]
            newBooks[index].count += 1
            this.setState({ books: newBooks })
          }
    
          decrement(index) {
            const newBooks = [...this.state.books]
            newBooks[index].count -= 1
            this.setState({ books: newBooks })
          }
    
          removeItem(index) {
            const newBooks = [...this.state.books]
            newBooks.splice(index, 1)
            this.setState({ books: newBooks })
          }
    
          render() {
            const { books } = this.state
    
            // 计算总价
            let totalPrice = 0
            for (let i = 0; i < books.length; i++) {
              totalPrice += books[i].count * books[i].price
            }
    
            return (
              <div>
                <table>
                  <thead>
                    <tr>
                      <th>序号</th>
                      <th>书籍名称</th>
                      <th>出版日期</th>
                      <th>价格</th>
                      <th>购买数量</th>
                      <th>操作</th>
                    </tr>
                  </thead>
    
                  <tbody >
                    {
                      books.map((item, index) => {
                        return (
                          <tr key={item.id}>
                            <td>{item.id}</td>
                            <td>{item.name}</td>
                            <td>{item.data}</td>
                            <td>{this.format(item.price)}</td>
                            <td>
                              <button disabled={item.count <= 1} onClick={() => this.decrement(index)}>-</button>
                              {item.count}
                              <button onClick={() => this.increment(index)}>+</button>
                            </td>
                            <td><button onClick={() => this.removeItem(index)}>移除</button></td>
                          </tr>
                        )
                      })
                    }
                  </tbody>
                </table>
                <h1>总价格:{this.format(totalPrice)}</h1>
              </div>
            )
          }
    
        }
    
        const container = document.getElementById('root');
        const root = ReactDOM.createRoot(container);
        root.render(<App />);
      </script>
    </body>
    
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    // data.js
    const books = [
        {
            id: 1,
            name: '《算法导论》',
            data: '2006-9',
            price: 85.0,
            count: 1,
        },
        {
            id: 2,
            name: '《UNIX编程艺术》',
            data: '2006-9',
            price: 59.0,
            count: 1,
        },
        {
            id: 3,
            name: '《编程珠玑》',
            data: '2006-9',
            price: 39.0,
            count: 1,
        },
        {
            id: 4,
            name: '《代码大全》',
            data: '2006-9',
            price: 128.0,
            count: 1,
        },
    ];
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    container);
    root.render();

    ```
    // data.js
    const books = [
        {
            id: 1,
            name: '《算法导论》',
            data: '2006-9',
            price: 85.0,
            count: 1,
        },
        {
            id: 2,
            name: '《UNIX编程艺术》',
            data: '2006-9',
            price: 59.0,
            count: 1,
        },
        {
            id: 3,
            name: '《编程珠玑》',
            data: '2006-9',
            price: 39.0,
            count: 1,
        },
        {
            id: 4,
            name: '《代码大全》',
            data: '2006-9',
            price: 128.0,
            count: 1,
        },
    ];
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 相关阅读:
    springcloud+nacos初体验
    java 版本企业招标投标管理系统源码+多个行业+tbms+及时准确+全程电子化
    Jmeter基础篇
    Nginx 文件名逻辑漏洞(CVE-2013-4547)(Vulhub)
    【数据结构笔记3】- 栈和队列 基础到实战-入门到应用
    # Sharding-JDBC从入门到精通(1)- 概述-分库分表
    基于MATLAB的随机搜索算法优化带有速度的路由网络
    Go单元测试及框架使用
    MySQL高级篇——事务
    数学建模笔记-第十讲-聚类
  • 原文地址:https://blog.csdn.net/ZhangYu_010228/article/details/134048779