JSX核心语法及JSX本质浅析

4,518 阅读6分钟

一、JSX核心语法

1.什么是JSX?

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

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

  • 比如UI需要绑定事件(button、a原生等等);
  • 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI;
  • 他们之间是密不可分,所以React没有讲标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件 (Component);
  • 在这里,我们只需要知道,JSX其实是嵌入到JavaScript中的一种结构语法;

2.JSX的书写规范

  • JSX的顶层只能有一个根元素,我们很多时候会在最外层包裹一个div(后续React推出了不占据Dom结构的Fragment)
  • 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
  • JSX中的标签可以使单标签,也可以是双标签(如果是单标签,必须以/>结尾

3.JSX中的注释

单行注释和多行注释,都需要在大括号{}中编写

{
    //我是单行注释
}
{/*我是一段注释*/}

4.JSX中嵌入变量

  • 当变量是Number、String、Array类型时,可以直接显示;
  • 当变量是null、undefined、Boolean类型时,内容显示为空(如果想让其展示可以将其转换为字符串进行展示);
  • 对象类型不能作为变量嵌入('not valid as a React child')
 {
          // 1.在{}中可以正常显示显示的内容
          name: "lanzhou", // String
          age: 20, // Number
          names: ["abc", "cba", "nba"], // Array

          // 2.在{}中不能显示(忽略)
          test1: null, // null
          test2: undefined, // undefined
          test3: true, // Boolean

          // 3.对象不能作为jsx的子类
          friend: {
            name: "lanzhou",
            age: 20
          }
        }

5.JSX嵌入表达式

  • 运算表达式
  • 三元运算符
  • 执行一个函数
 render() {
        const {firstName, lastName, isLogin} = this.state;
        return (
          <div>
            {/*1.运算符表达式*/}
            <h2>{ firstName + " " + lastName }</h2>
            <h2>{20 * 50}</h2>

            {/*2.三元表达式*/}
            <h2>{ isLogin ? "欢迎回来~": "请先登录~" }</h2>

            {/*3.进行函数调用*/}
            <h2>{this.sayHello()}</h2>
          </div>
        )
    }

6.JSX绑定属性

  • JSX绑定普通属性
  • JSX绑定class
  • JSX绑定style
 render() {
        const { title, imgUrl, link, active } = this.state;
        return (
          <div>
            {/* 1.绑定普通属性 */}
            <h2 title={title}>我是标题</h2>
            <img src={getSizeImage(imgUrl, 140)} alt=""/>
            <a href={link} target="_blank">百度一下</a>

            {/* 2.绑定class */}
            <div className="box title">我是div元素</div>
            <div className={"box title " + (active ? "active": "")}>我也是div元素</div>

            {/* 3.绑定style */}
            <div style={{color: "red", fontSize: "50px"}}>我是div,绑定style属性</div>
          </div>
      )

7.JSX绑定事件

  • JSX中绑定事件类似在HTML原生中绑定事件,只不过React中事件命名采用小驼峰(camelCase),而不是纯小写;
  • 但是我们会发现在我们绑定的回调事件中访问我们对应的this会是undefined,这是因为对应的回调函数是React内部帮我们去进行调用的,React无法确定对应的this所以采用的是callback.apply(undefined,[])方式调用,改变了this的指向为undefined。但是我们往往需要访问到的this是我们React组件对象。解决方法有以下三种:
 increment = () => {
     console.log(this);
  }
render() {
        return (
          <div>
            {/* 1.方案一: bind绑定this(显示绑定) */}
            <button onClick={this.btnClick.bind(this)}>按钮1</button>
            <button onClick={this.btnClick.bind(this)}>按钮2</button>
            <button onClick={this.btnClick.bind(this)}>按钮3</button>

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

            {/* 2.方案三(推荐): 直接传入一个箭头函数, 在箭头函数中调用需要执行的函数*/}
            <button onClick={() => { this.decrement(666) }}>-1</button>
          </div>
        )
     }
  • 绑定事件参数的传递,推荐采用绑定this的方案三,既方便的传递我们想要传递的参数,还可以非常简单的获取到事件对象event
  render() {
        return (
          <div>
            <ul>
              {
                this.state.movies.map((item, index, arr) => {
                  return (
                    <li className="item" 
                        onClick={ e => { this.liClick(item, index, e) }}
                        title="li">
                      {item}
                    </li>
                  )
                })
              }
            </ul>
          </div>
        )
      }

8.JSX条件渲染

某些情况下,页面会根据不同的条件展示不同的内容,或者决定是否渲染某部分内容。 在Vue中给我们提供了对应的v-if、v-show指令,然而React中实现条件渲染的方式有那些呢?

  1. 条件判断语句
  2. 三元运算符
  3. 与运算符 &&
  4. v-show效果(主要是控制html标签的display属性是否为none)
render() {
        const { isLogin } = this.state;

        // 1.方案一:通过if判断: 逻辑代码非常多的情况
        let welcome = null;
        let btnText = null;
        if (isLogin) {
          welcome = <h2>欢迎回来~</h2>
          btnText = "退出";
        } else {
          welcome = <h2>请先登录~</h2>
          btnText = "登录";
        }

        return (
          <div>
            {welcome}
            
            {/* 2.方案二: 三元运算符 */}
            <button onClick={e => this.loginClick()}>{isLogin ? "退出" : "登录"}</button>
            <h2>{isLogin ? "你好啊, 666": null}</h2>

            {/* 3.方案三: 逻辑与&& */}
            {/* 逻辑与: 一个条件不成立, 后面的条件都不会进行判断了 */}
            <h2>{ isLogin && "你好啊, 666" }</h2>
            { isLogin && <h2>你好啊, 666</h2> }
          </div>
        )
     }
{/* 4.方案四: 根据一个状态动态更改对应标签的display属性的值为block或none */}
 render() {
        const { isLogin} = this.state;
        const titleDisplayValue = isLogin ? "block": "none";
        return (
          <div>
            <button onClick={e => this.loginClick()}>{isLogin ? "退出": "登录"}</button>
            <h2 style={{display: titleDisplayValue}}>你好啊, 666</h2>
          </div>
        )
      }

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

9.JSX列表渲染

在React中列表生成及展示运用最多的是js中高阶函数,当然我们也可以运用for循环来进行列表的生成及展示。

render() {
        return (
          <div>
            <h2>名字列表</h2>
            <ul>
              {
                this.state.names.map(item => {
                  return <li>{item}</li>
                })
              }
            </ul>

            <h2>数字列表(过滤1)</h2>
            <ul>
              {
                this.state.numbers.filter(item => {
                  return item >= 50;
                }).map(item => {
                  return <li>{item}</li>
                })
              }
            </ul>

            <h2>数字列表(过滤2)</h2>
            <ul>
              {
                this.state.numbers.filter(item => item >= 50).map(item => <li>{item}</li>)
              }
            </ul>

            <h2>数字列表(截取)</h2>
            <ul>
              {
                this.state.numbers.slice(0, 4).map(item => {
                  return <li>{item}</li>
                })
              }
            </ul>
          </div>
        )
      }

二、JSX本质浅析

JSX实际上仅仅是React.createElement(type, config, children)方法的语法糖,该方法接收三个参数:

  1. type,当前ReactElement的类型,如果是标签元素,那么使用字符串表示“div”,如果是组件元素直接使用组件的名称就可以。
  2. config,我们在JSX中绑定的属性会在config对象中以键值对的形式存在。
  3. children,存放标签中的内容,以children数组的形式存储 我们知道JSX是通过babel进行解析的,我们编写JSX需要依赖babel,我们可以在babel官网查看转换的过程 babeljs.io/repl/#?pres…

捕获.JPG 如果我们直接使用React.createElement()来编写代码,就不需要以来bable进行解析也可以正常的渲染显示

捕获1.JPG 我们通过React.createElement()方法最后返回得到的是一个ReactElement对象,这个ReactElement对象作用是什么?其实React利用ReactElement对象组成了一个JavaScript对象树,这个对象树就是我们经常讲的一个概念--虚拟DOM,我们可以将之前jsx返回的结果进行打印来查看对应的ReactElemnt对象:

捕获3.JPG

捕获2.JPG 首先是我们编写的JSX代码经过bable编译解析成对应的React.createElement()方法形式,经过React.createElement()方法调用返回我们对应的ReactElement对象树(虚拟DOM树),对应的ReactElement对象树经过ReactDOM.render()方法转换为真正的DOM在我们的浏览器进行渲染。

graph TD
JSX --> ReactElement(虚拟DOM) --> 真实DOM

为什么采用虚拟DOM呢?

  • 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试;
  • 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低; DOM操作非常耗费性能:
  • document.createElement本身创建出来的就是一个非常复杂的对象developer.mozilla.org/zh-CN/docs/…
  • DOM操作会引起浏览器的回流和重绘,所以在开发中应该避免频繁的DOM操作