一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
前言
大家好呀,我是L同学。在上篇文章中,我们初次体验了react的使用。在接下来的文章中,我将会对react中的知识点进行详细介绍。
在这篇文章中,我们将学习什么是jsx以及jsx的使用。
jsx介绍
jsx是一种javascript的扩展语法,也在很多地方称它为JavaScript XML,因为看起来就是一段XML语法。它用来描述我们的UI界面,并且它可以和javascript融合在一起使用。
jsx是嵌入到javascript中的一种结构语法。
<body>
<div id="app"></div>
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
<script src="../react/babel.min.js"></script>
<script type="text/babel">
// jsx语法
const element = <h2>Hello World</h2>;
ReactDOM.render(element, document.getElementById("app"));
</script>
</body>
需要注意的是,必须有type="text/babel",如果去掉会报语法错误。
另外,jsx中还有一些书写规范需要注意:
- jsx的顶层只能有一个根元素
- jsx中的标签可以是单标签,也可以是双标签。如果是单标签,必须以/>结尾。
jsx的使用
jsx中的注释
jsx中使用 {/* */} 进行注释。
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {}
}
render() {
return (
<div>
{/* 这是一段注释 */}
<h2>hello react</h2>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
</script>
jsx嵌入变量
在jsx中可以嵌入变量,变量的类型不同,显示情况也会不同。
- 当变量是Number、String、Array类型时,在{}中可以显示内容
- 当变量是null、undefined、Boolean类型时,在{}中不显示内容
如果希望显示null、undefined、Boolean,那么就需要把它们转为字符串。可以和空字符串进行拼接、使用toString方法、String()方法将它们转换成字符串。
- 对象类型不能作为子元素
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
// 1. 在{}中可以正常显示内容
name: 'haha',
age: 20,
names: ['aaa', 'bbb', 'ccc'],
// 2. null、undefined、Boolean类型在{}中不能显示
test1: null,
test2: undefined,
flag: true,
// 3. 对象不能作为jsx的子类
friend: {
name: 'xixi',
age: 20
}
}
}
render() {
return (
<div>
<h2>{this.state.name}</h2>
<h2>{this.state.age}</h2>
<h2>{this.state.names}</h2>
<h2>{this.state.test1 + ''}</h2>
<h2>{this.state.test2 + ''}</h2>
<h2>{this.state.flag.toString()}</h2>
{/*运算表达式*/}
<h2>{this.state.flag ? '你好呀' : null}</h2>
<h2>{this.state.friend}</h2>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
jsx嵌入表达式
在jsx中可以嵌入表达式,可以是运算表达式,也可以是三元运算符,还可以执行一个函数。
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
firstName: 'haha',
lastName: 'xixi',
isLogin: false
}
}
render() {
const {firstName, lastName, isLogin} = this.state
return (
<div>
{/* 1. 运算表达式 */}
<h2>{firstName + ' ' + lastName}</h2>
<h2>{1 + 2}</h2>
{/* 2. 三元运算符 */}
<h2>{isLogin ? '欢迎光临' : '请先登陆'}</h2>
{/* 3. 调用一个函数 */}
<h2>{this.getFullName()}</h2>
</div>
)
}
getFullName() {
return this.state.firstName + ' ' + this.state.lastName
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
</script>
jsx绑定属性
我们知道img标签都有src属性,并且我们经常从服务器获取图片地址然后渲染到页面上,这是就需要给图片动态绑定src属性。还有些元素我们也需要给它们绑定clss。那么这些属性需要动态绑定,我们该怎么做呢?
接下来我们学习jsx如何绑定属性。学习如何绑定普通属性、绑定class、绑定style。
注意点:
jsx语法和javascript语法是混在一起的。jsx不能使用javascript中的关键字,例如定义类的class,想要给标签加类名,应为className。还有html的label元素有for属性,它在javascript中也是关键字,所以在jsx中应使用htmlFor。
<script type="text/babel">
function getSizeImage(imgUrl, size) {
return imgUrl + `?param=${size}x${size}`
}
class App extends React.Component {
constructor() {
super()
this.state = {
title: '我是标题',
imgUrl: 'http://p2.music.126.net/L8IDEWMk_6vyT0asSkPgXw==/109951163990535633.jpg',
link: 'http://www.baidu.com',
active: true
}
}
render() {
const { title, imgUrl, link, active } = this.state
return (
<div>
{/* 1. 绑定普通属性 */}
<h2 title={title}>我是标题</h2>
{/* 单标签必须加/ */}
<img src={getSizeImage(imgUrl, 140)} alt="" />
{/* 2. 绑定class */}
{/* 绑定class时不能使用class写类名,因为在javascript中class是关键字 */}
<div className='box title'>我是div元素1</div>
<div className={'box title' + active ? 'active' : ''}>我是div元素2</div>
{/* 在label标签中不能使用for,因为在javascript中for是关键字 */}
<label htmlFor=""></label>
{/* 3.绑定style */}
<div style={{ color: 'red', fontSize: '50px' }}>我是div3</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
jsx绑定事件和this处理
在react中,我们需要经常绑定事件,例如点击事件。我们需要在{}中传入一个事件处理函数,这个函数会在事件发生时被执行。
我们需要知道,在render函数中,this是当前对象,我们可以通过this.state拿到数据。
现在页面中有一个按钮,点击按钮我想要获取到state中的message数据。以下代码会出现什么问题呢?
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
message: '我是一条消息'
}
}
render() {
return (
<div>
<button onClick={this.btnClick}>按钮1</button>
</div>
)
}
btnClick() {
console.log(this);
console.log(this.state.message);
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
</script>
点击按钮,我们发现报了错误。打印btnClick这个函数的this,我们发现this指向的是undefined。这是为什么呢?
因为btnClick函数并不是我们主动调用的,当button发生改变时,react内部调用了btnClick。react内部发现我们点击了按钮之后,会对这个btnClick做了回调,执行btnClick。而它内部调用时,并不知道如何绑定正确的this,所以绑定了this是undefined,做了btnClick.call(undefined,事件对象event)。
那么我们应该怎么解决这个this问题呢?
this绑定问题可以阅读我之前写得关于this指向的系列文章 javascript中的this指向总结(一)
- 方法一:使用bind绑定this
render() {
return (
<div>
<button onClick={this.btnClick.bind(this)}>按钮1</button>
<button onClick={this.btnClick.bind(this)}>按钮2</button>
<button onClick={this.btnClick.bind(this)}>按钮3</button>
</div>
)
}
但是这样写,存在多个按钮的话,需要给多个按钮btnClick使用bind。
我们可以这样写。
constructor() {
super()
this.state = {
message: '我是一条消息'
}
this.btnClick = this.btnClick.bind(this)
}
render() {
return (
<div>
{/*
<button onClick={this.btnClick.bind(this)}>按钮1</button>
<button onClick={this.btnClick.bind(this)}>按钮2</button>
<button onClick={this.btnClick.bind(this)}>按钮3</button>
*/}
<button onClick={this.btnClick}>按钮1</button>
<button onClick={this.btnClick}>按钮2</button>
<button onClick={this.btnClick}>按钮3</button>
</div>
)
}
- 方法二: 定义函数时,使用箭头函数 这种方法是ES6中给对象增加了属性: class fields
我们需要知道,箭头函数永远不绑定this。
class App extends React.Component {
constructor() {
super()
this.state = {
message: '我是一条消息',
counter: 100
}
this.btnClick = this.btnClick.bind(this)
}
render() {
return (
<div>
{/*
<button onClick={this.btnClick.bind(this)}>按钮1</button>
<button onClick={this.btnClick.bind(this)}>按钮2</button>
<button onClick={this.btnClick.bind(this)}>按钮3</button>
*/}
{/* 方法一 bind绑定this */}
<button onClick={this.btnClick}>按钮1</button>
<button onClick={this.btnClick}>按钮2</button>
<button onClick={this.btnClick}>按钮3</button>
{/* 方法二 定义函数时,使用箭头函数 */}
<button onClick={this.increment}>+1</button>
</div>
)
}
btnClick() {
console.log(this);
console.log(this.state.message);
}
increment = () => {
console.log(this);
console.log(this.state.counter);
}
}
我们可以看到结果正确打印出来了。
- 方法三 事件监听时传入箭头函数,在剪头函数中调用需要执行的函数(推荐) 当我们点击按钮的时候,就会执行{}里的箭头函数,箭头函数的函数体里面的代码就会执行。我们知道箭头函数的函数体是不绑定this的,它就会从上层作用域找,找到render里面的this。render中的this指向的是对象。然后调用decrement,进行了隐式绑定。所以decrement中的this就是render中的this。
我们还可以往需要执行的函数中传递参数。
class App extends React.Component {
constructor() {
super()
this.state = {
message: '我是一条消息',
counter: 100
}
this.btnClick = this.btnClick.bind(this)
}
render() {
return (
<div>
{/*
<button onClick={this.btnClick.bind(this)}>按钮1</button>
<button onClick={this.btnClick.bind(this)}>按钮2</button>
<button onClick={this.btnClick.bind(this)}>按钮3</button>
*/}
{/* 方法一 bind绑定this */}
<button onClick={this.btnClick}>按钮1</button>
<button onClick={this.btnClick}>按钮2</button>
<button onClick={this.btnClick}>按钮3</button>
{/* 方法二 定义函数时,使用箭头函数 */}
<button onClick={this.increment}>+1</button>
{/* 方法三 事件监听时传入一个箭头函数,在箭头函数中调用需要执行的函数 */}
<button onClick={() => { this.decrement('haha') }}>-1</button>
</div>
)
}
btnClick() {
console.log(this);
console.log(this.state.message);
}
increment = () => {
console.log(this);
console.log(this.state.counter);
}
decrement(name) {
console.log(this);
console.log(this.state.counter, name);
}
}
点击按钮后我们能获取到counter和name的值。