01_JSX基础

403 阅读8分钟

1.介绍

概念:JSX是一种JavaScript语法扩展(eXtension),也有很多地方称之为Javascript XML(HTML)的缩写,表示在JS代码中书写HTML结构

作用:用于描述UI界面,并且可以和JavaScript融合在一起使用

优势:

  1. 采用类似于HTML的语法,降低学习成本,会HTML就会JSX
  2. 充分利用JS自身的可编程能力创建HTML结构

为什么选择React选择JSX:

  1. React认为渲染逻辑与其他UI逻辑存在内在耦合

    1. 比如UI需要绑定事件(button,a原生等)
    2. 比如UI需要展示数据状态
    3. 比如在某些状态发生变化是,又需要改变UI
  2. 它们之间是密不可分的,所以React没有将标记分离到不同的文件中,而是将他们组合,这个地方就是组件

注意事项:

  1. JSX必须有一个根节点,如果没有根节点,可以使用<></>(Frament节点)替代
  2. 所有标签必须形成闭合,成对闭合或者自闭合都可以
  3. JSX中的语法更加贴近JS语法,属性名采用驼峰命名法  class -> className  for -> htmlFor
  4. JSX支持多行(换行),如果需要换行,需使用() 包裹,防止bug出现

2.基本使用

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      counter: 10,
      message: "Hello World",
      names: ["小红", "小明", "小李"],

      isUndefine: undefined,
      isNull: null,
      isBoolean: true,

      friend: { name: "yyy" },

      firstName: "cl",
      lastName: "y",

      age: 20,

      movieList: ["大话西游", "肖申克的救赎", "速度与激情9"]
    }
  }

  getMovieEls() {
    return this.state.movieList.map(movie => <li>{movie}</li>)
  }

  render() {
    const { message, names, counter } = this.state
    const { isUndefine, isNull, isBoolean } = this.state
    const { friend } = this.state

    const { firstName, lastName } = this.state
    const fullName = firstName + " " + lastName
    
    const { age } = this.state
    const ageText = age >= 18 ? "大人": "小孩"
    
    const liEls = this.state.movieList.map(movie => <li>{movie}</li>)

    return (
      <div>
        {/* 1.Number/String/Array直接显示 */}
        <h2>{counter}</h2>
        <h2>{message}</h2>
        <h2>{names}</h2>

        {/* 2.undefined/null/Boolean */}
        <h2>{String(isUndefine)}</h2>
        <h2>{isNull + ""}</h2>
        <h2>{isBoolean.toString()}</h2>

        {/* 3.Object类型不能作为子元素进行显示*/}
        <h2>{friend.name}</h2>
        <h2>{Object.keys(friend)[0]}</h2>

        {/* 4.可以插入对应的表达式*/}
        <h2>{10 + 20}</h2>
        <h2>{firstName + " " + lastName}</h2>
        <h2>{fullName}</h2>

        {/* 5.可以插入三元运算符*/}
        <h2>{ageText}</h2>
        <h2>{age >= 18 ? "大人": "小孩"}</h2>

        {/* 6.可以调用方法获取结果*/}
        <ul>{this.state.movieList.map(movie => <li>{movie}</li>)}</ul>
        <ul>{liEls}</ul>
        <ul>{this.getMovieEls()}</ul>
      </div>
    )
  }
}
  1.  JSX中注释写法:{ /* JSX的注释写法 */ }

  2. 当变量是 Number/String/Array 类型时,可以直接显示

  3. 当变量是 undefined/null/Boolean 类型时,内容为空

    1. 如果希望展示 undefined/null/Boolean,需要转换成字符串
    2. 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式
  4. Object对象类型不能作为子元素(not valid a React child)

  5. 使用JS表达式

    1. 运算表达式 { 10 + 20 }
    2. 三元表达式 { age >= 18 ? '成年人' : '未成年人'}
    3. 执行一个函数
    4. 特别注意:if语句/switch-case语句/变量声明语句,这些叫做语句,不是表达式,不能出现在{}

