JSX核心语法

基于React渲染电影列表

方案一:基于for of循环实现

class App extends React.Component {
	constructor(){
		super();
		this.state = {
			title:'电影列表1',
			movies:['功夫','少年黄飞鸿','国产凌凌漆','大决战之辽沈战役']
		}
	}
	render(){
		const lis = [];
		for(let movie of this.state.movies){
			lis.push(<li>{movie}</li>)
		}
		return (
			<div>
				<h2>{this.state.title}</h2>
				<ul>
					{lis}
				</ul>
			</div>
		)
	}
}
ReactDOM.render(<App/>,document.getElementById('app'));

方案二:基于map方法实现

class App extends React.Component {
	constructor(){
		super();
		this.state = {
			title:'电影列表2',
			movies:['功夫','少年黄飞鸿','国产凌凌漆','大决战之辽沈战役']
		}
	}
	render(){
		return (
			<div>
				<h2>{this.state.title}</h2>
				<ul>
					{
						this.state.movies.map((item)=>{
							return <li>{item}</li>
						})
					}
				</ul>
			</div>
		)
	}
}
ReactDOM.render(<App/>,document.getElementById('app'));

Vue是模板语法,如果相似的地方要复用就要抽离成为一个个的组件
React比较灵活,相似的地方只需要抽离成为一个变量即可

基于React渲染计数器案例

问题1:为什么事件绑定的事件处理函数其内部默认为undefiend?该如何解决?

因为this.addClick这种事件绑定的函数,是先传递给react内部处理的。

react内部监听到按钮被点击,它会在内部将该事件处理函数执行,所以说事件触发后的函数不是我们在执行,而是react内部在调用的,但是在调用的时候它并不清楚要绑定一个什么this,react内部默认是通过fn.apply(undefined,args),所以react会默认绑定一个undefined。

要解决这个this的问题,最简单的就是采用箭头函数绑定,除此之外也可以使用bind在事件处理函数执行之前就给其本身绑定一个this,这个this也就是基于继承React.Component类之后生成的实例对象。

问题2:为什么修改数据可以通过this.setState()?
因为setState此方法是从父类React.Component中继承得来的

class App extends React.Component{
	constructor(){
		super();
		this.state = {
			counter:0,
		}
	}
	
	render(){
		return (
			<div>
				<h2>当前计数:{this.state.counter}</h2>
				<button className="add" onClick={this.addClick.bind(this)}>点击+1</button>
				<br/>
				<button className="sub" onClick={this.subClick.bind(this)}>点击 -1</button>
			</div>
		)
	}
	
	addClick(){
		this.setState({
			counter:++this.state.counter
		})
	};
	
	subClick(){
		this.setState({
			counter:--this.state.counter
		})
	};
}
ReactDOM.render(<App/>,document.getElementById('app'));

JSX核心语法(一)

1.使用jsx语法的两个前提(在HTML页面中使用的时候)

  1. 首先必须得引入babel.js这个依赖
  2. script标签中type必须得声明type="text/babel"

2.jsx语法是什么?

JSX是一种JavsScript的语法拓展extension,也被称之为javascript XML,因为看起来和XML类似
JSX的作用就是在React中描述页面的UI界面,并且在JSX中可以注入javascript代码一起使用
JSX不同于Vue中模板mustache语法,不需要学习一系列的模板语法的指令如v-for,v-if,v-if等
最通俗的是将一段JSX代码看做一个js中的值,开启了{}之后就等于开启了js上下文。
JSX是嵌套到js中的一种结构语法

3.为什么React要选择JSX语法

React设计哲学:All in js 所有东西都可以写在js里面
UI布局通过ReactDOM.render(ele,container)写在script代码块中完成
Style样式通过Style-Component库也可以将css写在script标签中完成

React人为渲染逻辑本质上与其他UI逻辑存在内在耦合性
比如UI页面中元素需要绑定事件
比如UI界面中需要展示数据,当数据发生改变的时候,又需要改变界面

