# JSX核心基础、条件与列表渲染、事件与样式绑定

# 一、JSX简介

# 1、JSX是什么

JSX 是 JavaScript 语法扩展,可以让你在 JavaScript 文件中书写类似 HTML 的标签。

// 以下为一段 JS 代码 
function App() {
    // 函数的返回值就是一个JSX
    return <div title="标题">Hello React !!</div>
}

export default App

# 2、Babel 在线工具转换 JSX

浏览器默认无法识别JSX,因此在 React 项目内部借助了 Babel 编译器将 JSX 代码转换为了浏览器可以理解的函数调用。

打开 Babel 在线转换器 (opens new window),将下面JSX 转换成对应的函数

image-20260408154815712

  • 转换前的 jsx 代码
function App() {
    return <div title="标题">Hello React !!</div>
}

export default App
  • babel 转换后代码
import { jsx as _jsx } from "react/jsx-runtime";
//  App.tsx 根组件
function App() {
  /* jsx 被转换成了一个 _jsx函数 (React 内置了这个函数),
  	 第一个参数为  html标签名
  	 第二个参数是一个对象,用来设置html标签属性和子元素内容
  	 		title 为标签属性
  	 		children 属性值为子元素内容
  	  _jsx 函数调用后会返回 DOM  <div title="标题">Hello React !!</div>
  */
    
  return /*#__PURE__*/_jsx("div", {
    title: "\u6807\u9898",
    children: "Hello React !!"
  });
}
export default App;

# 3、React 中 JSX 转换分析

你可以执行 npm create vite@latest 命令,创建一个 React 项目,然后修改 App.jsx 文件 内容如下

//  App.tsx 根组件
function App() {
  return <div title="标题">Hello React !!</div>
}

export default App;

执行 npm run dev 运行项目后,在浏览器的控制台,可看到 App 组件中的 JSX 被转换成了一个 函数调用

image-20260408164021113

# 二、扩展内容:选学

# 3、手动配置 Babel 转换 JSX

本小节,我们通过手动配置Babel,用 Babel 来转换 JSX,看转换后是否是我们上面提到的效果。

演示步骤

  • 创建项目一个 文件夹jsx,然后在当前目录下执行npm init初始化package.json
npm init # 初始化package.json
  • 安装Babel转换JSX所需要的三个包
    • @babel/core babel的核心包
    • @babel/cli babel命令行工具
    • @babel/preset-react babel预设包,专门用于React应用程序,将React的JSX代码转换为纯JavaScript代码的过程中,确保代码的兼容性和正确性
npm install --save-dev @babel/core @babel/preset-react @babel/cli
  • babel.config.js文件中来配置 Babel,告诉它使用 @babel/preset-react 来转换JSX
module.exports = {  
    presets: ['@babel/preset-react']  
};
  • 创建App.jsx文件,文件内容如下
function App() {
    // 函数的返回值就是一个JSX
    return <div title="标题">Hello React !!</div>
}

export default App
  • 执行以下命令,Babel 将采用 React 预设来转换 JSX,然后将转换后的结果输出的 App.js 文件
npx babel App.jsx --out-file App.js

最终在当前目录下生成了 App.js,内容如下:

function App() {
    // 函数的返回值就是一个JSX
    return /*#__PURE__*/React.createElement("div", {
        title: "\u6807\u9898"  // 标题
    }, "Hello React !!");
}
export default App;

# 3、JSX 与 React的关系

通过上面转换后的代码,我们可以看到 JSX 在这里与 React 是强绑定的(转换后返回的是 React.createElement) 函数。

在最新的 React17 之后,JSX 和 React 是相互独立的,但它们经常一起使用。不过你可以单独使用其中一个。JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。

代码演示

  • 将上面代码中的babel.config.js中的配置修改成如下:
runtime 值 说明
automatic 会自动导入 JSX 编译后所需的函数
classic 不会自动导入任何内容

注意:在 Babel 8 中,默认运行时将切换为 automatic。我们现在演示的是 Babel 7 版本,所以需要单独配置 runtime 选项值

// babel.config.js  
module.exports = {  
    presets: [
        ['@babel/preset-react',{
            "runtime": "automatic"
        }]
    ]  
};
  • 然后重新执行以下命令来转换JSX
npx babel App.jsx --out-file App.js
  • 最终转换后生成的 App.js 文件内容如下
// 由编译器插入(不需要自己导入)
import { jsx as _jsx } from "react/jsx-runtime";