3.绑定属性

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      title: "哈哈哈",
      imgURL: "https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/6c61ae65d1c41ae8221a670fa32d05aa.svg",
      href: "https://www.baidu.com",

      isActive: true,
      objStyle: {color: "red", fontSize: "30px"}
    }
  }

  render() {
    const { title, imgURL, href, isActive, objStyle } = this.state
    // 需求: isActive: true -> active
    // 1.class绑定的写法一: 字符串的拼接
    const className = `abc cba ${isActive ? 'active': ''}`
    // 2.class绑定的写法二: 将所有的class放到数组中
    const classList = ["abc", "cba"]
    if (isActive) classList.push("active")
    // 3.class绑定的写法三: 第三方库classnames -> npm install classnames
    return (
      <div>
        { /* 1.基本属性绑定 */ }
        <h2 title={title}>我是h2元素</h2>
        {/*<img src={imgURL} alt=""/>*/}
        <a href={href}>百度一下</a>

        { /* 2.绑定class属性: 最好使用className */ }
        <h2 className={className}>哈哈哈哈</h2>
        <h2 className={classList.join(" ")}>哈哈哈哈</h2>

        { /* 3.绑定style属性: 绑定对象类型 */ }
        <h2 style={{color: "red", fontSize: "30px"}}>呵呵呵呵</h2>
        <h2 style={objStyle}>呵呵呵呵</h2>
      </div>
    )
  }
}

4.事件绑定

在原生DOM中监听一个事件,有两种方式:

  1. 获取原生DOM,添加监听事件
  2. 在HTML中,直接绑定onclick

在React中监听事件,需要注意以下两点:

  1. React事件的命名采用小驼峰(camelCase),而不是纯小写
  2. 需要通过{}传入一个事件处理函数,这个函数会在事件发生时执行

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

为什么是undefined呢?

  • 原因是btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;
  • 而它内部调用时,并不知道要如何绑定正确的this;

如何解决this的问题呢?

  • 方案一:bind给btnClick显示绑定this
  • 方案二:使用 ES6 class fields 语法
  • 方案三:事件监听时传入箭头函数(推荐)
class App extends React.Component {
  // class fields
  name = "App"
  constructor() {
    super()
    this.state = {
      message: "Hello World",
      counter: 100
    }
    this.btn1Click = this.btn1Click.bind(this)
  }
  btn1Click() {
    console.log("btn1Click", this);
    this.setState({ counter: this.state.counter + 1 })
  }
  // class fields
  btn2Click = () => {
    console.log("btn2Click", this)
    this.setState({ counter: 1000 })
  }
  btn3Click() {
    console.log("btn3Click", this);
    this.setState({ counter: 9999 })
  }
  render() {
    const { message } = this.state
    return (
      <div>
        {/* 1.this绑定方式一: bind绑定 */}
        <button onClick={this.btn1Click}>按钮1</button>
        {/* 2.this绑定方式二: ES6 class fields */}
        <button onClick={this.btn2Click}>按钮2</button>
        {/* 3.this绑定方式三: 直接传入一个箭头函数(重要) */}
        <button onClick={() => console.log("btn3Click")}>按钮3</button>
        <button onClick={() => this.btn3Click()}>按钮3</button>
        <h2>当前计数: {this.state.counter}</h2>
      </div>
    )
  }
}

在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数

情况一:获取event对象

  • 很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)
  • 那么默认情况下,event对象有被直接传入,函数就可以获取到event对象;

情况二:获取更多参数

  • 有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;
class App extends React.Component {
	constructor() {
		super()
		this.state = {
			message: "Hello World"
		}
	}
	btnClick(event, name, age) {
		console.log("btnClick:", event, this)
		console.log("name, age:", name, age)
	}
	render() {
		const { message } = this.state
		return (
			<div>
				{/* 1.event参数的传递 */}
				<button onClick={this.btnClick.bind(this)}>按钮1</button>
				<button onClick={(event) => this.btnClick(event)}>按钮2</button>
				{/* 2.额外的参数传递 */}
				<button onClick={this.btnClick.bind(this, "kobe", 30)}>按钮3(不推荐)</button>
				<button onClick={(event) => this.btnClick(event, "why", 18)}>按钮4(推荐写法)</button>
			</div>
		)
	}
}

5.条件渲染

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

  • 在vue中,我们会通过指令来控制:比如v-if、v-show;

  • 在React中,所有的条件判断都和普通的JavaScript代码一致;

    • 方式一:条件判断语句 : 适合逻辑较多的情况
    • 方式二:三元运算符: 适合逻辑比较简单
    • 方式三:与运算符&&: 适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染;
    • v-show的效果 : 主要是控制display属性是否为none