所以React认为UI界面和JS逻辑是互不可分的,它并没有像Vue一样做逻辑样式模板分离,而是将它们组合到了一起,所以就形成了组件,这种组件更加灵活易复用。

4.jsx语法书写注意项

  • JSX的最顶层只能有一个根元素,这一点和Vue中的template一样
  • 一般情况下会用一个小括号对整个JSX代码做一个包裹,易于查看,结构清晰
  • SX中如果要写单标签,但是必须在末尾加上一个/,如果不加就会报错

5.jsx语法注释

{/* 这是一段jsx语法的注释 */}
可换行

6.jsx嵌入变量

  1. 当变量是Number、String、Array类型的时候,可以直接显示
  2. 当变量是null、undefined、Boolean类型的时候,内容为空

为什么React要这样设计?
因为很多时候再进行渲染的时候需要进行判断,三元表达式中原本我的意思是null代表不执行任何操作,但是如果null要显示的话那么页面会多出一个字符串的null
还有一种情况是当为布尔值的时候,我的意思是如果false为true才显示你好啊,否则什么都不显示,但是这里就有可能会造成在页面上多出一个字符串的布尔值

<div>{flag?'你好啊':null}</div>
<div>{flag &&'你好啊'}</div>

如果希望可以将其显示,那么该如何操作?
如果希望可以显示null、undefined、Boolean类型的值,那么需要将其作为字符串展示
null和undefined不可以调用toString()方法,所以只可以使用加''拼接字符串或者String()强制转型函数
Boolean布尔值三个方法都可以使用,比如toString()、String()或者空串进行拼接

  1. 当变量是一个对象Object类型的时候,不能作为JSX的子类进行渲染
    不可以将一个对象整体放入到JSX语法中,但是可以获取到对象上的属性后放入
    Uncaught Error: Objects are not valid as a React child (found: object with keys {x, y}). If you meant to render a collection of children, use an array instead.

7.jsx嵌入表达式

所有js中的表达式都可以写在这里

{/* 1.数学运算符表达式 */}
<h2>{pName + '---' + pAge}</h2>

{/* 2.三元表达式 */}
<h2>{flag?'是的很好':'请优化一下'}</h2>

{/* 3.进行函数调用 */}
<h2>{this.getName()}</h2>

问题1:这里的this.getName()为什么不需要手动用bind绑定this了?
很明显的告诉react,这里是实例在调用getName方法,将返回值渲染到页面
这里代表的是将函数调用的结果输出到UI中,所以this可以确定就是实例

事件处理函数并不会手动执行,而是等到事件触发才执行
在原生js的DOM中,一般是那个dom元素绑定事件,其this就是dom元素
但是在react中,this默认为undefiend,需要手动绑定,因为不知道谁来调用

JSX核心语法(二)

1.JSX绑定属性[内置属性 class属性 style内联属性]

给HTML元素绑定内置属性
常见的绑定的html元素的属性如下:

  • 任何一个HTML元素的title属性
    任何一个HTML元素都有一个title属性,当鼠标选中的时候放上去会展示出title属性的值

  • img元素的src属性
    在这里提出了一个服务器给的大图片和渲染小图片之间的性能问题?
    前端进行请求图片的时候传入一个参数params=140x140

  • a元素的href以及target属性

  • label元素的for属性需要谢伟htmlFor,这是为了避免和js中的for循环冲突

注意:
属性值可以是一个this.state中的变量
属性值也可以是一个经过函数调用的结果或者表达式处理后的结果

给HTML元素绑定class属性

  • 给元素添加class类名的时候,需要将class修改为className
    js中的class用来定义类的,所以在标签中使用class来给元素添加类名的时候就会冲突
    React会警告不可以用class来为元素绑定类名,而是应该用className

  • js中的for指的是循环,html元素中的for指的是要绑定的表单id
    所以也会冲突,解决方案是将lable中的for修改为

本质原因还是js中的关键字会和html元素中属性进行冲突

动态给元素绑定class类名

React写法:通过拼接一个动态的字符串来实现,是基于原生JS实现
<div className={"header large " + (active?'active':'')}></div>

