React学习笔记[2]✨~JSX语法详解👻

98 阅读10分钟

我正在参加「掘金·启航计划」

一、认识JSX

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

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

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <script type="text/babel">

    const element = <div>123</div>

    const app = ReactDOM.createRoot(document.querySelector('#app'))
    app.render(element)
  </script>

</body>

</html>

这段element变量的声明右侧赋值的标签语法是什么呢?

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

其实它是一段JSX的语法,那么JSX是什么呢?

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

二、为什么React选择了JSX

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

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

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

三、JSX书写规范

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>计数器</title>
</head>
<body>
  
  <div id="root"></div>

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    // 1.定义App根组件
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          message: "Hello World"
        }
      }

      render() {
        const { message } = this.state
        return (
          <div>
            <div>
              <h2>{message}</h2>
              <br/>
            </div>
            <div>哈哈哈</div>
          </div>
        )
      }
    }

    // 2.创建root并且渲染App组件
    const root = ReactDOM.createRoot(document.querySelector("#root"))
    root.render(<App></App>)
  </script>

</body>
</html>
  1. JSX结构中只能有一个根元素
  2. JSX结构通常会包裹一个(), 将整个jsx当做一个整体, 实现换行
  3. JSX可以是单标签, 也可以双标签, 但是单标签必须以 /> 结尾

四、JSX中的注释

在JSX中注释内容要包含在'{}'中:

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      message: "Hello World"
    }
  }

  render() {
    const { message } = this.state

    return (
      <div>
        { /* JSX的注释写法 */ }
        <h2>{message}</h2>
      </div>
    )
  }
}

五、JSX中插入内容

插入Number/String/Array

