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(
  • {movie}
  • ) } return (

    {this.state.title}

      {lis}
    ) } } ReactDOM.render(,document.getElementById('app'));

    方案二:基于map方法实现

    class App extends React.Component {
    	constructor(){
    		super();
    		this.state = {
    			title:'电影列表2',
    			movies:['功夫','少年黄飞鸿','国产凌凌漆','大决战之辽沈战役']
    		}
    	}
    	render(){
    		return (
    			

    {this.state.title}

      { this.state.movies.map((item)=>{ return
    • {item}
    • }) }
    ) } } ReactDOM.render(,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 (
    			

    当前计数:{this.state.counter}


    ) } addClick(){ this.setState({ counter:++this.state.counter }) }; subClick(){ this.setState({ counter:--this.state.counter }) }; } ReactDOM.render(,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才显示你好啊,否则什么都不显示,但是这里就有可能会造成在页面上多出一个字符串的布尔值

    {flag?'你好啊':null}
    {flag &&'你好啊'}

    如果希望可以将其显示,那么该如何操作?
    如果希望可以显示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.数学运算符表达式 */}
    

    {pName + '---' + pAge}

    {/* 2.三元表达式 */}

    {flag?'是的很好':'请优化一下'}

    {/* 3.进行函数调用 */}

    {this.getName()}

    问题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实现
    
    Vue写法:通过动态绑定class的对象语法实现,是模板语法的实现

    给HTML元素绑定内联style样式

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

    2.jsx绑定事件方法

    
    render(){
    	return (
    		
    点击
    ) } 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 (
    			
    点击1
    点击2
    点击3
    ) } }
    • 对缺点进行优化处理:
      在组件的constructoir构造器的地方提前将需要多次绑定this的事件处理函数进行赋值处理
    class A extends React.Component {
    	constructor(){
    		super();
    		this.btnClick = this.btnClick.bind(this);
    	}
    	render(){
    		return (
    			
    点击1
    点击2
    点击3
    ) } }

    这种方案绑定的事件处理函数,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 (
    			
    点击1
    ) } btnClick = ()=>{ console.log(this); } }

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

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

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

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

    1. 只需要event事件对象,不需要额外参数
      直接在定义的事件中获取event事件对象即可
    
    
    btnClick3(e){
    	console.log(e);
    }
    
    1. 只传递参数,不需要event事件对象
    
    
    btnClick3(value,num){
    	console.log(value,num);
    }
    
    1. 既需要传递参数,又需要event事件对象
    
    
    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 (
    			
    			
      { this.state.arr.map((item,index)=>{ return (
    • {this.liClick(item,index,e)}} title={this.state.title} style={{width:'400px',fontSize="20px"}} >
    • ) }) }
    ) } 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 (
    			

    {welcome}

    ) } 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 (
    			

    {isLogin && '你好啊,李银河!'}

    ) } 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 (
    			

    {isLogin && '你好啊,李银河!'}

    {isLogin &&

    你好啊,李银河

    }
    ) } 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 (
    			

    {welcome}

    ) } 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 (
    			
      { this.state.list.map((item,index)=>{ return (
    • {`这是第${index}个数据,值为${item}`}
    • ) }) }
    ) } }

    二、对原数据进行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 (
    				
      { list.filter((item,index)=>{ return item <= 50; }).map((item,index)=>{ return (
    • {`这是第${index}个数据,值为${item}`}
    • ) }) }
    ) } }