Vue写法:通过动态绑定class的对象语法实现,是模板语法的实现
<div class="header large" :class={active?'active':''}></div>

给HTML元素绑定内联style样式

<div style={{width:"500px";height:this.state.innerHeight;fontSize:"24px"}}></div>
  • 外面一层表示开始javaScript的上下文
  • 里面一层表示对象object,里面是key-value的键值对
  • 每个键值对用逗号隔开
  • 如果键值对的值是固定值,那么直接写字符串;如果是动态的哪些要写state中的变量
  • 键值对的键名key如果是多个单词组成的,必须要用小驼峰标识,比如:fontSize:"50px"

2.jsx绑定事件方法


render(){
	return (
		<div>
			<div onClick={this.btnClick}>点击</div>
		</div>
	)
}
btnClick(){
	console.log(this)
}
  • 为了和原生做区分,jsx中的事件名称要用小驼峰来绑定,比如onclick变为onClick
  • 如果在事件处理函数中需要用到当前组件中的某个属性或者方法,那么直接通过this.xxx是获取不到的
    为什么获取不到?原因有三点
  1. 事件处理函数在绑定之后并不会立即执行,而是会在事件触发的时候执行,只有在函数执行的时候this才会被确定
  2. 事件触发的时候,React内部会回调绑定的事件处理函数并将其执行
  3. 在React执行的过程中,它内部的逻辑是:btnClick.call(undefined,...args)可见react内部默认给他绑定的就是一个undefined

3.jsx中绑定事件处理函数时如何让this指向当前组件对象?【三种绑定方案】

方案一:在绑定事件处理函数的时候就通过bind提前绑定事件处理函数中this为当前组件对象

  • 缺点:代码冗余,耦合度高,如果有多个需要挨个一一对应
class A extends React.Component {
	constructor(){
		super();
	}
	render(){
		return (
			<div onClick={this.btnClick.bind(this)}>点击1</div>
			<div onClick={this.btnClick.bind(this)}>点击2</div>
			<div onClick={this.btnClick.bind(this)}>点击3</div>
		)
	}
}
  • 对缺点进行优化处理:
    在组件的constructoir构造器的地方提前将需要多次绑定this的事件处理函数进行赋值处理
class A extends React.Component {
	constructor(){
		super();
		this.btnClick = this.btnClick.bind(this);
	}
	render(){
		return (
			<div onClick={this.btnClick}>点击1</div>
			<div onClick={this.btnClick}>点击2</div>
			<div onClick={this.btnClick}>点击3</div>
		)
	}
}

这种方案绑定的事件处理函数,react会隐式的传递一个参数event事件对象,只要事件处理函数的形参进行了接收,那么就可以拿到事件的事件对象。核心还是react在执行事件处理函数的时候采用了如下的事件处理机制:
btnCLick.call(undefined,event);执行btnCLick函数的同时,将this指向undefined,并传递一个参数event
function btnCLick(e){};接收参数event

bind的优先级是大于call的,所以只要手动bind绑定了,那么通过bind绑定的this还是有效的。

方案二:利用箭头函数中永远不绑定this的特点
箭头函数中this会向上一级作用域中依次查找上一级作用域中的this
将箭头函数通过等号赋值给一个属性名,这种语法是ES6中给对象添加属性的新特性,叫做class fields

class A extends React.Component {
	constructor(name){
		super();
		this.name = name;
	}
	render(){
		return (
			<div onClick={this.btnClick}>点击1</div>
		)
	}
	btnClick = ()=>{
		console.log(this);
	}
}

方案三:直接给事件绑定一个箭头函数

  1. 逻辑比较简单的代码,直接写在箭头函数体中就可以了
    因为箭头函数体中this向上一级查找是render函数,render函数中的this默认指向组件对象
class A extends React.Component {
	constructor(name){
		super();
		this.name = name;
	}
	render(){
		return (
			<div onClick={()=>{
				this.xxx =xxx;
				....这里是若干行事件触发后要执行的js代码
			}}></div>
		)
	}
	btnClick = ()=>{
		console.log(this);
	}
}
  1. 逻辑比较复杂的,还是抽取成一个单独的函数来写
    只不过不直接调用这个函数,而是包裹在箭头函数中执行,流程如下:
    事件触发=>箭头函数中的函数执行=>this.btnClick()执行
