龙空技术网

王红元React系列二 - 核心JSX语法一

小码哥教育 39

前言:

此刻大家对“jsx属性”大体比较看重,同学们都想要分析一些“jsx属性”的相关内容。那么小编同时在网上网罗了一些有关“jsx属性””的相关内容,希望朋友们能喜欢,咱们一起来学习一下吧!

一. ES6的class

虽然目前React开发模式中更加流行hooks,但是依然有很多的项目依然是使用类组件(包括AntDesign库中);

但是有很多的同学对ES6中的类不太熟悉,所以这里我还是补充一下;

1.1. 类的定义

在ES6之前,我们通过function来定义类,但是这种模式一直被很多从其他编程语言(比如Java、C++、OC等等)转到JavaScript的人所不适应。

原因是,大多数面向对象的语言,都是使用class关键字来定义类的。

而JavaScript也从ES6开始引入了class关键字,用于定义一个类。

ES6之前定义一个Person类:

function Person(name, age) {  this.name = name;  this.age = age;}Person.prototype.running = function() {  console.log(this.name + this.age + "running");}var p = new Person("why", 18);p.running();

转换成ES6中的类如何定义呢?

类中有一个constructor构造方法,当我们通过new关键字调用时,就会默认执行这个构造方法构造方法中可以给当前对象添加属性类中也可以定义其他方法,这些方法会被放到Person类的prototype上

class Person {  constructor(name, age) {    this.name = name;    this.age = age;  }  running() {    console.log(this.name + this.age + "running");  }}const p = new Person("why", 18);p.running();

另外,属性也可以直接定义在类中:

height和address是直接定义在类中

class Person {  height = 1.88;  address = "北京市";  constructor(name, age) {    this.name = name;    this.age = age;  }  studying() {    console.log(this.name + this.age + "studying");  }}
1.2. 类的继承

继承是面向对象的一大特性,可以减少我们重复代码的编写,方便公共内容的抽取(也是很多面向对象语言中,多态的前提)。

ES6中增加了extends关键字来作为类的继承。

我们先写两个类没有继承的情况下,它们存在的重复代码:

Person类和Student类

class Person {  constructor(name, age) {    this.name = name;    this.age = age;  }  running() {    console.log(this.name, this.age, "running");  }}class Student {  constructor(name, age, sno, score) {    this.name = name;    this.age = age;    this.sno = sno;    this.score = score;  }  running() {    console.log(this.name, this.age, "running");  }  studying() {    console.log(this.name, this.age, this.sno, this.score, "studing");  }}

我们可以使用继承来简化代码:

注意:在constructor中,子类必须通过super来调用父类的构造方法,对父类进行初始化,否则会报错。

class Student1 extends Person {  constructor(name, age, sno, score) {    super(name, age);    this.sno = sno;    this.score = score;  }  studying() {    console.log(this.name, this.age, this.sno, this.score, "studing");  }}const stu1 = new Student1("why", 18, 110, 100);stu1.studying();
二. 案例练习2.1. 列表展示

真实开发中,我们的数据通常会从服务器获取,比较常见的是获取一个列表数据,保存到一个数组中进行展示

比如现在有一个电影列表,我们如何通过React进行展示呢?

我们还是通过一个组件来完成:

class App extends React.Component {  constructor(props) {    super(props);    this.state = {      movies: ["星际穿越", "大话西游", "盗梦空间", "少年派"]    }  }  render() {    // var movieLis = [];    // for (var i in this.state.movies) {    //   movieLis.push((<li>{this.state.movies[i]}</li>));    // }    return (      <div>        <h2>电影列表</h2>        <ul>          {            this.state.movies.map((item, index) => {              return (<li>{item}</li>)            })          }        </ul>      </div>    )  }}ReactDOM.render(<App/>, document.getElementById("app"));
2.2. 计数器案例

电影列表的案例中并没有交互,我们再来实现一个计数器的案例:

class App extends React.Component {  constructor(props) {    super(props);    this.state = {      counter: 0    }  }  render() {    return (      <div>        <h2>当前计数:{this.state.counter}</h2>        <button onClick={this.increment.bind(this)}>+1</button>        <button onClick={this.decrement.bind(this)}>-1</button>      </div>    )  }  increment() {    this.setState({      counter: this.state.counter+1    })  }  decrement() {    this.setState({      counter: this.state.counter-1    })  }}ReactDOM.render(<App/>, document.getElementById("app"));
三. JSX语法解析3.1. 认识JSX的语法

我们先来看一段代码:

这段element变量的声明右侧赋值的标签语法是什么呢?它不是一段字符串(因为没有使用引号包裹),它看起来是一段HTML原生,但是我们能在js中直接给一个变量赋值html吗?其实是不可以的,如果我们将 type="text/babel" 去除掉,那么就会出现语法错误;它到底是什么呢?其实它是一段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中的标签可以是单标签,也可以是双标签;注意:如果是单标签,必须以/>结尾;

JSX的本质,我们后面再来讨论;

3.2. JSX嵌入表达式

如果我们jsx中的内容是动态的,我们可以通过表达式来获取:

书写规则:{表达式}大括号内可以是变量、字符串、数组、函数调用等任意js表达式;3.2.1. jsx中的注释

jsx是嵌入到JavaScript中的一种语法,所以在编写注释时,需要通过JSX的语法来编写:

<div>  {/* 我是一段注释 */}  <h2>Hello World</h2></div>
3.2.2. JSX嵌入变量情况一:当变量是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>    )  }}
3.3.3. JSX嵌入表达式

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;  }}
3.3.4. jsx绑定属性

很多时候,描述的HTML原生会有一些属性,而我们希望这些属性也是动态的:

比如元素都会有title属性比如img元素会有src属性比如a元素会有href属性比如元素可能需要绑定class注意:绑定class比较特殊,因为class在js中是一个关键字,所以jsx中不允许直接写class写法:使用className替代比如原生使用内联样式stylestyle后面跟的是一个对象类型,对象中是样式的属性名和属性值;注意:这里会讲属性名转成驼峰标识,而不是连接符-;

我们来演示一下属性的绑定:

class App extends React.Component {  constructor(props) {    super(props);    this.state = {      title: "你好啊",      imgUrl: ";,      link: ";,      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>    )  }}
3.3. jsx事件监听3.3.1. 和原生绑定区别

如果原生DOM原生有一个监听事件,我们可以如何操作呢?

方式一:获取DOM原生,添加监听事件;方式二:在HTML原生中,直接绑定onclick;

我们这里演练一下方式二:

btnClick()这样写的原因是onclick绑定的后面是跟上JavaScript代码;

<button onclick="btnClick()">点我一下</button><script>  function btnClick() {  console.log("按钮发生了点击");}</script>

在React中是如何操作呢?

我们来实现一下React中的事件监听,这里主要有两点不同

React 事件的命名采用小驼峰式(camelCase),而不是纯小写;我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行;

class App extends React.Component {  render() {    return (      <div>        <button onClick={this.btnClick}>点我一下(React)</button>      </div>    )  }  btnClick() {    console.log("React按钮点击了一下")  }}
3.3.2. this绑定问题

在事件执行后,我们可能需要获取当前类的对象中相关的属性:

比如我们这里打印: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

在传入函数时,我们可以主动绑定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);  }}
3.3.3. 事件参数传递

在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数

情况一:获取event对象

很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)假如我们用不到this,那么直接传入函数就可以获取到event对象;

class App extends React.Component {  constructor(props) {  render() {    return (      <div>        <a href="; 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="; 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);  }}

标签: #jsx属性