function App() {
    // 函数的返回值就是一个JSX
    return /*#__PURE__*/_jsx("div", {
        title: "\u6807\u9898",
        children: "Hello React !!"
    });
}
export default App;

通过编译后的代码,我们可以看到此时生成的 JS 代码中,并没有用到 React 这个对象。也就是说,你可以在任何地方使用 Babel 来转换 JSX(但仍记得要安装react,因为需要用到 react/jsx-runtime 这个文件)

# 三、JSX 书写规则

JSX 是 JavaScript 的语法扩展,它允许你在 JavaScript 文件中写类似 HTML 的标记。虽然 JSX 看起来很像 HTML,但在实际编写时有自己的一套规则。掌握这些规则,可以避免常见的错误,让代码更规范、更易维护。

以下是 JSX 书写时必须遵守的 10 条核心规则。

  • 只能返回一个根元素
  • 标签必须正确闭合
  • 属性命名大部分使用驼峰式命名法
  • 了解特殊属性名的处理
  • JSX 中注释书写规范
  • JSX 中通过大括号使用 JS
  • 了解 哪些值不会被渲染到页面
  • JSX 的哪些地方可以使用大括号
  • 变量中保存 JSX

# 1、只能返回一个根元素

  • 如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来
function App(){
    return <div>
        <h3>只能返回一个根元素</h3>
        <div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
    </div>
}
export default App
  • 如果不想多添加一层嵌套,可以使用<><> 将标签包裹起来
function App(){
    return <>
    <h3>只能返回一个根元素</h3>
    <div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
    </>
}
export default App
  • 你可以把<><>看作是<Fragment></Fragment>内置标签的简写形式,在某些特殊情况下不能采用简写,只能采用<Fragment></Fragment>写法。(往后会讲)
// Fragment是React提供的内置标签,记得一定要导入
import { Fragment } from "react"

function App(){
    return <Fragment>
        <h3>只能返回一个根元素</h3>
        <div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
    </Fragment>
}
export default App
  • 默认情况下,紧跟在 return 后面的标签不能换行,如果想要换行,可以使用()小括号包裹标签。

为了代码可读性,建议使用小括号包裹 JSX

import { Fragment } from "react"
function App(){
    return (
        <>
        <h3>只能返回一个根元素</h3>
        <div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
        </> 
    ) 

}
export default App

# 2、标签必须正确闭合

  • JSX 要求标签必须正确闭合。
  • <img> 这样的自闭合标签必须书写成 <img />
  • <div>这样成对出现的标签,必须写成<div> xxx </div>
function App(){
    return (
        <>
        <img src="/vite.svg" />
        <div>标签必须闭合</div>
        </>
    ) 
}
export default App

# 3、属性命名大部分使用驼峰式命名法

  • 大部分标签属性名,要采用驼峰写法
  • 自定义属性不需要采用驼峰,保留data-xx写法
function App(){
    return (
        <img 
            src='/vite.svg'
            alt='vite log' 
            onClick={()=>alert('我被点了')}   
            data-size="30" />
    ) 

}
export default App

渲染后的真实DOM

<img src="/vite.svg" alt="vite log" data-size="30">

# 4、特殊属性名处理

  • 在JSX中 class 和 for 是关键字,所以class要用 className 代替,for 要用 htmlFor 代替
function App(){
    return (
        <div className="box" >
            <label htmlFor="username">用户名</label>
            <input type='text' id='username' />
        </div>
    ) 

}
export default App

渲染后的真实DOM

<div class="box">
    <label for="username">用户名</label>
    <input type="text" id="username">
</div>

# 6 、大括号内书写JS表达式

  • 在JSX中,可以通过在{}内来书写单一的 JS 表达式,也就是一段能够被求值的 JavaScript 代码。
  • 判断是否为单一JS表达式,最简单的方法是,是否可以合法地写在 return 后面

注意:在 JSX 的{}大括号中,直接放一个对象 函数,会报错,报错

function App() {
    const text = "hello React";
    const fn = () => 10;
    const a = 1;
    const b = 2;
    const obj = {
        attr: "对象属性值",
    };
    return (
        <div>
            {/* text是一个变量 */}
            <p> {text}</p>
            {/* fn() 是函数调用 */}
            <p>{fn()}</p>
            {/* a+b 数学运算 */}
            <p>{a + b}</p>
            {/* 三元表达式 */}
            <p>{a > b ? "YES" : "NO"}</p>
            {/* 数组操作 */}
            <p>{[1, 2, 3].join(",")}</p>
            {/* jsx也是一个表达式 */}
            <div>{<p>{obj.attr}</p>}</div>
        </div>
    );
}
export default App;

