React(2-1):JSX入门

ObjectKaz Lv4

JSX 基础

介绍

1
const element = <p>Hello, world!</p>;

这种看上去像 html 的东西便是 jsx

一段 jsx 代码必须有一个根元素,也就是说,下面的代码是错的:

1
const element = <p>Hello, world!</p><p>Hello, world!</p>;

在项目中使用 jsx

前面提到,jsx 最终会编译成 React.createElement

因此,在使用 jsx 语法之前,应该引入 React,尽管它可能没有在其他的地方被使用:

1
import React from 'react'; // 引入 React 才能使用 jsx

多行jsx

如果你的 jsx 比较长,可以将它拆分成多行,不过需要加上一对括号:

1
2
3
4
5
const el = (
<p>
Hello, world!
</p>
);

插入 js 表达式

可以使用 {} 来嵌入表达式:

1
2
const name = 'kaz';
const el = <p>{name}</p>;

绑定子元素

这和 html 是一样的。

1
2
3
4
5
6
7

const el = (
<div>
<h1>标题</h1>
<p>内容</p>
</div>
);

除了这样写,还可以往元素的内容里传入另一个 React DOM 对象:

1
2
const title = <h1>标题</h1>;
const el = (<div>{title}</div>);

这时这个这个 React DOM 将作为 el 的子元素。

闭合标签

如果标签内没有内容,那么可以使用 /结尾

1
const el = <img src="https://api.r10086.com/%E9%A3%8E%E6%99%AF%E7%B3%BB%E5%88%976.php" />;

不渲染的子元素

false, null, undefined, 以及 true 是合法的子元素。但它们并不会被渲染:[1]

1
const el = <div>{null}</div>; //渲染出的 div 里面不会有任何东西

如果你想把这些内容输出到 UI 上,那么你得手动转换成字符串。

1
const el = <div>{String(null)}</div>;

绑定属性

介绍

如果只是一个普通字符串,那么这和 html 是一样

1
const el = <font color="red">hello world!</font>;

也可以通过大括号来传入 js 表达式:

1
2
const color = 'red';
const el = <font color={color}>hello world!</font>;

批量绑定属性

除了这种绑定方式,我们还可以通过剩余运算符来批量绑定:

1
2
const props = {color: 'red', size: 18};
const el = <font {...props}>hello world!</font>;

在批量绑定属性后,可以继续绑定属性,遇到相同的属性则会覆盖:

1
2
const props = {color: 'red', size: 18};
const el = <font {...props} color="blue">hello world!</font>;

但在批量绑定之前的属性则不会起作用:

1
2
const props = {color: 'red', size: 18};
const el = <font color="blue" {...props}>hello world!</font>;

使用原始 html 字符串

这是十分危险的行为,应该尽量避免

可以通过 dangerouslySetInnerHTML 属性传入一个原始的 html 字符串。当你想设置 dangerouslySetInnerHTML 时,需要向其传递包含 key 为 __html 的对象。

绑定事件

介绍

jsx 中的事件和 html 有一些不同:

  • React 事件的命名采用小驼峰式,而不是小写。例如 onClick
  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数
1
2
3
4
5
6

function handleClick()
{
console.log("clicked");
}
const el = <button onClick={handleClick}>hello world!</button>;

小心 this 指向

注意一点,事件处理函数是直接调用的,如果你使用了 this,这可能是一个雷:

1
2
3
4
5
6
7
8
9

let obj = {
clicked: false,
handleClick(){
console.log(this.clicked);
}
}

const el = <button onClick={obj.handleClick}>hello world!</button>;

这时候你点击按钮,发现控制台输出的是 undefined

这是因为,事件处理函数最终是通过 func() 这样调用的,它并没有挂载到某一个对象上。最终,它的指向会是 globalThis

为了避免这个问题,你应该使用 箭头函数,或者使用 bind 函数绑定 this 的指向。

1
2
3
4
5
6
let handleClick = () => {
console.log("clicked");
}

// or
handleClick = handleClick.bind(obj);

在类组件中,这种问题是很容易出现的。

一般的做法是,会在组件的构造函数中手动绑定 this

1
this.handleClick = this.handleClick.bind(this);

获取事件发生的对象

通常使用 e.target 来获取触发事件对象。但这有可能是绑定事件的内部元素。

1
2
3
4
<div onclick="console.log(event.target, event.currentTarget)">
<button>Click Me</button>
123456456
</div>

我们运行这个例子,当按钮点击的时候,event.target 便是按钮,而 event.currentTarget 则是绑定事件的元素。

这说明:

  • e.target 得到的是触发事件的 DOM, 但触发事件的 DOM 可能是子元素。
  • e.currentTarget 得到是触发事件时,事件绑定的 DOM

绑定样式

绑定 class

由于 class 是关键字,所以不能使用 class 来绑定样式,而使用 className 属性来绑定样式:

1
const el = <div className="active"></div>;

如果你用过 Vue 并且希望能够使用其中的数组语法、对象语法,classnames 这个包或许是不错的选择。

绑定 style

对于 style,只需要传递一个小驼峰对象即可:

1
const el = <div style={{color:'red', fontSize: 14, borderRadius: '50%'}}>一些内容</div>;

需要注意的是:

  • 对于大小,React 会自动补上 px 单位
  • 不会针对旧浏览器提供兼容性前缀。浏览器引擎前缀都应以大写字母开头,除了 ms。因此,WebkitTransition 首字母为 ”W”。

使用CSS模块

在前面一个搭建好的 React项目中,我们可以康康 App.js

src/App.js
1
2
3
import logo from './logo.svg';
import './App.css';
// ... 后面已省略

这里的第二行挺有趣的,直接 import 了一个 App.css

实际上,我们创建的 React 项目,在浏览器运行前会经历一个构建的过程,在这个过程中,会对各种类型的代码进行打包。

当我们使用 import 引入了一个 CSS 文件时,构建工具会自动将 CSS 文件加载到网页的 <style> 标签中。

至于这些操作具体是怎么完成的,将会在后面的 Webpack 相关文章中解释

但这也有一些缺点,这些类名都是全局的,一旦出现了什么相同的 class,很容易出现全局命名污染。

于是便有了CSS 模块。

要在一个 React 项目中使用 CSS 模块,需要保证 css 文件的后缀是 .module.css,否则它将会被当做全局 CSS 文件,而不是作为 CSS 模块。

我们先建立一个简单的 css 文件:

main.module.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.panel {
background: #FFF;
}

.panel .title {
font-size: 18px;
padding: 10px;
border-bottom: solid 1px #eee;
}

.post {
background: #fff;
}

.post .title {
font-size: 36px;

}

然后在 App.js引入这个文件:

src/App.js
1
2
3
import logo from './logo.svg';
import './App.css';
import mainCss from './main.module.css';

注意 CSS 模块的引入和前面的引入方法有些不一样。

然后在下面的 App 函数中加入这些:

src/App.js
1
2
3
4
5
6
7
8
9
10
      </header>
<div className={mainCss.panel}>
<div className={mainCss.title}>标题</div>
<div>内容</div>
</div>
<div className={mainCss.post}>
<div className={mainCss.title}>标题</div>
<div>内容</div>
</div>
</div>

接下来打开界面,我们就可以看到效果:

mark

打开浏览器控制台,我们可以看到页面增加的 CSS 样式:

mark

可以看到,构建工具会自动将 CSS 模块编译成带随机前后缀的全局 CSS 样式,进而确保样式在全局是唯一的。

我们再看看我们引入的 class,刚好是编译后的 class

mark

当一个 css 模块被引入时,它会被解析成一个 javascript 对象,其键是原来的属性名,其值是编译后的属性名。

如果一个 css 样式比较复杂,则属性名为最后一级的 css 选择器。

例如上面的 .post .title,对应的键就是 title

关于 CSS 模块更详细的说明,可以参考:

引入媒体资源

在浏览器中引入图片、视频等媒体资源是比较简单的。这里只说明在 React 项目中引入媒体资源的方法。

使用 import

要想在项目中引入一个媒体资源,只需要将资源 import 进来即可。

比若说我们的 App.js

src/App.js
1
2
3
import logo from './logo.svg';
import './App.css';
import mainCss from './main.module.css';

第一行就引用了图片资源,然后在后面使用了它:

1
<img src={logo} className="App-logo" alt="logo" />

但这似乎是多次一举,因为这种链接我可以直接写死在属性里:

1
<img src="./logo.svg" className="App-logo" alt="logo" />

但这样操作后,图片就打不开了:

这是因为,项目在构建的时候,图片、视频、脚本等等资源的位置会发生变化,它和 src 中的目录是不一样的。

例如,图片会最终放到 /images 目录下。但开发状态下,我们可能就直接放到 src 目录了。

此外,构建工具为了考虑性能,减小网络请求,通常会把非常小的图片直接使用 base64 编码,然后内嵌到网页中。这种情况下,images 目录就看不到相应的图片了。

不使用 import

如果不想通过 import 的方式来引入媒体资源,我们可以把它们移动到 public 目录下。

构建时,public 目录下的文件会被直接复制到项目打包后的文件夹下,路径啥的都不会变化。

在搭好的项目中, public 目录刚好有一个 logo512.png,我们可以修改 App.js 文件:

1
<img src="./logo512.png" className="App-logo" alt="logo" />

然后,图片就正常显示了。


  1. 深入 JSX ↩︎

  • 标题: React(2-1):JSX入门
  • 作者: ObjectKaz
  • 创建于: 2021-05-03 05:56:03
  • 更新于: 2023-05-25 17:10:50
  • 链接: https://www.objectkaz.cn/33d37bb27182.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。