ReactJS语法-简介、虚拟DOM、JSX
简介
- React的官方文档:https://zh-hans.react.dev/、https://zh-hans.legacy.reactjs.org/
- 开发React必须依赖三个库
- react:包含react所必须的核心代码
- react-dom:react渲染在不同平台所需要的核心代码
- babel:将jsx代码转换成React代码的工具
- babel是什么呢?
- Babel ,又名 Babel.js
- 是目前前端使用非常广泛的编译器、转移器.比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它,那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。
- React和Babel的关系:
- 默认情况下开发React其实可以不使用babel,但是前提是我们自己使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差。
- 那么我们就可以直接编写Jsx(JavaScript XML)的语法,并且让babel帮助我们转换成React.createElement
脚手架
- 编程中提到的脚手架(Scaffold),其实是一种工具,帮我们可以快速生成项目的工程化结构;
- 每个项目作出完成的效果不同,但是它们的基本工程化结构是相似的;
- 既然相似,就没有必要每次都从零开始搭建,完全可以使用一些工具,帮助我们生产基本的工程化模板
- 不同的项目,在这个模板的基础之上进行项目开发或者进行一些配置的简单修改即可;
- 这样也可以间接保证项目的基本结构一致性,方便后期的维护
前端脚手架
- 对于现在比较流行的三大框架都有属于自己的脚手架:
- Vue的脚手架:@vue/cli
- Angular的脚手架:@angular/cli
- React的脚手架:create-react-app
- 它们的作用都是帮助我们生成一个通用的目录结构,并且已经将我们所需的工程环境配置好
- 使用这些脚手架需要依赖什么呢?
- 目前这些脚手架都是使用node编写的,并且都是基于webpack的;
- 所以我们必须在自己的电脑上安装node环境
React的脚手架:create-react-app使用
- 先安装node环境
- 安装脚手架:
npm install create-react-app -g - 初始化项目:
create-react-app 项目名称,项目名称必须小写 - 运行项目:进入项目执行命令:
npm run start
虚拟DOM(Virtual Document Object Model)
- DOM的本质:就是用JS表示的UI元素,即用浏览器中的JS提供API获取html中的某个元素。
- 虚拟DOM:并不是由浏览器提供的,而是我们程序员手动模拟实现的,类似于浏览器中的DOM,但是有着本质的区别;
- 虚拟DOM的使用目的:为了实现DOM节点的高效更新。
- 场景:有个列表页面,有n行n列数据,点击每列表头可以对整个表进行升降排序。
- 普通实现方案:当点击x列时,首先将数据源按照该列进行排序,然后使用for循环重新生成每列元素标签,填充最新数据。
- 缺点:如果某次排序仅仅变动了其中的2行数据,那么每次点击整个表格部分DOM要重新创建,非常消耗浏览器性能。
- 理想方法:每次点击排序只需要更新这两条需要调整的元素即可。
- 解决方案:1. 用js对象来描述整个表格的DOM—旧DOM。2.点击排序后的数据,用JS创建新的对象DOM—新DOM 3. 将旧、新进行对比,校验出不同的地方。 4. 只更新不同数据的元素。
- 虚拟DOM的本质:使用JS对象来模拟DOM树
- 用虚拟DOM实现
<p title="123">真好<span>456</span></p> -
实现虚拟DOM如下:
var p = { tagName:"p", arrts:{ title: "123", id: "" }, children:{ "真好", { tagName:"span", children:{ "456" } } } }
- 用虚拟DOM实现
- React框架已经实现了虚拟DOM,无需自己麻烦创建。
- 新旧DOM如何进行比对呢?—–Diff算法