以上代码渲染后的DOM

<div>
    <p> hello React</p>
    <p>10</p>
    <p>3</p>
    <p>NO</p>
    <p>1,2,3</p>
    <div>
        <p>对象属性值</p>
    </div>
</div>

以下写法,不是表达式,是属于JS语句,不能出现在{ }

if(){}
function
for(var i=0;i<5;i++){}
switch(){ }

# 7、JSX 注释

JSX 中注释要写在 { } 大括号中

function App(){
    return (
        <div className="box" >
            {/* htmlfor 等同于 for 属性  */}
            <label htmlFor="username">用户名</label>
            <input type='text' id='username' />
        </div>
    ) 
}
export default App

# 8、哪些值不会被渲染到页面

  • 在 JSX 的{}大括号中,truefalseundefinednull 4个值是不会被渲染到页面上。
  • 对于以上不渲染的值想进行输出,可将其与 '' 空字符串拼接或 JSON.stringify() 成字符串输出
const App=()=>{

   return (
    <>
        <div>{true}</div>
        <div>{false}</div>
        <div>{undefined}</div>
        <div>{null}</div>
        <hr/>
        <div>{true+''}</div>
        <div>{false+''}</div>
        <div>{undefined+''}</div>
        <div>{null+''}</div>
    </>
    
   )
}

export default App

最终渲染结果如下:

<div id="root">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <hr>
    <div>true</div>
    <div>false</div>
    <div>undefined</div>
    <div>null</div>
</div>

# 9、JSX 的哪些地方可以使用大括号

在 JSX 中,只能在以下两种场景中使用大括号

  • 在 JSX 标签体中,用来渲染标签体内容

  • 在 JSX 标签属性 = 符后面,用来渲染标签属性值

function App(){
    const href="https://www.icodingedu.com"
    const title="艾编程"

    return (
        // 标签属性 =  号符后面
        <a href={href} title={title}>
            {/* 标签体中 */}
            {title}
        </a>
    )
}
export default App

# 10、变量中保存 JSX

function App(){
    const value="请输入用户名"
    { /* myInput 中保存的是一段 JSX */ }
    const myInput=<input type='text' value={value}/>
          return (
              <div>
                  {myInput}
              </div>  
          )
}
export default App

最终渲染后DOM

<div>
    <input type="text" value="请输入用户名">
</div>

# 11、特殊的 dangerouslySetInnerHTML 属性

dangerouslySetInnerHTML 是 React 提供的一个特殊属性,用于将字符串中的 HTML 内容解析为真实的 DOM 元素。

# 为什么需要它


React 默认会对所有动态内容进行转义,防止 XSS 攻击。所以字符串中的 HTML 内容不会被解析为真实 DOM 元素,而是显示为纯文本。

const userInput = '<strong>加粗文本</strong>';
// userInput 转义后的  &lt;strong&gt;加粗文本&lt;/strong&gt;
<div>{userInput}</div>  

渲染到页面效果

image-20260413175318775

当你需要将字符串中的 HTML 内容解析为真实 DOM 元素时,就必须使用 dangerouslySetInnerHTML 属性。

const userInput = '<strong>加粗文本</strong>';
<div dangerouslySetInnerHTML={{ __html: userInput }}></div>

image-20260413175445722

重要提示:为了防止 XSS 攻击,你在编写代码时,一定要确保传入给 dangerouslySetInnerHTML 的内容一定是安全的。

# 什么是 XSS ?

XSS(Cross-Site Scripting,跨站脚本攻击) 是一种安全漏洞,攻击者通过在目标网站中注入恶意脚本,当其他用户访问该网站时,这些脚本会在用户浏览器中执行。

简单理解:攻击者让别人的网站执行了自己写的恶意代码。

示例:在评论中输入恶意内容

export default function App() {
    // 假设这里的内容是用户在评论区输入框中输入的内容
    const imgstr = `<img src="/favicon.svg" onClick="alert('cookie被偷了${document.cookie}')" />`;
    return (
        <>
        <textarea value={imgstr}></textarea>
        {/* 显示用户输入的评论 */}
        <div dangerouslySetInnerHTML={{ __html: imgstr }}></div>
        </>
    );
}

