一、JSX核心语法
1.什么是JSX?
- JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起来就是一段XML语法;
- 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
- 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
React认为渲染逻辑本质上与其他UI逻辑存在内在耦合
- 比如UI需要绑定事件(button、a原生等等);
- 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI;
- 他们之间是密不可分,所以React没有讲标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件 (Component);
- 在这里,我们只需要知道,JSX其实是嵌入到JavaScript中的一种结构语法;
2.JSX的书写规范
- JSX的顶层只能有一个根元素,我们很多时候会在最外层包裹一个div(后续React推出了不占据Dom结构的Fragment)
- 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
- JSX中的标签可以使单标签,也可以是双标签(如果是单标签,必须以/>结尾
3.JSX中的注释
单行注释和多行注释,都需要在大括号{}中编写
{
//我是单行注释
}
{/*我是一段注释*/}
4.JSX中嵌入变量
- 当变量是Number、String、Array类型时,可以直接显示;
- 当变量是null、undefined、Boolean类型时,内容显示为空(如果想让其展示可以将其转换为字符串进行展示);
- 对象类型不能作为变量嵌入('not valid as a React child')
{
// 1.在{}中可以正常显示显示的内容
name: "lanzhou", // String
age: 20, // Number
names: ["abc", "cba", "nba"], // Array
// 2.在{}中不能显示(忽略)
test1: null, // null
test2: undefined, // undefined
test3: true, // Boolean
// 3.对象不能作为jsx的子类
friend: {
name: "lanzhou",
age: 20
}
}
5.JSX嵌入表达式
- 运算表达式
- 三元运算符
- 执行一个函数
render() {
const {firstName, lastName, isLogin} = this.state;
return (
<div>
{/*1.运算符表达式*/}
<h2>{ firstName + " " + lastName }</h2>
<h2>{20 * 50}</h2>
{/*2.三元表达式*/}
<h2>{ isLogin ? "欢迎回来~": "请先登录~" }</h2>
{/*3.进行函数调用*/}
<h2>{this.sayHello()}</h2>
</div>
)
}
6.JSX绑定属性
- JSX绑定普通属性
- JSX绑定class
- JSX绑定style
render() {
const { title, imgUrl, link, active } = this.state;
return (
<div>
{/* 1.绑定普通属性 */}
<h2 title={title}>我是标题</h2>
<img src={getSizeImage(imgUrl, 140)} alt=""/>
<a href={link} target="_blank">百度一下</a>
{/* 2.绑定class */}
<div className="box title">我是div元素</div>
<div className={"box title " + (active ? "active": "")}>我也是div元素</div>
{/* 3.绑定style */}
<div style={{color: "red", fontSize: "50px"}}>我是div,绑定style属性</div>
</div>
)
7.JSX绑定事件
- JSX中绑定事件类似在HTML原生中绑定事件,只不过React中事件命名采用小驼峰(camelCase),而不是纯小写;
- 但是我们会发现在我们绑定的回调事件中访问我们对应的this会是undefined,这是因为对应的回调函数是React内部帮我们去进行调用的,React无法确定对应的this所以采用的是callback.apply(undefined,[])方式调用,改变了this的指向为undefined。但是我们往往需要访问到的this是我们React组件对象。解决方法有以下三种:
increment = () => {
console.log(this);
}
render() {
return (
<div>
{/* 1.方案一: bind绑定this(显示绑定) */}
<button onClick={this.btnClick.bind(this)}>按钮1</button>
<button onClick={this.btnClick.bind(this)}>按钮2</button>
<button onClick={this.btnClick.bind(this)}>按钮3</button>
{/* 2.方案二: 定义函数时, 使用箭头函数 */}
<button onClick={this.increment}>+1</button>
{/* 2.方案三(推荐): 直接传入一个箭头函数, 在箭头函数中调用需要执行的函数*/}
<button onClick={() => { this.decrement(666) }}>-1</button>
</div>
)
}
- 绑定事件参数的传递,推荐采用绑定this的方案三,既方便的传递我们想要传递的参数,还可以非常简单的获取到事件对象event
render() {
return (
<div>
<ul>
{
this.state.movies.map((item, index, arr) => {
return (
<li className="item"
onClick={ e => { this.liClick(item, index, e) }}
title="li">
{item}
</li>
)
})
}
</ul>
</div>
)
}
8.JSX条件渲染
某些情况下,页面会根据不同的条件展示不同的内容,或者决定是否渲染某部分内容。 在Vue中给我们提供了对应的v-if、v-show指令,然而React中实现条件渲染的方式有那些呢?
- 条件判断语句
- 三元运算符
- 与运算符 &&
- v-show效果(主要是控制html标签的display属性是否为none)
render() {
const { isLogin } = this.state;
// 1.方案一:通过if判断: 逻辑代码非常多的情况
let welcome = null;
let btnText = null;
if (isLogin) {
welcome = <h2>欢迎回来~</h2>
btnText = "退出";
} else {
welcome = <h2>请先登录~</h2>
btnText = "登录";
}
return (
<div>
{welcome}
{/* 2.方案二: 三元运算符 */}
<button onClick={e => this.loginClick()}>{isLogin ? "退出" : "登录"}</button>
<h2>{isLogin ? "你好啊, 666": null}</h2>
{/* 3.方案三: 逻辑与&& */}
{/* 逻辑与: 一个条件不成立, 后面的条件都不会进行判断了 */}
<h2>{ isLogin && "你好啊, 666" }</h2>
{ isLogin && <h2>你好啊, 666</h2> }
</div>
)
}
{/* 4.方案四: 根据一个状态动态更改对应标签的display属性的值为block或none */}
render() {
const { isLogin} = this.state;
const titleDisplayValue = isLogin ? "block": "none";
return (
<div>
<button onClick={e => this.loginClick()}>{isLogin ? "退出": "登录"}</button>
<h2 style={{display: titleDisplayValue}}>你好啊, 666</h2>
</div>
)
}
loginClick() {
this.setState({
isLogin: !this.state.isLogin
})
}
}
9.JSX列表渲染
在React中列表生成及展示运用最多的是js中高阶函数,当然我们也可以运用for循环来进行列表的生成及展示。
render() {
return (
<div>
<h2>名字列表</h2>
<ul>
{
this.state.names.map(item => {
return <li>{item}</li>
})
}
</ul>
<h2>数字列表(过滤1)</h2>
<ul>
{
this.state.numbers.filter(item => {
return item >= 50;
}).map(item => {
return <li>{item}</li>
})
}
</ul>
<h2>数字列表(过滤2)</h2>
<ul>
{
this.state.numbers.filter(item => item >= 50).map(item => <li>{item}</li>)
}
</ul>
<h2>数字列表(截取)</h2>
<ul>
{
this.state.numbers.slice(0, 4).map(item => {
return <li>{item}</li>
})
}
</ul>
</div>
)
}
二、JSX本质浅析
JSX实际上仅仅是React.createElement(type, config, children)方法的语法糖,该方法接收三个参数:
- type,当前ReactElement的类型,如果是标签元素,那么使用字符串表示“div”,如果是组件元素直接使用组件的名称就可以。
- config,我们在JSX中绑定的属性会在config对象中以键值对的形式存在。
- children,存放标签中的内容,以children数组的形式存储 我们知道JSX是通过babel进行解析的,我们编写JSX需要依赖babel,我们可以在babel官网查看转换的过程 babeljs.io/repl/#?pres…
如果我们直接使用React.createElement()来编写代码,就不需要以来bable进行解析也可以正常的渲染显示
我们通过React.createElement()方法最后返回得到的是一个ReactElement对象,这个ReactElement对象作用是什么?其实React利用ReactElement对象组成了一个JavaScript对象树,这个对象树就是我们经常讲的一个概念--虚拟DOM,我们可以将之前jsx返回的结果进行打印来查看对应的ReactElemnt对象:
首先是我们编写的JSX代码经过bable编译解析成对应的React.createElement()方法形式,经过React.createElement()方法调用返回我们对应的ReactElement对象树(虚拟DOM树),对应的ReactElement对象树经过ReactDOM.render()方法转换为真正的DOM在我们的浏览器进行渲染。
graph TD
JSX --> ReactElement(虚拟DOM) --> 真实DOM
为什么采用虚拟DOM呢?
- 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试;
- 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低; DOM操作非常耗费性能:
- document.createElement本身创建出来的就是一个非常复杂的对象developer.mozilla.org/zh-CN/docs/…
- DOM操作会引起浏览器的回流和重绘,所以在开发中应该避免频繁的DOM操作