class A extends React.Component {
	constructor(name){
		super();
		this.name = name;
	}
	render(){
		return (
			<div onClick={()=>{this.btnClick}}></div>
		)
	}
	btnClick = ()=>{
		console.log(this);
	}
}

以上这种思路联想起珠峰将bind时的一个问题
问题:要求当div被点击的时候,add函数执行的时候this指向obj

方案一:通过bind绑定
div.onclick = add.bind(obj);

方案二:通过函数嵌套处理
div.onclick = function(){
add.call(obj,args);
}

4.jsx语法中绑定事件时如何传递参数

class A extends React.Component {
	constructor(name){
		super();
		this.name = name;
	}
	render(){
		return (
			<div onClick={()=>{this.btnClick}}></div>
		)
	}
	btnClick(e){
		console.log(e);
	}
}

所有以上述第三种方案绑定事件处理函数为例,因为这种方案最简单,易于组织代码

浏览器原生事件对象
React内部合成的事件对象

  1. 只需要event事件对象,不需要额外参数
    直接在定义的事件中获取event事件对象即可
<button onClick={(e)=>{this.btnClick3(e)}}>绑定事件</button>
<!-- 事件触发 -->
btnClick3(e){
	console.log(e);
}
  1. 只传递参数,不需要event事件对象
<button onClick={()=>{this.btnClick3('666',8988)}}>绑定事件</button>
<!-- 事件触发 -->
btnClick3(value,num){
	console.log(value,num);
}
  1. 既需要传递参数,又需要event事件对象
<button onClick={(e)=>{this.btnClick3('666',8988,e)}}>绑定事件</button>
<!-- 事件触发 -->
btnClick3(value,num,e){
	console.log(value,num,e);
}

5.jsx中map循环时组织代码技巧:

  • 如果是map循环,那么请在return后面加上括号组织换行,因为只有加了括号才可以换行
  • 多个属性和事件绑定换行来写,要清楚的多
class A extends React.Component {
	constructor(name){
		super();
		this.state = {
			arr = [12,45,45,45],
		}
	}
	render(){
		return (
			<button onClick={(e)=>{this.btnClick3('666',8988,e)}}>绑定事件</button>
			<ul>
				{
					this.state.arr.map((item,index)=>{
						return (
							<li className="demo"
								onClick={(e)=>{this.liClick(item,index,e)}}
								title={this.state.title}
								style={{width:'400px',fontSize="20px"}}
							>
							</li>
						)
					})
				}
			</ul>
		)
	}
	btnClick3(value,num,e){
		console.log(value,num,e);
	}
	liClick(item,index,e){
		console.log(item,index,e);
	}

6.jsx条件渲染的四个方案

方式一:if--else--if条件判断语句
适合逻辑比较多的场景
在逻辑代码中还需要做一些操作的时候
将比较多的逻辑代码抽取在函数中,可以让代码看起来比较简单

class App extends React.Component{
	constructor(){
		super();
		this.state = {
			isLogin:true,
		}
	}
	
	render(){
		let {isLogin} = this.state;
		let welcome = null;
		let btnText = null;
		if(isLogin){
			welcome = '欢迎登录';
			btnText = '退出'
			
		}else{
			welcome = '请先登录';
			btnText = '登录'
		}
		return (
			<div>
				<h2>{welcome}</h2>
				<button onClick={()=>{this.btnClick()}}>{btnText}</button>
			</div>
		)
	}
	btnClick(){
		this.setState({
			isLogin:!this.state.isLogin,
		})
	}
}

方式二: 三目运算符
适合逻辑比较少的场景,比如直接写在jsx中渲染的地方
但是null比较冗余,三元中的null表示什么都不做

class App extends React.Component{
	constructor(){
		super();
		this.state = {
			isLogin:true,
		}
	}
	