上面示例中,当用户点击图标时,就可以获取到用户的 cookie 。

如果恶意者在图片的点击事件中,通过请求将你的 cookie 发送到他的服务器上,他就拿到了你的 cookie 身份。

# 12、HTML 转换为 JSX

如果你是老项目进行改造,需要将原来大量的 HTML 代码转换为 JSX,可以借助JSX在线转换器 (opens new window)

你可以通过点击放大图片来查看

image-20240408163728968

转换前的HTML代码

<!--这里的注释会被转换-->
<h3>HTML 转换成 JSX </h3>
<div>这里有多个元素,所以转换后会被添加一个父标签<> 包裹</div>
<!--自定义属性第二个单词不用首字母大写-->
<img src="" data-Size="100px">

转换后的JSX代码

  • 将所有 html 包裹在 <> <>
  • 注释做了转换
  • img 标签后面加了/ ,实现标签闭合
<>
{/*这里的注释会被转换*/}
<h3>HTML 转换成 JSX </h3>
<div>这里有多个元素,所以转换后会被添加一个父标签&lt;&gt; 包裹</div>
{/*自定义属性第二个单词不用首字母大写*/}
<img src="" data-size="100px" />
</>

# 四、条件渲染

通常我们需要根据不同的情况显示不同的内容。在React中,你可以通过使用 JavaScript 的 if 语句&&?: 三目运算符来选择性地渲染 JSX

# 1、if 条件渲染

根据 if 的判断条件,组件返回不同的 JSX

const App=()=>{
    const isShow=false
    if(isShow){
        return <div>条件为 true 渲染的内容</div>
    }else{
        return <div>条件为 false 渲染的内容</div>; 
    }
}

export default App

根据if的判断条件,给变量赋不同的JSX内容

const App=()=>{
    // 用于判断的条件 0 未开始  1 正在进行中  2 已完成
   let isDone=1;
   // 保存渲染的JSX
   let item;
   if(isDone===0){
        item=<div>未开始</div>
   }else if(isDone===1){
        item=<div>正在进行中..</div>
   }else{
        item=<div>已完成</div>
   }
    
   // 组件返回值
   return item
}

export default App

# 2、switch条件渲染(用得少)

根据switch的判断条件,返回不同的 JSX 作为变量值

const App=()=>{
    // 用于判断的条件 0 未开始  1 正在进行中  2 已完成
    let isDone=1;
    // 保存渲染的JSX
    let item;
    switch(isDone){
        case 0:
            item=<div>未开始</div>
            break
        case 1:
            item=<div>正在进行中..</div>
            break
        case 2:
            item=<div>已完成</div>
            break
    }

    // 组件返回值
    return item
}

export default App

# 3、&& 运算符

当条件满足时返回 JSX,不满足时不返回任何内容,可以用&&运算符

const App=()=>{
    const isShow=false
    return (
        <div>
            { isShow && <div>为真显示</div> }
        </div>
    )
}

export default App

# 4、?: 三目运算符

当条件成立与不成立时,分别显示不同的JSX内容

const App=()=>{
    const isShow=false
    return (
        <div>
            { isShow? <div>为真显示</div>:<div>为假显示</div> }
        </div>
    )
}

export default App

你还可以这样实现

const App=()=>{
    const isShow=false
    return (
        <div>
            <div>{isShow?"为真显示":"为假显示"}</div>
        </div>
    )
}

export default App

# 五、列表渲染

本小节我们将学习在 React 中,如何将一个数组渲染成一个列表。