Diff算法
- tree diff:新旧DOM树,逐层对比的方式,就叫做 tree diff,每当我们从前到后,把所有层的节点对比完后,必然能够找到那些 需要被更新的元素;
- component diff:在对比每一层的时候,组件之间的对比,叫做 component diff;当对比组件的时候,如果两个组件的类型相同,则暂时认为这个组件不需要被更新,如果组件的类型不同,则立即将旧组件移除,新建一个组件,替换到被移除的位置;
- element diff:在组件中,每个元素之间也要进行对比,那么,元素级别的对比,叫做 element diff;
- key:key这个属性,可以把 页面上的 DOM节点 和 虚拟DOM中的对象,做一层关联关系;
代码举例
-
main.js文件(注意,该文件是webpack下默认的打包入口文件,其他js文件导入该文件即可)
// 1. 在 React 需要安装 两个包 react react-dom // 1.1 react 这个包,是专门用来创建React组件、组件生命周期等这些东西的; // 1.2 react-dom 里面主要封装了和 DOM 操作相关的包,比如,要把 组件渲染到页面上 import React from 'react' import ReactDOM from 'react-dom' // 2. 创建 DOM 元素 // React.createElement() 方法,用于创建 虚拟DOM 对象,它接收 3个及以上的参数 // 参数1: 是个字符串类型的参数,表示要创建的元素类型 // 参数2: 是一个属性对象,表示 创建的这个元素上,有哪些属性 // 参数3: 从第三个参数的位置开始,后面可以放好多的虚拟DOM对象,这些参数,表示当前元素的子节点 // <div title="this is a div" id="mydiv">这是一个div</div> var myH1 = React.createElement('h1', null, '这是一个大大的H1') var myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '这是一个div', myH1) //3. 使用ReactDOM把元素渲染到页面指定的容器中上 / ReactDOM.render('要渲染的虚拟DOM元素', '要渲染到页面上的哪个位置中') // 注意: ReactDOM.render() 方法的第二个参数,不接受 "#app" 这样的字符串,而是需要传递一个 原生的 DOM 对象 ReactDOM.render(myDiv,document.getElementById('app')) -
index.html(注意,该文件是webpack下默认的页面加载入口,且会自动导入main.js)
<body> <!-- 这是一个容器,将来 使用 react 渲染的虚拟DOM,都会放到这个容器中 --> <div id="app"></div> </body>
JSX
认识JSX语法
- 如果创建很多虚拟DOM使用React.createElement,那么会很麻烦,是否可以向直接写HTML标签的样式来快速创建虚拟DOM呢?—JSX
- React 官方,就提出了一套 JSX 语法规范,能够让我们在 JS 文件中,书写类似于 HTML 那样的代码,快速定义虚拟DOM结构;
- JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法。
- 它用于描述我们的UI界面,并且其完全可以和JavaScript融合在一起使用
- 本质:哪怕写了JSX这样的标签,也并不是直接渲染到页面上,而是先转换成React.createElement 这样的JS代码,再渲染到页面中;(JSX是一个对程序员友好的语法糖)
JSX的书写规范
- 在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹(下面案例div包裹了h1,p,label);
- 注释必须放到 {} 内部
- 如果要在 JSX 语法内部,书写 JS 代码了,那么,所有的JS代码,必须写到 {} 内部;
- 当编译引擎,在编译JSX代码的时候,如果遇到了<那么就把它当作 HTML代码去编译,如果遇到了 {} 就把 花括号内部的代码当作 普通JS代码去编译;
- JSX嵌入变量作为子元素
- 当变量是Number、String、Array类型时,可以直接显示
- 当变量是null、undefined、Boolean类型时,内容为空;
- 如果希望可以显示null、undefined、Boolean,那么需要转成字符串,
- Object对象类型不能作为子元素(not valid as a React child)
- JSX嵌入表达式
- 运算表达式
- 三元运算符
- 执行一个函数
- 在JSX中,如果要为元素添加class属性了,那么,必须写成className,因为 class在ES6中是一个关键字;和class类似,label标签的 for 属性需要替换为 htmlFor.
-
如果要直接使用 JSX 语法,需要先安装相关的 语法转换工具,
运行 cnpm i babel-preset-react -D -
举例
//js代码 var mytitle = '这是使用变量定义的 tilte 值' //创建10个p标签 var arr = [] for (var i = 0; i < 10; i++) { //虚拟DOM var p = <p className="myp" key={i}>但是,你知道它的本质吗???</p> arr.push(p) } var a = true; var b = { name = "zh" } var c = "123"; var isActive = true; //左上角单引号 const className=`abc cba ${isActive ?'active':''}` //1. JSX创建虚拟DOM var mydiv = <div> 这是使用 jsx 语法创建的div元素 {//mytitle 是js语法,因此要用{} } <h1 title={mytitle + 'aaaaa'}>哈哈哈,JSX真好用啊</h1> {//不会显示 } <h1 title={a}>哈哈哈,JSX真好用啊</h1> {//会报错,对象类型不能作为子元素 } <h1 title={b}>哈哈哈,JSX真好用啊</h1> {//创建10个p标签} {arr} {//运算表达式} <h2>{firstName + "" + lastName}</h2> {//三元运算符} <h2>{age >=18 ?"成年人":"未成年人"}</h2> {//执行一个函数} <ul>{this.getMovieEls()}</u1> {//必须写成className} <p className="myp">但是,你知道它的本质吗???</p> <label htmlFor=""></label> {//属性绑定变量} <label title={c}></label> {//属性动态绑定} <label className={className}></label> {//绑定style属性} <h2 style=>呵呵呵呵</h2> </div> getMovieEls(){ const liEls =this.state.movies.map(movie =><li>{movie}</li>) return liEls ) //2. 渲染 ReactDOM.render(mydiv,document.getElementById('app'))
JSX的条件渲染
- 某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容
- 在React中,所有的条件判断都和普通的JavaScript代码一致
- 常见的条件渲染的方式
- 条件判断语句:适合逻辑较多的情况
- 三元运算符:适合逻辑比较简单
- 与运算符&&: 适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染,
- v-show的效果:主要是控制display的属性是否为none
-
举例:
constructor(){ super() this.state ={ isReady: false, friend:{ name: "kobe” desc:"凌晨四点钟的太阳!" } } } render(){ const {isReady, friend }= this.state //1.条件判断方式-:使用话进行条件判断 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 && kdiv>{friend.name + "" + friend.desc}</div> {*4.v-show的效果 */} <h2 style=>哈哈哈哈</h2> </div> )
JSX的列表渲染
-
在React中,展示列表最多的方式就是使用数组的map高阶函数,
render(){ const { students }= this.state return( <div> <h2>学生列表数据</h2> <div className="list"> { students.map(item =>{ return ( <div className="item" key={item.id}) <h2>学号:{item.id}</h2> <h3>姓名:{item.name}</h3> <h1>分数:{item.score}</h1> </div> ) }) } </div> </div> ) }