1.介绍
概念:JSX是一种JavaScript语法扩展(eXtension),也有很多地方称之为Javascript XML(HTML)的缩写,表示在JS代码中书写HTML结构
作用:用于描述UI界面,并且可以和JavaScript融合在一起使用
优势:
- 采用类似于HTML的语法,降低学习成本,会HTML就会JSX
- 充分利用JS自身的可编程能力创建HTML结构
为什么选择React选择JSX:
-
React认为渲染逻辑与其他UI逻辑存在内在耦合
- 比如UI需要绑定事件(button,a原生等)
- 比如UI需要展示数据状态
- 比如在某些状态发生变化是,又需要改变UI
-
它们之间是密不可分的,所以React没有将标记分离到不同的文件中,而是将他们组合,这个地方就是组件
注意事项:
- JSX必须有一个根节点,如果没有根节点,可以使用
<></>(Frament节点)替代 - 所有标签必须形成闭合,成对闭合或者自闭合都可以
- JSX中的语法更加贴近JS语法,属性名采用驼峰命名法
class -> classNamefor -> htmlFor - 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>
)
}
}
-
JSX中注释写法:{ /* JSX的注释写法 */ }
-
当变量是 Number/String/Array 类型时,可以直接显示
-
当变量是 undefined/null/Boolean 类型时,内容为空
- 如果希望展示 undefined/null/Boolean,需要转换成字符串
- 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式
-
Object对象类型不能作为子元素(not valid a React child)
-
使用JS表达式
- 运算表达式 { 10 + 20 }
- 三元表达式 { age >= 18 ? '成年人' : '未成年人'}
- 执行一个函数
- 特别注意: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中监听一个事件,有两种方式:
- 获取原生DOM,添加监听事件
- 在HTML中,直接绑定onclick
在React中监听事件,需要注意以下两点:
- React事件的命名采用小驼峰(camelCase),而不是纯小写
- 需要通过{}传入一个事件处理函数,这个函数会在事件发生时执行
在事件执行后,我们可能需要获取当前类的对象中相关的属性,这个时候需要用到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属性
- key 在HTML结构中是看不到的,是React内部用来进行性能优化的
- key 在当前列表中要唯一的字符串或者数值(String/Number)
- 如果列表中有像 id 这种唯一值,就用 id 作为 key 来使用
- 如果列表中没有像 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为什么要创建它?
- 原因是React利用ReactElement对象组成了一个JavaScript的对象树
- 这个JavaScript的对象树就是虚拟DOM(Virtual DOM)
怎么查看ReactElement的树结构?
- 可以将之前的JSX返回结果进行打印:
console.log(new App().render())
10.声明式编程
虚拟DOM帮助我们从命令式编程转到了声明式编程的模式
React官方的说法: Virtual DOM 是一种编程理念
- 在这个理念中,UI是以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象
- 通过ReactDOM.render 让 虚拟DOM 和 真实DOM 同步起来,这个过程叫做协调 这种编程方式赋予了React声明式的API
- 你只需要告诉React希望UI是什么状态
- React确保DOM和这些状态时匹配的
- 你不需要直接进行DOM操作,就可以从手动更改DOM、属性操作、事件处理中解放出来