你将会学习到

  • JSX 中渲染数组的默认行为
  • for 或 while 循环将数组渲染成列表
  • 用数组的 map 方法渲染列表(推荐
  • 列表筛选(推荐
  • 用 key 保持列表项的顺序

# 1、JSX 中渲染数组的默认行为

JSX 中渲染数组时,React 是直接遍历数组,把每个元素当作 React 节点 去渲染

错误理解:很多人认为 React 渲染数组时,默认调的是 join("")

const App = () => {
    const text = ["arry", "晓冉", "清心"];
    // 变量中可以直接放 jsx
    const list = [<li>arry</li>, <li>晓冉</li>, <li>清心</li>];
    return (
        <div>
            <p>{text}</p>
            <ul>{list}</ul>
        </div>
    );
};
export default App

渲染后的DOM

<div>
    <p>arry晓冉清心</p>
    <ul>
        <li>arry</li>
        <li>晓冉</li>
        <li>清心</li>
    </ul>
</div>

# 2、for 或 while 循环将数组渲染成列表(不推荐)

利用 for 或 while 循环,将数组中的每一项转换成一个JSX,添加到一个新数组,再渲染这个新数组

const App=()=>{
    const list=["arry","晓冉","清心"]
    const listItems=[];
    
    // for循环遍历后,新数组listItems的值 [<li>arry</li>,<li>晓冉</li>,<li>清心</li>]
    for(let i=0;i<list.length;i++){
        listItems.push(<li>{list[i]}</li>)
    }
	
    // 在 jsx 中渲染新数组 listItems 时,会把数组中每个元素当作 React 节点去渲染
    return <ul>{listItems}</ul>
}

export default App

渲染后的DOM

<ul>
    <li>arry</li>
    <li>晓冉</li>
    <li>清心</li>
</ul>

# 3、map 方法将数组渲染成列表(推荐)

数组的 map 方法将返回一个新数组,新数组中的每个成员是由原数组中每个成员转换后的 JSX

const App=()=>{
    const list=["arry","晓冉","清心"]
    /* map方法返回的新数组: [<li>arry</li>,<li>晓冉</li>,<li>清心</li>]
       然后 React 将遍历数组,将每一项当作 React 节点渲染  */
    const newList=list.map((item,index)=><li>{item}</li>)
    return <ul>{newList}</ul>
}

export default App

或以下写法

const App = () => {
  const list = ["arry", "晓冉", "清心"];
  return (
    <ul>
          {/* 换行时,要用 () 包裹返回值 */}
      {list.map((item, index) => (
        <li>{item}</li>
      ))}
    </ul>
  );
};

export default App;

渲染后的DOM

<ul>
    <li>arry</li>
    <li>晓冉</li>
    <li>清心</li>
</ul>

# 4、列表筛选

如果我们只想将数组中的部分符合条件的数据渲染成列表,我们可以分以下两步来实现

  • 先调用数组的 filter 方法,筛选出所需的数组项
  • 再用筛选过后的数组调用 map 方法,将数组渲染成列表

示例:筛选出价格大于 10 块的水果

const App=()=>{
    const fruits=[
        {
            id:1,
            name:'蓝莓',
            price:10
        },
        {
            id:2,
            name:'苹果',
            price:5
        },
        {
            id:3,
            name:'芒果',
            price:7
        },
        {
            id:4,
            name:'草莓',
            price:15
        },
        {
            id:5,
            name:'香蕉',
            price:4
        }
    ]

    // 筛选出价格大于10元的水果
    const filterFruits= fruits.filter(item=>item.price>=10);

    // 返回一个新数组,数组中每一项为一个JSX
    const fruitItems=filterFruits.map(item=><li>{item.name}-{item.price}</li>)

    // 渲染新数组 fruitItems
    return <ul>{fruitItems}</ul>
}

export default App

你还可以向下面这样连着写

const App=()=>{
    // ......省略部分代码

    // 组件返回值
    return <ul>{
            fruits.filter(item=>item.price>=10)
                .map(item=><li>{item.name}-{item.price}</li>)
        }
    </ul>
}

export default App

# 5、用 key 保持列表项的顺序

在 React 中,如果你按下面方式渲染列表(不在 li 上添加 key 属性),React 会警告

const App = () => {
    const fruit = [
        { id: 1, text: "苹果" },
        { id: 2, text: "香蕉" },
        { id: 3, text: "橙子" },
    ];
    return (
        <ul>
            {fruit.map((item) => (
                <li>{item.text}</li>
            ))}
        </ul>
    );
};

export default App;

image-20260408181734224

这是因为你必须给数组中的每一项都指定一个 key——它可以是字符串或数字的形式,只要能唯一标识出各个数组项就行。

key 属性帮助 React 识别列表中的每一个元素,从而决定哪些节点可以复用、哪些需要更新或移动,避免状态错位和性能问题。

key 属性只是用来标识列表项的唯一性,不会出现在最终的 HTML 结构里。

代码示例:列表添加 key 属性

<ul>
    {fruit.map((item) => (
        {/* 给列表项 添加唯 一的 key 后,控制台就不报错了 */}
        <li key={item.id}>{item.text}</li>
    ))}
</ul>

# key 需要满足的条件

  • key 值在兄弟节点之间必须是唯一的。 不过不要求全局唯一,在不同的数组中可以使用相同的 key。
  • key 值不能改变,否则就失去了使用 key 的意义!所以千万不要在渲染时动态地生成 key。
  • 数组索引 不要作为 key 属性值。
    • 如果后续不会对数组数据做任何操作,就不会有影响。
    • 如果后期对数组做 增 ,删 操作时,就会改变每项的索引,key 值就改变,并不能保证唯一。
// ❌ 不推写法 : 利用数组索引作为 key 属性值
<ul>
    {fruit.map((item, index) => (
        <li key={index}>{item.text}</li>
    ))}
</ul>

温馨提示

  • 为什么 key 在兄弟节点之间要唯一,且不能改变 ?

针对上面1个疑问,我们将在 如何设置列表 key 值 这里会详细讲解。

# 六、事件绑定

本小节我们将学习,如何在 JSX 中给原生的 HTML 标签添加 DOM 事件。

你将会学习到:

  • 如何给 HTML 标签添加DOM事件
  • 事件处理函数如何传参
  • 事件处理函数中如何获取event事件对象
  • 事件传播

# 1、如何给 HTML 标签添加 DOM 事件

给 HTML 标签添加 DOM 事件时,需要:

  • 使用以 on 开头、后跟事件名的属性,事件名每个单词首字母大写 例如:onClickonMouseOveronKeyDown
  • 属性值是一个事件处理函数(推荐函数名以 handle 开头,后跟事件名称,便于识别)
const App=()=>{
    
    /* handleClick 为事件处理函数 */
    const handleClick=()=>{
        alert('我被点击了')
    }

    /* 为div元素添加了Click点击事件,并添加事件处理函数handleClick,当元素被点击时,就会调用handleClick函数 */
    return <div onClick={handleClick}>点我呀!</div>
}
export default App

重要注意事项

事件处理函数不要加括号

onClick={handleClick}onClick={handleClick()} ❌(会在渲染时立即执行,而非等待点击)

# 捕获阶段触发事件

DOM 事件流程分为三个阶段:

  1. 捕获阶段:从根节点向下传递到目标元素
  2. 目标阶段:事件到达目标元素
  3. 冒泡阶段:从目标元素向上冒泡回根节点

默认情况下,onClick 等事件是在冒泡阶段触发。

如果需要在捕获阶段触发,可以使用 on + 事件名(首字母大写) + Capture 的格式。

const App = () => {
    const handleClickCapture = () => {
        alert('捕获阶段触发:我被点击了');
    };

    return <div onClickCapture={handleClickCapture}>点我呀(捕获阶段)</div>;
};

# 常见事件对照表

更多事件绑定查阅 React 官网:https://zh-hans.react.dev/reference/react-dom/components/common

冒泡阶段(默认) 捕获阶段
onClick onClickCapture
onMouseOver onMouseOverCapture
onKeyDown onKeyDownCapture
onFocus onFocusCapture
onBlur onBlurCapture

# 2、事件处理函数如何传参

如果我们给事件处理函数传递参数,推荐采用以下利用箭头函数写法

const App=()=>{
    const username="清心"
    // ts写法,参数加了类型标注
    const handleClick=(username)=>{
        alert(`${username},你好呀!!`)
    }
    // 箭头函数返回元素被点击后要调用的handleClick函数
    return <div onClick={()=>handleClick(username)}>
        点击我
    </div>
}
export default App

# 3、事件处理函数中获取 event 事件对象

默认情况下,事件处理函数接受一个事件对象作为唯一的参数。它通常被称为 e (代表 “event” 事件)。你可以使用此对象来读取有关事件的信息。

注意event 并不是原生的事件对象,而是 React 合成事件对象,它是对原生事件的跨浏览器封装,具有与原生事件相同的接口。

  • 事件处理函数不传参情况,则事件处理函数的第一个参数默认为 event
const App = () => {
    //  e为事件对象  添加了ts类型标注
    const handleClick = (e) => {
        // e.target 获取被点击的元素:<div>点击我 </div>
        console.log(e.target);
        alert(`清心,你好呀!!`);
    };
    // 添加事件
    return <div onClick={handleClick}> 点击我 </div>;
};
export default App;
  • 如果需要向事件处理函数传递额外参数,不能直接写 onClick={handleClick(username)}(这样会在渲染时立即执行),而是要通过箭头函数将事件对象 e 作为第一个参数显式传递。如:(e) => handler(e, 额外参数)
const App=()=>{
    const username="清心"
    //  e为事件对象  添加了ts类型标注
    const handleClick=(e,username)=>{
        // e.target获取被点击的元素 : <div>点击我 </div>
        console.log(e.target)
        alert(`${username},你好呀!!`)
    }

    // 事件处理函数的第一个参数默认为事件对象
    return <div onClick={(e)=>handleClick(e,username)}>点击我 </div>
}
export default App

# 4、事件传播

在 React 中所有原生事件都会传播,除了 onScroll。在某些情况下,我们可能需要阻止事件冒泡或阻止事件的默认行为。

# 4.1、阻止事件冒泡

e.stopPropagation()  // e为事件对象

以下代码中,我们在 span 元素的事件处理函数中添加了e.stopPropagation()阻止了事件冒泡。所以当我们点击 h3 元素时,并不会触发外层 div 元素上的点击事件。

const App = () => {
  const handleClick1 = (e) => {
    // 绑定事件元素的标签名
    alert(e.currentTarget.tagName);
  };

  const handleClick2 = (e) => {
    // 阻止事件冒泡
    e.stopPropagation();
    // 触发事件元素的标签名
    alert(e.target.tagName);
  };
  return (
    <div onClick={handleClick1}>
      div标签
      <h3 onClick={handleClick2}>h3标签</h3>
    </div>
  );
};

export default App;

GIF2026-4-919-10-31

如果将 e.stopPropagation() 代码注释掉,你会发现,点击 h3 元素时,h3 元素和 div 元素绑定的事件处理函数都会被调用。

GIF2026-4-919-16-21

# 4.2、阻止事件的默认行为

e.preventDefault()  // e为事件对象

以下代码中,我们添加了e.preventDefault()代码,阻止了 a 标签点击默认会跳转到新页面的行为。所以点击 a 标签时,并不会发生页面的跳转。

const App = () => {
  const handleClick = (e) => {
    e.preventDefault();
  };
  return (
    <a href="http://www.icodingedu.com" onClick={handleClick}>
      点击无法跳转
    </a>
  );
};
export default App;

# 七、样式绑定

本小节我们学习在 React 中,如何给元素添加样式

你将会学习到:

  • 如何添加行内样式
  • 如何添加全局样式
  • 如何添加局部样式 -CSS 模块化
  • sass 预处理器

# 1、行内样式

  • JSX 中标签的 style 属性可以为元素添加行内样式。

  • 行内样式值书写为一个对象,对象的属性名为 css 属性名,但属性名必须采用驼峰命名

function App() {
    /* 通过变量定义css样式 */
    const styleObj = {
        fontSize: "20px",
        backgroundColor: "skyblue",
        margin: 0,
    };

    return (
        <div>
            <h3 style={styleObj}>h3 标题内容</h3>
            <p
                style={{
                    color: "red",
                }}
                >
                p 标签描述内容
            </p>
        </div>
    );
}
export default App;

渲染后DOM如下:

<div>
    <h3 style="font-size: 20px; background-color: skyblue; margin: 0px;">h3 标题内容</h3>
    <p style="color: red;"> p 标签描述内容 </p>
</div>

# 2、全局样式

通过 import './xx.css' 导入的 CSS 样式默认是全局样式,它会作用于整个项目。

import "./App.css"
const App=()=>{
    // box类名在App.css文件中有声明
    return <div className="box"></div>
}
export default App

App.css文件内容如下

.box {
    width: 100px;
    height: 100px;
    background-color: skyblue;
}

渲染后效果如下:

image-20260409200645625

# 3、局部样式-CSS模块化

如果希望某些 CSS 样式只作用于当前组件,而不影响其他组件,可以采用 CSS 模块化的方式来引入样式。在使用 Vite 构建项目时,对于以 .module.css 结尾的文件,Vite 会将其作为 CSS 模块进行处理,从而实现样式的局部作用域。

Vite 官方教程:CSS 模块化 (opens new window)

使用 CSS 模块化的具体步骤

  • 第一步:在src 目录下新建 App.module.css 文件,并编写样式
.box {
    width: 100px;
    height: 100px;
    background-color: skyblue;
}
  • 第二步:在组件文件中使用 import 导入该 CSS 模块
import Styles from './App.module.css'
  • 第三步:通过 Styles.类名 的方式获取对应的 CSS 类名,并赋值给 className
<!-- 填加单个classname-->
<div className={Styles.box}></div>

完整代码示例

import Styles from './App.module.css'
const App=()=>{
    return <div className={Styles.box}></div>
}
export default App

最终渲染效果如下:

image-20260409202351984

通过模块化方式引入的 CSS 类名,最终会被加上一段 hash 值,这样就能达到唯一性。不会影响到其它组件的 css 样式。

# 3.1、模块化 CSS 中声明全局样式

如果你想在.module.css 文件中定义全局 CSS 样式,可以借助 :global 伪类来实现。

  • App.module.css 文件
/* 局部样式 */
.box {
    width: 100px;
    height: 100px;
    background-color: skyblue;
}

/* 全局样式 */
:global(.font-color-red) {
  color: red;
}
  • App.tsx 文件
import Styles from './App.module.css'
const App=()=>{
    return <div className={Styles.box}>
        <div className='font-color-red'>全局样式控制字体变红</div>
    </div>
}
export default App

最终渲染后的效果如下:

image-20260409202828767

# 3.2、CSS 模块化中: CSS 类名编写规则

当我们以模块化方式加 CSS 时,CSS的类名最好以驼峰命名,否则在调用时就必须以方括号形式调用。

App.module.css

/* 必须以 Styles['my-box'] 方式调用 */
.my-box {
    width: 100px;
    height: 100px;
    background-color: skyblue;
}

/* 可以以 Styles.myTitle方式调用   推荐驼峰命名,因为使用方便  */
.myTitle {
    background-color: khaki;
    margin: 0;
    text-align: center;
}

代码示例

import Styles from './App.module.css'
const App=()=>{
    // 不能以Styles.myBox和Styles.my-box形式调用,只能Styles['my-box']
   return <div className={Styles['my-box']}>
        <p className={Styles.myTitle}>标题</p>
   </div>
}
export default App

# 3.3、CSS 模块化配置

如果想在使用 CSS 模块,css 样式以xxx-aaa形式书写,而在调用时可以Styles.xxxAaa形式调用,可以在vite.config.js文件中添加如下配置。

更多模块化配置,参考 Vite 官网的 css.module (opens new window)

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [react()],
    css: {
        modules: {
            // 如何转换CSS类,'camelCase'意味着所有CSS类名都将被转换为驼峰命名法(camelCase)。例如.my-box转换为myBox
            localsConvention: 'camelCase',
        },
    },
});