class App extends React.Component {
	constructor() {
		super()
		this.state = {
			message: "Hello World",
			isReady: false,
			friend: undefined
		}
	}
	render() {
		const { isReady, friend } = this.state
		// 1.条件判断方式一: 使用if进行条件判断
		let showElement = null
		if (isReady) {
			showElement = <h2>准备开始比赛吧</h2>
		} else {
			showElement = <h1>请提前做好准备!</h1>
		}
		return (
			<div>
				{/* 1.方式一: 根据条件给变量赋值不同的内容 */}
				<div>{showElement}</div>
				{/* 2.方式二: 三元运算符 */}
				<div>{ isReady ? <button>开始战斗!</button>: <h3>赶紧准备</h3> }</div>
				{/* 3.方式三: &&逻辑与运算 */}
				{/* 场景: 当某一个值, 有可能为undefined时, 使用&&进行条件判断 */}
				<div>{ friend && <div>{friend.name + " " + friend.desc}</div> }</div>
				 {/* v-show的效果 */}
        <h2 style={{display: isReady ? 'block': 'none'}}>哈哈哈哈</h2>
			</div>
		)
	}
}

6.列表渲染

页面的构建离不开重复的列表结构,比如歌曲列表、商品列表等,比如vue中用的是v-for

在React中,展示列表最多的方式就是使用数组的map高阶函数;

过滤掉一些内容:filter函数

截取数组中的一部分内容:slice函数

const songs = [
  { id: 1, name: 'AAA' },
  { id: 2, name: 'bbb' },
  { id: 3, name: 'CCC' }
]

function App() {
  return (
    <div className="App">
      <ul>
        {
          songs.map(item => <li>{item.name}</li>)
        }
      </ul>
    </div>
  )
}

export default App

注意点:需要为遍历项添加key属性

  1. key 在HTML结构中是看不到的,是React内部用来进行性能优化的
  2. key 在当前列表中要唯一的字符串或者数值(String/Number)
  3. 如果列表中有像 id 这种唯一值,就用 id 作为 key 来使用
  4. 如果列表中没有像 id 这种的唯一值,可以使用 index(下标) 来作为key值(不推荐)

7.JSX的本质

实际上,jsx仅仅是React.createElement(type, props, ...children)函数的语法糖

所有的jsx最终都会被转换成React.createElement的函数调用

createElement()函数需要传递三个参数:

  • 参数一:type
    • 当前ReactElement的类型
    • 如果是标签元素,直接使用字符串表示,如 "div"
    • 如果是组件,使用组件的名称,如 <HelloWorld/>
  • 参数二:props
    • 所有jsx中的属性都在config中以对像的属性和值的形式存储
    • 比如传入className作为元素的class
  • 参数三:children
    • 存放在标签中的内容,以数组的方式进行存储

8.Babel官网

默认jsx是通过babel帮我们进行语法转换的,所以之前写的jsx代码都需要依赖babel。

可以在babel的官网中快速查看转换的过程:babeljs.io/repl/#?pres…

9.JSX本质和原理

JSX会被babel编译为React.createElement

通过React.createElement最终创建出来的是一个ReactElement对象 虚拟DOM(Virtual DOM)

最后通过ReactDOM.rtender 将 虚拟DOM(Virtual DOM)创建真实DOM

这个ReactElement对象是什么作用?React为什么要创建它?

  1. 原因是React利用ReactElement对象组成了一个JavaScript的对象树
  2. 这个JavaScript的对象树就是虚拟DOM(Virtual DOM)

怎么查看ReactElement的树结构?

  1. 可以将之前的JSX返回结果进行打印:console.log(new App().render())

10.声明式编程

虚拟DOM帮助我们从命令式编程转到了声明式编程的模式

React官方的说法: Virtual DOM 是一种编程理念

  1. 在这个理念中,UI是以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象
  2. 通过ReactDOM.render 让 虚拟DOM 和 真实DOM 同步起来,这个过程叫做协调 这种编程方式赋予了React声明式的API
  3. 你只需要告诉React希望UI是什么状态
  4. React确保DOM和这些状态时匹配的
  5. 你不需要直接进行DOM操作,就可以从手动更改DOM、属性操作、事件处理中解放出来