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页面中使用的时候)
- 首先必须得引入babel.js这个依赖
- 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嵌入变量
- 当变量是Number、String、Array类型的时候,可以直接显示
- 当变量是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()或者空串进行拼接
- 当变量是一个对象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是获取不到的
为什么获取不到?原因有三点
- 事件处理函数在绑定之后并不会立即执行,而是会在事件触发的时候执行,只有在函数执行的时候this才会被确定
- 事件触发的时候,React内部会回调绑定的事件处理函数并将其执行
- 在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);
}
}
方案三:直接给事件绑定一个箭头函数
- 逻辑比较简单的代码,直接写在箭头函数体中就可以了
因为箭头函数体中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);
}
}
- 逻辑比较复杂的,还是抽取成一个单独的函数来写
只不过不直接调用这个函数,而是包裹在箭头函数中执行,流程如下:
事件触发=>箭头函数中的函数执行=>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内部合成的事件对象
- 只需要event事件对象,不需要额外参数
直接在定义的事件中获取event事件对象即可
<button onClick={(e)=>{this.btnClick3(e)}}>绑定事件</button>
<!-- 事件触发 -->
btnClick3(e){
console.log(e);
}
- 只传递参数,不需要event事件对象
<button onClick={()=>{this.btnClick3('666',8988)}}>绑定事件</button>
<!-- 事件触发 -->
btnClick3(value,num){
console.log(value,num);
}
- 既需要传递参数,又需要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为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>
)
}
}