添加了以上配置后,我们在JSX中就可以通过Styles.myBox来应用App.module.css中声明的.my-box样式

import Styles from './App.module.css'
const App=()=>{
 // 在Vite.config.js中添加上面提到的配置后,就可以通过Styles.myBox形式调用
    return <div className={Styles.myBox}>
        <p className={Styles.myTitle}>标题</p>
    </div>
}
export default App

# 4、CSS 预处理器 Sass

Vite 官方教程:CSS 预处理器 (opens new window)

如果我们想在 React 中使用 Sass 来编写 CSS,需要按以下步骤来操作

  • 第一步:安装 sass

    sass 为纯 JS版本,编译大型项目时相对较慢。

    sass-embedded 为嵌入式版,在处理大型、复杂的 Sass 文件和频繁编译时更快。

npm i -D sass-embedded # 安装sass 
# 或
npm i -D sass  # 安装 sass 

安装好后,可执行 npm list sass 查看当前项目安装的 sass 版本。

  • 第二步:创建xxx.scssxxx.module.scss文件,可以以 sass 语法编写 css
// App.scss   与 App.module.scss 文件内容相内
.my-box{
    width:100px;
    height:100px;
    background-color:skyblue;
    .my-title{
        font-size:16px;
        background-color:khaki;
    }
}
  • 第三步:使用 import 关键词导入.css,然后在 JSX 中使用对应的 css 类名
import './App.scss'
const App=()=>{
    return <div className='my-box'>
        <div className='my-title'>标题</div>
    </div>
}
export default App

渲染后效果如下:

image-20240409182019032

如果导入的是.module.scss文件,则以模块化方式调用类名

import Styles from './App.module.scss'
const App=()=>{
    return <div className={Styles.myBox}>
        <div className={Styles.myTitle}>标题</div>
    </div>
}
export default App

渲染后效果如下:

image-20240409181922709

# 八、动态操作元素 class 类名

动态操作元素 class 类名需要与 state 状态一起配合完成,所以这块内容,放在学完 useState 组件状态之后讲。

点击学习:动态操作元素 class 类名

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X