认识JSX
JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法
它用于描述我们的UI界面,JSX会被解析为ReactRenderObject,所以JSX本质就是JS对象, 所以JSX可以和JavaScript融合在一起使用
JSX其实是将HTML嵌入到JavaScript中的一种结构语法
// JSX本质就是对象,所以其可以作为变量来进行使用
const msg = <h2>Hello World</h2>
ReactDOM.render(msg, document.getElementById('app'))
React为什么使用JSX
React认为渲染逻辑本质上与其他UI逻辑存在内在耦合
- 比如UI需要绑定事件(button、a元素等等)
- 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI
他们之间是密不可分,所以React没有讲标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件 (Component)
书写规范
- JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div元素或Fragment组件
- 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),但这仅仅是为了方便我们阅读和JSX代码换行,不是必须的
- JSX中的标签可以是单标签,也可以是双标签,但如果是单标签,必须使用/来进行闭合,也就是要使用严格模式来闭合标签
注释
// JSX的{}中可以写JS语法,其中也就可以写JS注释
{/* 这里是JSX的块级注释内容 */}
{
// 这里是JS的单行注释内容
// 注意: //和{之间必须换行,否则无法正常解析}
}
嵌入值
JSX使用大括号语法来嵌入值,在大括号中可以书写的是任意合法的JS变量或合法的JS表达式
变量
- 当变量是Number、String、Array类型时,可以直接显示
- 当变量是null、undefined、Boolean类型时,内容为空
- 如果希望可以显示null、undefined、Boolean,那么需要转成字符串
- 例如使用toString,String,拼接空字符串
- 因为undefined和null是无法调用函数的,所以推荐使用拼接空字符串的方式来进行转换
- 对于Boolean类型的值,无论值是true还是false,其都不会在界面上进行显示
- 对象类型不能作为子元素(not valid as a React child)
- JSX会转换为React.createElement,而其第三个参数children即使当前元素所对应的子元素
- 而children数组中是不可以有对象的
- 也就是说对象不可以写在大括号语法中
表达式
class App extends React.Component {
constructor() {
this.state = {
firstName: 'klaus',
lastName: 'Wang',
isLogin: false
}
}
render() {
// 对象的解构,可以使用常量来进行接收
// 因为每一次界面刷新的时候,其都会重新执行一遍render函数
// 也就是会生成一个新的函数作用域,因此解构出的变量可以使用常量来进行接收
const { firstName, lastName, isLogin } = this.state
return (
// 虽然JSX可以使用Fragment进行包裹(也就是使用<></>来进行包裹)
// 但是Fragment只能在脚手架中使用,在CDN引入的时候无法使用
<div>
{/* JSX中可以书写合法的JS表达式 */}
<div>{ firstName + ' ' + lastName }</div>
{/*
JSX中可以编写三目运算符
在实际开发中,LoginOut和LoginIn可能对应着是变量
为了避免变量为null或undefined的时候,在界面显示出现bug
随意null和undefined在JSX中一般不会显示在界面上
*/}
<div>{ isLogin ? 'LoginOut' : 'LoginIn' }</div>
{/*
逻辑运算符
JSX中,会出现许多通过逻辑运算符来决定渲染内容的情况
所以JSX中如果需要显示的是boolean类型的时候,无论结果为true还是false
其值都不会显示,以避免出现显示bug
例如: isLogin && 'welcome'中 如果isLogin的值为false
那么表达式的结果为false,此时在界面上显示false明显是不合理的
*/}
<div>{ isLogin && 'welcome' }</div>
{/*
函数调用
JSX中可以进行函数的调用
也可以使用一些方法,例如map, filter, some等
*/}
<div>{ this.getFullName() }</div>
</div>
)
}
getFullName() {
return this.state.firstName + ' ' + this.state.lastName
}
}
ReactDOM.render(<App />, document.getElementById('app'))
绑定属性
普通属性
{/* 可以使用大括号语法来绑定普通属性 */}
<a href={ this.state.link }>google</a>
class
{/*
class是js的关键字,而JSX本质就是all in js
所以为了避免HTML书写和JS关键字冲突
JSX为对应的HTML属性起了别名
class -> className
for -> htmlFor
*/}
<h2 className={`titile ${this.state.isActive && 'active'}`}>Lorem</h2>
style
{/*
第一个大括号表示内部使用JS语法
第二个大括号表示里面的值是一个对象
style中的属性值 如果是字符串需要加上引号
因为是在对象中,如果不加引号,会被解析为对象
*/}
<h2 style={{
color: '#fff',
backgroundColor: 'skyblue',
'font-size': '20px'
}}>Lorem</h2>
事件绑定
class App extends React.Component {
constructor() {
super()
this.state = {
count: 100
}
}
// render方法在编译后,是通过App组件的实例对象来进行调用的
// 所以在render函数中,this的指向是正确的
render() {
return (
<div>
<button onClick={ this.getCount }>click me</button>
</div>
)
}
getCount() {
// 在React中,对HTML生事件进行了二次封装
// 而HTML原生事件是所有组件都可以调用的
// 所以React在封装HTML原生事件的时候,并不知道具体的调用者
// 所以React在执行传入的原生事件回调的时候,使用了`call(undefined)`
// 因此在React中 默认情况下原生HTML事件回调中的this是undefined
// 我们在使用的时候,需要对this的指向进行修正
console.log(this) // => undefined
}
}
解决方法1 -- 使用bind方法
{/*
使用bind进行绑定的时候,可以返回一个拥有正确this指向的新函数
但是如果在jsx中多次调用同样函数的时候,需要多次重复使用bind方法来修正this指向
*/}
<button onClick={ this.getCount.bind(this) }>click me</button>
<button onClick={ this.getCount.bind(this) }>click me</button>
class App extends React.Component {
constructor() {
super()
this.state = {
count: 100
}
// 我们可以在构造函数中 一次性对某个方法进行this的修正
// 但是这就意味着所有的函数都需要在构造器中使用bind方法修正this指向
// 而实际开发中,组件内部的函数是很多的,如果都在构造器中修正this指向
// 必然会导致构造函数过于繁琐,不利于维护
this.getCount = this.getCount.bind(this)
}
render() {
return (
<div>
<button onClick={ this.getCount }>click me</button>
<button onClick={ this.getCount }>click me</button>
</div>
)
}
getCount() {
console.log(this.state.count)
}
}
解决方法二 -- 使用class field
class App extends React.Component {
constructor() {
super()
this.state = {
count: 100
}
}
render() {
return (
<div>
{/* 绑定事件的时候,传入的其实是对应事件的引用地址 */}
<button onClick={ this.getCount }>click me</button>
</div>
)
}
// 在定义函数的时候,使用class fields定义类的成员变量
// 并且在定义函数的时候,使用箭头函数
// 而类中的方法,本质上都是通过类的实例去进行调用的
// 所以类的成员变量的上层作用域是App类
// 因此在这里, 我们可以获取正确的this指向
getCount = () => {
console.log(this.state.count)
}
}
解决方法三 -- 使用箭头函数 --- 推荐
class App extends React.Component {
constructor() {
super()
this.state = {
count: 100
}
}
render() {
return (
<div>
{/*
在调用函数的时候在外层包裹一层箭头函数
1. 如果在事件调用的时候传入箭头函数,其上层作用域是render函数,所以拥有正确的this指向
2. 在箭头函数的执行体中,是去调用我们实际需要执行的函数,此时不是引用地址,而是具体的函数调用
所以使用这种方法进行事件的绑定 便于我们进行函数参数的传递
*/}
<button onClick={ () => { this.getCount() }}>click me</button>
</div>
)
}
getCount() {
console.log(this.state.count)
}
}
参数传递
class App extends React.Component {
constructor() {
super()
this.state = {
count: 100
}
}
render() {
return (
<div>
<button onClick={ this.handleClick }>click me</button>
{/*
使用箭头函数修正this后,默认的事件对象是传递给外层的箭头函数的
所以在箭头函数体中进行函数实际调用的时候,
事件对象作为第几个参数传入还是不进行传入就可以由我们自己进行主动控制
*/}
<button onClick={ e => { this.calcCount(2, e) }}>click me</button>
</div>
)
}
// 默认情况下,JSX在进行事件绑定的时候,会默认将事件参数传入
// ps: 这个参数并不是原生事件对象,而是React基于原生事件对象二次封装得到的合成对象
// 原生的事件对象位于 e.nativeEvent
handleClick(e) {
console.log(e)
}
calcCount(step, e) {
console.log(this.state.count * step)
console.log(e)
}
}
条件渲染
某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容
class App extends React.Component {
constructor() {
super()
this.state = {
isLogin: true
}
}
// 和渲染UI相关的函数,写在render函数的上边
renderText() {
// 使用if进行条件判断,适合于逻辑比较复杂的情况
if (this.state.isLogin) {
return '欢迎回来'
} else {
return '请先登录'
}
}
render() {
return (
<div>
<h1>{ this.renderText() }</h1>
{/* 使用三目运算符来进行条件分支的判断 */}
<button onClick={ () => this.changeLoginStatus() }>{ this.state.isLogin ? '退出' : '登录' }</button>
<hr />
{/* 通过逻辑运算符来实现条件分支的判断 */}
{ this.state.isLogin && <p>您已经成功登录</p> }
</div>
)
}
// 和渲染UI无关,但是和业务逻辑有关的函数,写在render函数的后边
changeLoginStatus() {
this.setState({
isLogin: !this.state.isLogin
})
}
}
模拟v-show
class App extends React.Component {
constructor() {
super()
this.state = {
isLogin: true
}
}
render() {
return (
<div>
<button onClick={ () => this.changeLoginStatus() }>change login status</button>
<h2 style={{ display: this.state.isLogin ? 'block' : 'none' }} >登录成功</h2>
</div>
)
}
changeLoginStatus() {
this.setState({
isLogin: !this.state.isLogin
})
}
}
列表渲染
在React中,我们可以通过map,filter,slice等一系列的JavaScript函数来帮助我们实现列表渲染
class App extends React.Component {
constructor() {
super()
this.state = {
nums: [10, 23, 35, 122, 78, 4331, 23, 62]
}
}
render() {
return (
// 以列表形式展示出nums中所有的偶数数据
<ul>
{
this.state.nums.filter(num => num % 2 === 0).map(num => <li>{num}</li>)
}
</ul>
)
}
}