	render(){
		let {isLogin} = this.state;
		return (
			<div>
				<button onClick={()=>{this.btnClick()}}>{isLogin?'退出':'登录'}</button>
				<h3>{isLogin && '你好啊,李银河!'}</h3>
			</div>
		)
	}
	btnClick(){
		this.setState({
			isLogin:!this.state.isLogin,
		})
	}
}

方式三:逻辑与&& 实现效果类似于Vue的v-if
适合取反的状态,比如:
当条件满足就取A;否则就取B
当条件满足就渲染A,或者什么都不渲染

重点:

{isLogin &&

你好啊,李银河

}

类似上面这种,本质是当isLogin为true的时候,就渲染h2标签及其内容;否则就直接不渲染,就是DOM结构中不存在这个h2元素。

class App extends React.Component{
	constructor(){
		super();
		this.state = {
			isLogin:true,
		}
	}
	
	render(){
		let {isLogin} = this.state;
		return (
			<div>
				<button onClick={()=>{this.btnClick()}}>{isLogin?'退出':'登录'}</button>
				<h3>{isLogin && '你好啊,李银河!'}</h3>
				<div>{isLogin && <h2>你好啊,李银河</h2>}</div>
			</div>
		)
	}
	btnClick(){
		this.setState({
			isLogin:!this.state.isLogin,
		})
	}
}

方式四:实现效果类似于Vue的v-show 需要频繁控制元素显示与隐藏的时候
基于动态绑定style内联样式中display属性为block还是none完成,这种是基于CSS属性来控制的,当display为block的时候元素显示;none的时候元素隐藏,但是无论如何,该元素在DOM结构中是一直存在的。

简单的显示与隐藏的逻辑可以直接在{}中完成

复杂的逻辑可以直接抽取到一个变量中,让变量保存每次render的时候的结果

class App extends React.Component{
	constructor(){
		super();
		this.state = {
			isLogin:true,
		}
	}
	
	render(){
		let {isLogin} = this.state;
		let isShowMessage = isLogin?'block':'none';
		return (
			<div>
				<h2>{welcome}</h2>
				<button onClick={()=>{this.btnClick()}}>{isLogin?'退出':'登录'}</button>
				<div style={{display:(isLogin?'block':'none')}}></div>
				<div style={{display:isShowMessage}}></div>
			</div>
		)
	}
	btnClick(){
		this.setState({
			isLogin:!this.state.isLogin,
		})
	}
}

7.jsx的列表循环

在JSX中并没有像Vue模块语法中的v-for指令,并且需要我们通过javaScript代码的方式实现

一、Array.prototype.map高阶函数实现

class App extends React.Component{
	constructor(){
		super();
		this.state = {
			list:[78,89,45,121,2,45,5,12,12,12,12]
		}
	}
	
	render(){
		return (
			<div>
				<ul style={{listStyle:"none",margin:'10px',padding:0}}>
					{
						this.state.list.map((item,index)=>{
							return (<li>{`这是第${index}个数据,值为${item}`}</li>)
						})
					}
				</ul>
			</div>
		)
	}
}

二、对原数据进行filter过滤之后再进行map映射
filter内部必须return一个布尔值,并且filter函数一定是返回一个新数组
如果返回true,那么会将当前遍历项放入到新的数组中
如果返回flase,那么这一项会被过滤掉
链式调用filter和map函数,可以很大程度上简化代码并且结构清晰

class App extends React.Component{
		constructor(){
			super();
			this.state = {
				list:[78,89,45,121,2,45,5,12,12,12,12]
			}
		}
		
		render(){
			const {list} = this.state;
			return (
				<div>
					<ul style={{listStyle:"none",margin:'10px',padding:0}}>
						{
							list.filter((item,index)=>{
								return item <= 50;
							}).map((item,index)=>{
								return (<li>{`这是第${index}个数据,值为${item}`}</li>)
							})
						}
					</ul>
				</div>
			)
		}
	}

posted @ 2021-08-30 21:29  小高同学1997  阅读(218)  评论(0编辑  收藏  举报