1.认识JSX的语法
下面是一段JSX语法
<script type="text/babel">
const element = <h2>Hello World</h2>
ReactDOM.render(element, document.getElementById("app"));
</script>
JSX是什么?
- JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;
- 它用于描述我们的UI界面,并且其完全可以和JavaScript融合在一起使用.(所以灵活性很高);
- 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
为什么React选择了JSX?
-
React认为渲染逻辑本质上与其他UI逻辑存在内在耦合
- 比如UI需要绑定事件(button、a原生等等);
- 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI;
-
他们之间是密不可分,所以React没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件(Component),JSX其实是嵌入到JavaScript中的一种结构语法;
JSX的书写规范:
- JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div原生(或者使用后面我们学习的Fragment);
- JSX中的标签可以是单标签,也可以是双标签;(注意:如果是单标签,必须以/>结尾;)
- 我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;如下例子:
return (
<div>
<h2>电影列表1</h2>
<ul>
{liArray}
</ul>
<h2>电影列表2</h2>
<ul>
{
this.state.movies.map((item) => {
return <li>{item}</li>
})
}
</ul>
</div>
)
1.2 JSX嵌入表达式
如果我们jsx中的内容是动态的,我们可以通过表达式来获取:
- 书写规则:{表达式}
- 大括号内可以是变量、字符串、数组、函数调用等任意js表达式;如上例子:
1.2.1jsx中的注释
jsx是嵌入到JavaScript中的一种语法,所以在编写注释时,需要通过JSX的语法来编写:
<div>
{/* 我是一段注释 */}
<h2>Hello World</h2>
</div>
1.2.2JSX嵌入变量
- 当变量是Number、String、Array类型时,可以直接显示
- 当变量是null、undefined、Boolean类型时,内容为空;
- 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
- 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
- 对象类型不能作为子元素(not valid as a React child)否则会报错
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "why",
age: 18,
hobbies: ["篮球", "唱跳", "rap"],
test1: null,
test2: undefined,
flag: false,
friend: {
name: "kobe",
age: 40
}
}
}
render() {
return (
<div>
{/* 我是一段注释 */}
<h2>Hello World</h2>
</div>
<div>
{/* 1.可以直接显示 */}
<h2>{this.state.name}</h2>
<h2>{this.state.age}</h2>
<h2>{this.state.hobbies}</h2>
{/* 2.不显示 */}
<h2>{this.state.test1}</h2>
<h2>{this.state.test1 + ""}</h2>
<h2>{this.state.test2}</h2>
<h2>{this.state.test2 + ""}</h2>
<h2>{this.state.flag}</h2>
<h2>{this.state.flag + ""}</h2>
{/* 3.报错,不显示 */}
<h2>123{this.state.friend}</h2>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
说明:为什么null、undefined、Boolean在JSX中要显示为空内容呢?
- 在判断结果为false时,不显示一个内容;
- 在判断结果为true时,显示一个内容;
这个时候,我们可以编写如下代码:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
flag: false
}
}
render() {
return (
<div>
{this.state.flag ? <h2>我是标题</h2>: null}
{this.state.flag && <h2>我是标题</h2>}
</div>
)
}
}
1.2.3JSX嵌入表达式
JSX中,也可以是一个表达式。
- 运算表达式
- 三元运算符
- 执行一个函数
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "kobe",
lastName: "bryant",
age: 20
}
}
render() {
return (
<div>
{/* 运算表达式 */}
<h2>{this.state.firstName + " " + this.state.lastName}</h2>
{/* 三元运算符 */}
<h2>{this.state.age >= 18 ? "成年人": "未成年人"}</h2>
{/* 执行一个函数 */}
<h2>{this.sayHello("kobe")}</h2>
</div>
)
}
sayHello(name) {
return "Hello " + name;
}
}
1.2.4 jsx绑定属性
很多时候,描述的HTML原生会有一些属性,而我们希望这些属性也是动态的:
- 比如元素都会有title属性
- 比如img元素会有src属性
- 比如a元素会有href属性
- 比如元素可能需要绑定class (注意:绑定class比较特殊,因为class在js中是一个关键字,所以jsx中不允许直接写class,,写法:使用className替代)
- 比如原生使用内联样式style(style后面跟的是一个对象类型,对象中是样式的属性名和属性值,注意:这里属性名转成驼峰标识,而不是连接符- ;)
下面为各类属性绑定写法
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "你好啊",
imgUrl: "https://upload.jianshu.io/users/upload_avatars/1102036/c3628b478f06.jpeg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240",
link: "https://www.baidu.com",
active: false
}
}
render() {
return (
<div>
<h2 title={this.state.title}>Hello World</h2>
<img src={this.state.imgUrl} alt=""/>
<a href={this.state.link} target="_blank">百度一下</a>
<div className={"message " + (this.state.active ? "active": "")}>你好啊</div>
<div className={["message", (this.state.active ? "active": "")].join(" ")}>你好啊</div>
<div style={{fontSize: "30px", color: "red", backgroundColor: "blue"}}>我是文本</div>
</div>
)
}
}
1.3 jsx事件监听
1.3.1和原生绑定区别
原生DOM原生有一个监听事件,常规操作一般为:
- 获取DOM原生,添加监听事件;
- 在HTML原生中,直接绑定onclick;
<button onclick="btnClick()">点我一下</button> //btnClick()这样写的原因是onclick绑定的后面是跟上JavaScript代码;
<script>
function btnClick() {
console.log("按钮发生了点击");
}
</script>
在React中是如何操作de?
React中的事件监听,这里主要有两点不同
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写;
- 我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行;
class App extends React.Component {
render() {
return (
<div>
<button onClick={this.btnClick}>点我一下(React)</button>
</div>
)
}
btnClick() {
console.log("React按钮点击了一下")
}
}
1.3.2this绑定问题
在事件执行后,我们可能需要获取当前类的对象中相关的属性:
- 比如我们这里打印:this.state.message
- 但是这里会报错:Cannot read property 'state' of undefined
- 原因是this在这里是undefined -如果我们这里直接打印this,也会发现它是一个undefined
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "你好啊,李银河"
}
}
render() {
return (
<div>
<button onClick={this.btnClick}>点我一下(React)</button>
</div>
)
}
btnClick() {
console.log(this);
console.log(this.state.message);
}
}
为什么是undefined呢?
- 原因是btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;
- 而它内部调用时,并不知道要如何绑定正确的this;
如何解决this的问题呢? 方案一:bind给btnClick显示绑定this
- 这里我们主动将btnClick中的this通过bind来进行绑定(显示绑定)
- 那么之后React内部调用btnClick函数时,就会有一个this,并且是我们绑定的this;
<button onClick={this.btnClick.bind(this)}>点我一下(React)</button>
但如果我有两个函数都需要用到btnClick的绑定我们发现 bind(this) 需要书写两遍;:
<button onClick={this.btnClick.bind(this)}>点我一下(React)</button>
<button onClick={this.btnClick.bind(this)}>也点我一下(React)</button>
我们可以通过在构造方法中直接给this.btnClick绑定this来解决(注意查看 constructor 中我们的操作:this.btnClick = this.btnClick.bind(this);):
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "你好啊,李银河"
}
this.btnClick = this.btnClick.bind(this);
}
render() {
return (
<div>
<button onClick={this.btnClick}>点我一下(React)</button>
<button onClick={this.btnClick}>也点我一下(React)</button>
</div>
)
}
btnClick() {
console.log(this);
console.log(this.state.message);
}
}
方案二:使用 ES6 class fields 语法
你会发现我这里将btnClick的定义变成了一种赋值语句:
- 这是ES6中给类定义属性的方法,称之为class fields语法;
- 因为这里我们赋值时,使用了箭头函数,所以在当前函数中的this会去上一个作用域中查找;
- 而上一个作用域中的this就是当前的对象;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "你好啊,李银河"
}
}
render() {
return (
<div>
<button onClick={this.btnClick}>点我一下(React)</button>
<button onClick={this.btnClick}>也点我一下(React)</button>
</div>
)
}
btnClick = () => {
console.log(this);
console.log(this.state.message);
}
}
方案三:事件监听时传入箭头函数(推荐)
因为 onClick 中要求我们传入一个函数,那么我们可以直接定义一个箭头函数传入:
- 传入的箭头函数的函数体是我们需要执行的代码,我们直接执行 this.btnClick();
- this.btnClick()中通过this来指定会进行隐式绑定,最终this也是正确的;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "你好啊,李银河"
}
}
render() {
return (
<div>
<button onClick={() => this.btnClick()}>点我一下(React)</button>
<button onClick={() => this.btnClick()}>也点我一下(React)</button>
</div>
)
}
btnClick() {
console.log(this);
console.log(this.state.message);
}
}
1.3.3事件参数传递
在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数
情况一:获取event对象
- 很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)
- 假如我们用不到this,那么直接传入函数就可以获取到event对象;
class App extends React.Component {
constructor(props) {
render() {
return (
<div>
<a href="http://www.baidu.com" onClick={this.btnClick}>点我一下</a>
</div>
)
}
btnClick(e) {
e.preventDefault();
console.log(e);
}
}
情况二:获取更多参数
- 有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
names: ["衣服", "鞋子", "裤子"]
}
}
render() {
return (
<div>
<a href="http://www.baidu.com" onClick={this.aClick}>点我一下</a>
{
this.state.names.map((item, index) => {
return (
<a href="#" onClick={e => this.aClick(e, item, index)}>{item}</a>
)
})
}
</div>
)
}
aClick(e, item, index) {
e.preventDefault();
console.log(item, index);
console.log(e);
}
}
笔者通过学习codewhy的 React课程总结