当在JSX中插入Number/String/Array时,内容可以直接显示

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 100,
      message: 'hello',
      array: ['a', 'b', 'c']
    }
  }
  render() {
    const { count, message, array } = this.state
    return (
      <div>
        <h1>{count}</h1>
        <h1>{message}</h1>
        <h1>{array}</h1>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

数组中的内容会拼接到一起显示出来

插入Null/Undefined/Boolean

当插入Null/Undefined/Boolean时,内容将展示为空,如果想展示出来那么需要转成字符串

  • 通过String(xx)
  • 使用.toString()
  • 或在后面+""
class App extends React.Component {
  constructor() {
    super()
    this.state = {
      a: null,
      b: undefined,
      c: true
    }
  }
  render() {
    const { a, b, c } = this.state
    return (
      <div>
        <h1>{a + ""}</h1>
        <h1>{String(b)}</h1>
        <h1>{c.toString()}</h1>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

需要注意的是:

如果是nullundefined时,在转换成字符串时不要通过.toString()进行转换,会报错;对于Boolean来说,以上三种方式都可以

Object类型的数据不能被插入

当一个数据为Object类型时不能被插入,会报错

  • 一般我们会去展示其中的某一属性值或key
class App extends React.Component {
  constructor() {
    super()
    this.state = {
      friend: { name: "zhangsan" },
    }
  }
  render() {
    const { friend } = this.state
    return (
      <div>
        { /* <h1>{friend}</h1> 不能这样,会报错*/}
        <h1>{friend.name}</h1>
        <h1>{Object.keys(friend)[0]}</h1>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

插入表达式

在JSX中可以嵌入三元表达式

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      age: 7,
    }
  }
  render() {
    const { age } = this.state
    const ageText = age >= 18 ? '成年' : '未成年'
    return (
      <div>
        <h1>{ age >= 18 ? '成年' : '未成年'}</h1>
        <h1>{ ageText }</h1>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

也可以嵌入运算表达式

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count1: 10,
      count2: 20
    }
  }
  render() {
    const { count1, count2} = this.state
    return (
      <div>
        <h1>sum: {count1 + count2}</h1>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

也可以调用函数

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      movies: ["流浪地球", "星际穿越", "独行月球"]
    }
  }
  render() {
    const { movies } = this.state
    return (
      <div>
        <ul>{ movies.map(item => <li>{item}</li>)}</ul>
        <ul>{ this.getMovieEls() }</ul>
      </div>
    )
  }
  getMovieEls() {
    const liEls = this.state.movies.map(movie => <li>{movie}</li>)
    return liEls
  }
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

六、JSX中绑定属性

绑定title/href/src属性

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      title: '123',
      imgURL: "https://ts1.cn.mm.bing.net/th/id/R-C.95bc299c3f1f0e69b9eb1d0772b14a98?rik=W5QLhXiERW4nLQ&riu=http%3a%2f%2f20178405.s21i.faiusr.com%2f2%2fABUIABACGAAgoeLO-wUo4I3o2gEw8Qs4uAg.jpg&ehk=N7Bxe9nqM08w4evC2kK6yyC%2bxIWTjdd6HgXsQYPbMj0%3d&risl=&pid=ImgRaw&r=0",
      href: "https://www.baidu.com",
    }
  }
  render() {
    const { title, imgURL, href} = this.state
    return (
      <div>
        <h2 title={title}>我是h2元素</h2>
        <img src={imgURL} alt=""/>
        <a href={href}>百度一下</a>
      </div>
    )
  }
  getMovieEls() {
    const liEls = this.state.movies.map(movie => <li>{movie}</li>)
    return liEls
  }
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

绑定class属性

在绑定class时,使用className来进行绑定

方式一:使用字符串拼接

render() {
  const {isActive} = this.state
  const classStr = `abc cba ${isActive ? 'active': ''}`
  return (
    <div>
      <h2 className={classStr}>123</h2>
    </div>
  )

方式二:将class放入数组中,在通过join拼接

render() {
  const classList = ["abc", "cba"]
  if (isActive) classList.push("active")
  return (
    <div>
      <h2 className={classList.join(" ")}>123</h2>
    </div>
  )

绑定style属性

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      objStyle: {color: "red", fontSize: "30px"}
    }
  }

  render() {
    const { objStyle } = this.state
    return (
      <div>
        <h2 style={{color: "red", fontSize: "30px"}}>123</h2>
        <h2 style={objStyle}>123</h2>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)

需要注意的是,不要将style={{....}}看成vue中的模板语法,这里指的是{}中包裹style对象

七、事件绑定

React中的事件命名采用驼峰式,而不是纯小写

  • 需要通过{}来包裹传入的处理事件函数,该函数会在事件发生时被执行

this指向问题

但是需要注意的是,如果在{}中直接通过this.xx来传入函数时,该函数中的this指向undefined

class App extends React.Component {

  btnClick(){
    console.log("btnClick", this);
  }
  render() {
    return (
      <div>
        <button onClick={this.btnClick}>按钮</button>
      </div>
    )
  }
}

const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)

当进行点击按钮时会发现输出undefined,这是为什么呢?

这是因为<button ...>这段JSX代码本质上是:React.createElement('button', { onClick: this.btnClick })

当按钮发生点击的时候会拿到赋值给onClick的函数直接进行调用

相当于:

const click = config.onClick
click()

也就是说内部在调用传入的函数时是独立调用的,而class在转换代码时是默认开启严格模式的,所以this指向undefined

那么如何解决this问题呢?

方式一:可以对传入的函数绑定this

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

也可以在constructor中统一进行绑定(也是官方建议的方式)

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

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

  btnClick() {
    console.log("btnClick", this);
    this.setState({ counter: this.state.counter + 1 })
  }

  render() {

    return (
      <div>
        <button onClick={this.btn1Click}>按钮</button>
        <h2>当前计数: {this.state.counter}</h2>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)

方式二:将函数写为箭头函数形式

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

  btnClick = () => {
    this.setState({ counter: 1000 })
  }

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

        <h2>当前计数: {this.state.counter}</h2>
      </div>
    )
  }
}

由于箭头函数本身不绑定this,它会去其上层作用域去找this

这里它的上层作用域是该类的作用域,该类的作用域指向当前class即该组件App的实例,所以通过这种方式找到的this就是当前组件的实例

方式三:直接传入一个箭头函数

<button onClick={() => console.log("123")}>按钮</button>

既然直接可以传入一个箭头函数,所以也可以写成以下方式:

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

  btnClick() {
    console.log("btnClick", this);
    this.setState({ counter: 9999 })
  }

  render() {
    return (
      <div>
        <button onClick={() => this.btnClick()}>按钮</button>
        <h2>当前计数: {this.state.counter}</h2>
      </div>
    )
  }
}
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App/>)

采用箭头函数的方式,可以在箭头函数中调用实例方法并执行

通过this.xx的方式执行该方法时,this便会绑定为当前组件实例。推荐这种方式,因为方便传递参数

传递event和其他参数

有时候当事件发生时需要将参数传递过去

  • 在React中事件发生时同样也会默认传递event对象
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>计数器</title>
</head>
<body>
  
  <div id="root"></div>
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

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

      btnClick(event) {
        console.log("btnClick:", event)
      }

      render() {

        return (
          <div>
            <button onClick={this.btnClick.bind(this)}>按钮1</button>
            <button onClick={(event) => this.btnClick(event)}>按钮2</button>
          </div>
        )
      }
    }
    const root = ReactDOM.createRoot(document.querySelector("#root"))
    root.render(<App/>)
  </script>

</body>
</html>

需要注意的是,如果要通过箭头函数的方式来绑定事件处理函数时,要手动传入event:

<button onClick={(event) => this.btnClick(event)}>按钮2</button>

也可以传递额外参数:

class App extends React.Component {

  btnClick(event, name, age) {
    console.log("btnClick:", event, this)
    console.log("name, age:", name, age)
  }

  render() {
    const { message } = this.state

    return (
      <div>
        <button onClick={(event) => this.btnClick(event, "zs", 18)}>按钮4</button>
      </div>
    )
  }
}

案例

实现一个电影列表,点击某一项时,字体颜色变成红色

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .active {
      color: red;
    }
  </style>
</head>

<body>
  <div id="root"></div>
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          movies: ["星际穿越", "盗梦空间", "大话西游", "流浪地球"],
          currentIndex: 0 // 记录当前选中的项
        }
      }
      selectMovie(index) {
        this.setState({
          currentIndex: index
        })
      }
      render() {
        const { movies, currentIndex } = this.state
        return (
          <ul>
            {movies.map((item, index) => {
              return (
                <li
                  key={item}
                  className={currentIndex === index ? 'active' : ''}
                  onClick={() => this.selectMovie(index)}
                >
                  {item}
                </li>
              )
            })}
          </ul>
        )
      }
    }

    const root = ReactDOM.createRoot(document.querySelector('#root'))
    root.render(<App />)
  </script>
</body>

</html>

也可以将movies.map抽取出来:

render() {
  const { movies, currentIndex } = this.state

  const liEls = movies.map((item, index) => {
    return (
      <li
        className={ currentIndex === index ? 'active': '' }
        key={item}
        onClick={() => this.itemClick(index)}
        >
        {item}
      </li>
    )
  }) 

  return (
    <div>
      <ul>{liEls}</ul>
    </div>
  )
}

或者将map中的方法抽取出来:

render() {
  const { movies, currentIndex } = this.state

  const itemHandle = (item, index) => {
    return (
      <li
        className={ currentIndex === index ? 'active': '' }
        key={item}
        onClick={() => this.itemClick(index)}
        >
        {item}
      </li>
    )
  }

  return (
    <div>
      <ul>{movies.map(itemHandle)}</ul>
    </div>
  )
}

八、JSX中的条件渲染

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

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

方式一:根据不同条件为变量赋值不同的内容

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      flag: 1
    }
  }
  render() {
    let showElement;
    if (this.state.flag === 1) {
      showElement = <h1>Hello World!</h1>
    } else {
      showElement = <h1>Hello React!</h1>
    }
    return showElement;
  }
}

方式二:使用三元表达式

render() {
  return (
    <h1>{this.state.flag === 1 ? 'Hello World!' : 'Hello React!'}</h1>
  )
}

方式三:使用逻辑与运算符'&&'

场景:当该值可能为null或undefined时,我们可以采用&&来决定是否展示其中的内容

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      person: undefined
    }
  }
  render() {
    const { person } = this.state
    return (
      <div>
        name: {person && <h1>{person.name}</h1>}
      </div>
    )
  }
}

案例

案例:通过按钮点击来切换内容是否展示

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
  .active {
    color: red;
  }
</style>
</head>

<body>
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<script type="text/babel">
  class App extends React.Component {
    constructor() {
      super()
      this.state = {
        isShow: true
      }
    }
    changeShow() {
      this.setState({ isShow: !this.state.isShow })
    }
    render() {
      const { isShow } = this.state
      return (
        <div>
          <button onClick={() => this.changeShow()}>切换</button>
          {isShow && <h1>hi</h1>}
        </div>
      )
    }
  }

  const root = ReactDOM.createRoot(document.querySelector('#root'))
  root.render(<App />)
</script>
</body>

</html>

也可以为通过添加style属性来控制元素是否展示从而达到vue中v-show的效果:

render() {
  const { isShow } = this.state
  return (
    <div>
      <button onClick={() => this.changeShow()}>切换</button>
      <h1 style={{ display: isShow ? 'block' : 'none' }}>hi</h1>
    </div>
  )
}

九、JSX中的列表渲染

在React中,展示列表最多的方式就是使用数组的map高阶函数,但是在很多时候我们在展示一个数组中的数据之前,需要先对它进行一些处理:

  • 比如过滤掉一些内容:filter函数
  • 比如截取数组中的一部分内容:slice函数

比如,获取大于90分的同学,并展示前两名:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
  .item {
    border: 1px solid green;
    margin-bottom: 10px;
  }
</style>
</head>

<body>
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<script type="text/babel">
  class App extends React.Component {
    constructor() {
      super()
      this.state = {
        students: [
          { id: 111, name: "zs", score: 99 },
          { id: 112, name: "ls", score: 100 },
          { id: 113, name: "ww", score: 80 },
          { id: 114, name: "zl", score: 90 },
        ]
      }
    }
    render() {
      const { students } = this.state
      return (
        <div>
          {
            students.filter(stu => stu.score > 90).slice(0, 2).map(stu => {
              return (
                <div className="item" key={stu.id}>
                  <h2>学号:{stu.id}</h2>
                  <h2>姓名:{stu.name}</h2>
                  <h2>分数:{stu.score}</h2>
                </div>
              )
            })
          }
        </div>
      )
    }
  }

  const root = ReactDOM.createRoot(document.querySelector('#root'))
  root.render(<App />)
</script>
</body>

</html>

需要注意的是,在遍历元素时要为元素增加key属性,否则会报如下警告:

这个警告是告诉我们需要在列表展示的JSX中添加一个key

  • key主要的作用是为了提高diff算法时的效率

十、JSX的本质

JSX最终会被转换成什么

实际上,JSX仅仅只是 React.createElement(type, config, children) 函数的语法糖

Babel在每遇到一个标签时都会进行转换:

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

createElement需要传递三个参数:

参数一:type ,表示当前ReactElement的类型

如果是标签元素,那么就使用字符串表示 如“div”

如果是组件元素,那么就直接使用组件的名称

参数二:config

所有jsx中的属性都在config中以对象的形式存储属性和值

  • 比如传入className作为元素的class

参数三:children

存放在标签中的内容,以children数组的方式进行存储

可以在Babel官网查看JSX代码的转换过程:

可见每个元素都会转换为React.createElement

  • /#PURE/这个注释表示该函数调用是一个纯函数调用,有利于摇树优化

如果将JSX写法换成React.createElement的形式便不需要Babel进行转换了,此时也不需要在script标签上添加type="text/babel"标识了:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>计数器</title>
</head>

<body>

  <div id="root"></div>

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

  <script>
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          message: "Hello World"
        }
      }

      render() {
        const { message } = this.state

        const element = React.createElement("div", null,
        /*#__PURE__*/React.createElement("div", {
          className: "header"
        }, "Header"),
        /*#__PURE__*/React.createElement("div", {
          className: "content"
        }, "Content",
          /*#__PURE__*/React.createElement("ul", null,
          /*#__PURE__*/React.createElement("li", null, "1"),
          /*#__PURE__*/React.createElement("li", null, "2"),
          /*#__PURE__*/React.createElement("li", null, "3")
        )));

        console.log(element)

        return element
      }
    }

    const root = ReactDOM.createRoot(document.querySelector("#root"))
    root.render(React.createElement(App, null))
  </script>

</body>

</html>

打印创建出的最终的element:

通过React.createElement函数会创建出一个ReactElement对象,这些嵌套的ReactElement对象会组成一个JavaScript对象树

  • 这个JavaScript对象树就是虚拟DOM
  • 这个虚拟DOM最终会被渲染为真实DOM展示出来

JSX -> 虚拟DOM -> 真实DOM

React的解析过程相对于Vue来说更加的直观:

在Vue中有专门自己的解析库:

  • 将template解析为render函数,而且解析过程比较麻烦,因为还要解析各种指令,如v-if v-for v-model等等
  • 在render函数中调用了大量的h函数
  • h函数调用完毕后才变成一个个的JS对象
  • 这些对象之间产生联系最终变为虚拟DOM,最后再变成真实DOM
  • 当然Vue中也可以写JSX,当写成JSX后也是交由Babel去解析的,我们平常所写的template则是由Vue自己的解析库来解析的

而React中对JSX的解析是交给Babel的,一遇到标签就会解析为React.createElement,更加简单明了

为什么要使用虚拟DOM

  1. 在使用了虚拟DOM后,一旦数据更新了之后就不用将整段结构都重新渲染(方便快速进行diff)

在数据更新后,会重新执行render方法生成新的虚拟DOM,此时就会拿新的虚拟DOM与旧的虚拟DOM进行diff,diff时会发现更新了哪部分内容,只会去重新渲染更新部分的内容

  1. 由于虚拟DOM本质是JS对象,那么既可以通过React渲染成为Web端的节点,又可以渲染成为手机端的控件,从而做到跨平台
  2. 虚拟DOM帮助我们从命令式编程转到了声明式编程的模式

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

在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象,我们可以通过ReactDOM.render让 虚拟DOM 和 真实DOM同步起来。

这种编程的方式赋予了React声明式的API:

  • 只需要告诉React希望让UI是什么状态
  • React来确保DOM和这些状态是匹配的
  • 你不需要直接进行DOM操作,就可以从手动更改DOM、属性操作、事件处理中解放出来