主题
行内样式处理与class样式处理(Day2 2/4)
本页对应 React + TypeScript + 医通项目,共 6 段视频,约 35 分钟。
学习目标
- 能正确在 JSX 中为元素绑定
onClick,传入函数名或函数引用而非函数调用,并说明事件回调的调用者及严格模式下this的指向。 - 能在不传普通参数时通过第一个形参接收 React 合成事件对象,调用
preventDefault()阻止默认行为,并通过nativeEvent获取原生事件对象。 - 能在需要传参时使用箭头函数包裹事件处理函数并正确传入普通参数;需要同时使用事件对象和普通参数时,能用箭头函数形参接收事件对象并传入目标函数。
- 能绑定
onChange事件并读取e.target.value,能说明 React 中onChange的触发时机及其与原生change事件的差异。 - 能识别并修正 JSX 标签语法错误,确保标签成对出现或自闭合;能使用行内样式对象、
className,并用map渲染列表且为列表项设置key。 - 能完成点击内容区域弹出发表时间的案例,并在嵌套
A标签场景中通过事件对象调用preventDefault()处理默认跳转导致的刷新问题。
知识点
先明确:style 的值必须是对象
为什么需要:在 HTML 里我们习惯写 style="color:red",但 JSX 里写的不是 HTML 字符串,而是 React 元素属性。转写稿里也提醒过:JSX 里写的是 React 元素,不是 HTML;同理,style 不能照搬 HTML 的字符串写法。
怎么想:既然 JSX 属性值可以通过 {} 放表达式,那么 style 就应当接收一个 JavaScript 对象,由 React 把这个对象转换成行内样式。
怎么做:外层 {} 表示 JSX 表达式,内层 {} 表示对象字面量,所以常见写法是 style={{ color: 'red' }}。
易错点:不要写成 style="color:red";也不要把 style 当成普通字符串拼接。若写成对象变量,应使用 style={style},不要写成 style={{ style }},否则会变成嵌套对象。
jsx
const root = ReactDOM.createRoot(document.querySelector("#root"));
let style = {
color: 'red',
border: '1px solid blue',
backgroundColor: 'pink',
width: '200px',
height: 500,
fontSize: 30
}
root.render((
<div>
<h3>行内样式style处理</h3>
<div style={{ color: 'red', border: '1px solid blue', backgroundColor: 'pink', width: '200px', height: 500, fontSize: 30 }}>红色文字,蓝色边框,粉色背景</div>
<div style={style}>我是div</div>
</div>
))复合属性要使用小驼峰命名
为什么需要:CSS 里常写 background-color、font-size 这类带连字符的复合属性,但 JavaScript 对象的键名通常采用驼峰形式。
怎么想:React 的行内样式对象不是 CSS 文本,而是 JS 对象,所以要把 background-color 转成 backgroundColor,把 font-size 转成 fontSize。
怎么做:笔记中的 backgroundColor: 'pink'、fontSize: 30 就是正确写法。
易错点:写成 background-color 容易报错或不生效;大小写也要注意,backgroundColor 不是 BackgroundColor。转写稿里常把“插值表达式”说成“差值表达式”,这里也要纠正:JSX 的 {} 是插值/表达式容器,不是“差值”。
px 单位可以省略,直接写数字
为什么需要:行内样式里大量长度值以像素为单位,如果每次都写 '200px'、'30px',字符串拼接会比较啰嗦。
怎么想:React 对常见的像素长度属性支持数值写法,数值会按像素处理;如果单位不是 px,仍应写成带单位的字符串。
怎么做:height: 500、fontSize: 30 可以直接写数字;width: '200px' 也保留字符串写法,二者在本节示例中都能表达像素。
易错点:不要把“px 可省略”理解成所有 CSS 属性都能随便写数字;像 border: '1px solid blue' 这种复合值仍要写完整字符串。需要其他单位时,也不要省略单位。
复用样式对象:把对象提取出来
为什么需要:当多个元素使用相同样式时,如果每个标签里都重复写一大段对象,代码会臃肿,也不利于修改。
怎么想:先把样式整理成普通 JS 对象,再通过 style={style} 传给元素;JSX 属性值用 {} 接收变量。
怎么做:笔记中先定义 let style = {...},再写 <div style={style}>我是div</div>。
易错点:变量名和属性名相同没有问题,style={style} 表示“把变量 style 的值交给 style 属性”;但不要误写成 style={ {style} }。另外,对象里的属性名仍要遵守驼峰规则。
排查思路:先回到“React 元素属性”
为什么需要:行内样式不生效时,初学者容易继续按 HTML 思路改字符串。转写稿视频5强调 JSX 标签是 React 元素,这个提醒同样适用于 style 属性。
怎么想:先检查三件事:值是不是对象、复合属性是不是小驼峰、px 数值是否按规则书写。
怎么做:按“对象 → 驼峰 → 单位”的顺序排查;若是复用对象,再检查是否把对象正确放进 {}。
易错点:本节转写稿主要讲事件回调,没有给出行内样式的实际控制台调试过程,因此这里不编造调试现象;只依据笔记结论做检查。不要把事件回调里的 this、事件对象等内容硬套到样式处理上。
先跑起来:本节要解决的问题
前面的练习里,类名都是写死的:<div className="box">。但真实项目里,类名经常要“拼”出来——比如根据状态加 c1、加 f1,或者类名数量不固定。这时候就得把 JS 里的变量交给 className。本节要回答两件事:className 的值到底是什么类型,以及当它是数组时该怎么处理。
先看完整示例,边写边刷新看效果:
jsx
const root = ReactDOM.createRoot(document.querySelector("#root"));
let class1 = "box c1 f1"
let class2 = ['box','c1','f1'];
root.render((
<div>
<h3>样式类名</h3>
<div className="box">
box1
</div>
<div className="box c1 f1">box2</div>
<h3>插值表达式处理样式</h3>
<div className={class1}>box3</div>
<h3>样式是一个数组</h3>
<div className={class2}>box4</div>
<div className={class2.join(' ')}>box5</div>
</div>
))className 是字符串:多个类名用空格隔开
为什么需要:一个元素常常要同时挂多个类,比如 box 管尺寸、c1 管颜色、f1 管字体。
怎么想:浏览器解析 className 时,是拿空格把整串切开,切出来的每一段当一个类名。所以“多个类名”在字符串层面的表达方式,就是中间夹空格。
怎么做:像 box2 那样直接写 className="box c1 f1";要复用就先把这串字符串存进变量 class1,再交给 className。
易错点:写成 "box,c1,f1" 或者 "boxc1f1" 都不会报错,但类名对不上,样式静默失效——这类错误最难查,因为控制台不吭声。
用插值表达式把变量交给 className
为什么需要:类名要动态生成,不能写死。
怎么想:box3 里写的是 className={class1}。花括号就是 JSX 的插值表达式,它填进去的是表达式的值——也就是 class1 这个变量当前指向的字符串。课上讲插值表达式时强调过这一点:括号里放的是一个 JS 表达式,最终取的是它的结果,而不是表达式本身。
怎么做:let class1 = "box c1 f1",然后 className={class1}。
易错点:别在花括号里再加引号,className={"class1"} 拿到的是字面量字符串 class1,不是变量的值。
类名是数组时,为什么必须 join(' ')
为什么需要:类名数量不固定时,用数组装更方便,push、filter 都好操作。
怎么想:className 最终要的是一个空格分隔的字符串,而数组不是字符串。把数组变成字符串,默认的字符串化是用逗号连接,得到的就不是“空格分隔”了,类名会被当成一个整体。
怎么做:class2.join(' '),显式指定用空格当分隔符,得到 "box c1 f1",再交给 className。
易错点:join() 不传参时默认用逗号,join('') 是直接粘在一起——两个都错,必须写 join(' '),引号中间有一个空格。
box4 和 box5 放在一起,就是为了对比
box4 写的是 className={class2},box5 写的是 className={class2.join(' ')}。两个都放在页面里,刷新一对比就能看出差别:只有 box5 的类名被正确拆开。这种“把正确写法和错误写法并排放”的调试习惯很值得学——比只看一段正确代码印象深得多。
易错点集中过一遍
- 属性名统一写
className,笔记里全部代码都是这么写的。 - 字符串里的空格不能省、不能多;多一个空格就多一个空类名。
- 数组一定
join(' '),别指望 React 帮你拆。 - 插值表达式
{}里是表达式,不是字符串字面量。
顺带确认:写的是 React 元素,不是 HTML
课上补充过:JSX 里写的不是 HTML,是 React 元素,所以标签要么成对出现,要么自闭合。本节代码里 <div>...</div> 都是成对的,<h3> 也是。写顺手了之后,属性名、标签写法都按 React 元素的规则来,别按 HTML 的习惯随手改。
列表渲染到底在做什么
为什么需要:页面上经常要把一组数据变成一组标签,比如数组 [1,2,3,4] 变成多个 li。如果手写四个 li,数据一变就要改结构。
怎么想:React 的插值表达式能渲染数组,所以关键不是“循环写标签”,而是先把普通数据数组映射成 React 元素数组。笔记里说得很直接:根据普通数据的数组生成一个 React 元素的新数组,使用 map 最方便。
怎么做:先看数组本身能否渲染,再用 map 生成元素数组。核心代码保留如下:
jsx
const root = ReactDOM.createRoot(document.querySelector("#root"));
let arr1 = [1, 2, 3, 4];
let arr2 = [<li>1</li>, <li>2</li>, <li>3</li>, <li>4</li>];
let arr3 = arr1.map(item => {
console.log('item: ', item);
return (
<li>{item}</li>
)
});
let arr4 = arr1.map(item => (
<li>{item}</li>
));
root.render((
<div>
<h3>简单数组元素渲染</h3>
{arr1}
<h3>将简单数组,渲染成 ul li 列表</h3>
<ul>{arr1}</ul>
<hr />
<ul>{arr2}</ul>
<hr />
<h3>使用map映射成一个react元素的新数组</h3>
<ul>{arr3}</ul>
<h3>map 映射优化后,省略return和{}</h3>
<ul>{arr4}</ul>
<h3>map 映射列表渲染终极版本</h3>
<ul>
{arr1.map(item => (
<li>{item}</li>
))}
</ul>
</div>
))调试时可在 map 回调里打印 item,能看到遍历过程;打印 arr3,能看到它已经是 React 元素数组。
易错点:map 回调必须有返回值,否则新数组元素是 undefined;箭头函数返回 JSX 时,用圆括号包住对象式 JSX,不要误写成函数体却没有 return。
key 为什么必须加
为什么需要:列表渲染要告诉 React 每个元素是谁。笔记要求给每个遍历元素添加唯一不重复的 key,推荐用 id。
怎么想:key 不是给用户看的显示内容,而是 React 区分列表项的标识。没有 id 时可考虑索引,但笔记也提醒“有些情况会有问题,后面讨论”,所以索引不是首选。
怎么做:在 map 返回的最外层元素上写 key={item.id}。评论列表示例保留如下:
jsx
const list = [
{ id: 1, name: 'jack', content: 'rose, you jump i jump', time: '03:21' },
{ id: 2, name: 'rose', content: 'jack, you see you, one day day', time: '03:22' },
{ id: 3, name: 'tom', content: 'jack,。。。。。', time: '03:23' }
]
root.render((
<div>
{list.length === 0 ? <h1>暂无评论!</h1> : (
<ul>
{list.map(item => (
<li key={item.id}>
<a href="">
<h3 style={{fontSize:25}}>{item.name}</h3>
<p className="f20">{item.content}</p>
</a>
</li>
))}
</ul>
)}
</div>
))易错点:key 要写在 map 返回的最外层元素上,不是随便写到内部 h3 上;不要用可能重复的 username 代替稳定 id。
复杂对象与条件渲染
为什么需要:真实列表通常不是数字,而是对象数组,还要处理“有数据/无数据”两种界面。
怎么想:对象数组仍先 map,每个对象生成一个结构;空数组用三元表达式切换成“暂无评论”。
怎么做:笔记中 users 的渲染是:
jsx
<ul>
{users.map(user=>(
<li>
<h3 style={{color:'red'}}>姓名: {user.username}</h3>
<p className='f1'>年龄: {user.age}</p>
</li>
))}
</ul>评论列表则用 list.length === 0 ? <h1>暂无评论!</h1> : <ul>...</ul>。
易错点:对象数组不能直接 {users} 渲染;条件渲染要保证三元表达式两边都是可渲染的 JSX。
评论列表的交互调试
转写稿里的案例综合练习与本例评论列表同型:点击评论内容要弹出 time。事件回调在 JSX 中只能填函数名;要传参就用箭头函数包裹,事件对象会作为第一个参数传入。若点击 p 后页面刷新,是因为事件冒泡到 a,触发 a 的默认跳转;需要在 a 的点击回调里用事件对象 e.preventDefault() 阻止默认行为。调试时先打印事件对象确认来源,再处理冒泡与默认行为。
易错点:不要写 onClick={handle(item)},那会立即执行并把返回值交给事件;应写 onClick={() => handle(item)}。
课堂画面






代码示例
代码一:行内样式(style)与类名样式(className)处理
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>8-2 / 8-3 行内样式与class样式</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
.box { width: 300px; height: 80px; border: 1px dashed #333; margin: 6px 0; }
.c1 { background-color: pink; }
.f1 { font-size: 20px; color: blue; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 行内样式:style 的值必须是「对象」
let style = {
color: 'red',
border: '1px solid blue',
backgroundColor: 'pink', // 复合属性用小驼峰
width: '200px',
height: 500, // 单位是 px 时可省略,直接写数字
fontSize: 30 // font-size -> fontSize
};
let class1 = "box c1 f1"; // class 是字符串:空格隔开
let class2 = ['box', 'c1', 'f1']; // class 是数组:要 join(' ') 拼接
root.render((
<div>
<h3>行内样式style处理</h3>
<div style={{ color: 'red', border: '1px solid blue', backgroundColor: 'pink', width: '200px', height: 500, fontSize: 30 }}>
红色文字,蓝色边框,粉色背景
</div>
<div style={style}>我是div</div>
<h3>样式类名</h3>
<div className="box">box1</div>
<div className="box c1 f1">box2</div>
<h3>插值表达式处理样式</h3>
<div className={class1}>box3</div>
<h3>样式是一个数组</h3>
<div className={class2}>box4(数组没 join,样式不生效)</div>
<div className={class2.join(' ')}>box5(join 后样式生效)</div>
</div>
))
</script>
</body>
</html>关键行与易错点
style的值必须是对象,所以 JSX 里是双大括号style={{ ... }}:外层{}是插值表达式,内层{}是对象字面量;写成style="color:red"会直接报错。- 复合属性必须小驼峰:
backgroundColor、fontSize、borderRadius,写成background-color不会生效。 - 单位是 px 时可以省略,直接写数字:
height: 500、fontSize: 30;但width: '200px'这种带单位就必须用字符串。 - 类名在 React 里是
className,不是class。 - 数组类名一定要
join(' '):直接className={class2}会被渲染成box,c1,f1,中间是逗号,CSS 匹配不到,样式不生效(代码中 box4 就是故意演示的反例)。 - 抽成变量(
let style = {...})再用style={style}传入,方便复用,避免每处重复写一长串对象。
代码二:列表渲染(map 映射 + key)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>8-4 列表渲染与key</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
.f1 { font-size: 20px; color: blue; }
.f20 { font-size: 20px; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"));
let arr1 = [1, 2, 3, 4];
let users = [
{ id: 1, username: 'atguigu', age: 19 },
{ id: 2, username: '迪丽热巴', age: 29 },
{ id: 3, username: '古力娜扎', age: 20 },
{ id: 4, username: '热依扎', age: 31 }
];
const list = [
{ id: 1, name: 'jack', content: 'rose, you jump i jump', time: '03:21' },
{ id: 2, name: 'rose', content: 'jack, you see you, one day day', time: '03:22' },
{ id: 3, name: 'tom', content: 'jack,。。。。。', time: '03:23' }
];
root.render((
<div>
<h3>简单数组元素渲染</h3>
{arr1}
<h3>使用map映射成一个react元素的新数组</h3>
<ul>
{arr1.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<h3>对象数组列表渲染</h3>
<ul>
{users.map(user => (
<li key={user.id}>
<h3 style={{ color: 'red' }}>姓名: {user.username}</h3>
<p className='f1'>年龄: {user.age}</p>
</li>
))}
</ul>
<h3>评论列表(有数据 / 无数据)</h3>
{list.length === 0 ? <h1>暂无评论!</h1> : (
<ul>
{list.map(item => (
<li key={item.id}>
<a href="">
<h3 style={{ fontSize: 25 }}>{item.name}</h3>
<p className="f20">{item.content}</p>
</a>
</li>
))}
</ul>
)}
</div>
))
</script>
</body>
</html>关键行与易错点
- 列表渲染的原理:把普通数据的数组用
map映射成React 元素的新数组,再靠插值表达式渲染数组的能力渲染出来。map返回的新数组长度与原数组一致,元素由回调的返回值决定。 map回调用item => ( <li>...</li> )这种省略return和{}的写法最简洁;如果写了{},就必须补上return,否则返回undefined,页面什么都不渲染。- 每个遍历出来的元素都要加唯一不重复的
key,推荐用id;没有 id 时才考虑索引(后面会讲索引带来的问题)。不加key控制台会报警告。 - 条件渲染用三元表达式:
list.length === 0 ? <h1>暂无评论!</h1> : (<ul>...</ul>),注意三元的两个分支都必须是一个表达式,多标签要包一层括号或包一个根元素。 - 元素内部的样式依然遵循 8-2 的规则:
style={{ fontSize: 25 }}是对象、小驼峰、px 可省略。
代码三:事件回调——this、事件对象、传参、onChange
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSX 事件回调与参数传递</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 1. 直接写函数名:window 调用,严格模式下 this 是 undefined
function click1() {
console.log('click1 执行了, this =', this);
}
// 2. 需要传参的函数
function click3(a, b) {
console.log('click3 收到参数:', a, b);
}
// 3. 既要事件对象,又要自己的参数
function click4(e, a, b) {
e.preventDefault(); // 阻止 a 标签默认跳转
console.log('事件对象 e:', e);
console.log('原生事件对象:', e.nativeEvent);
console.log('自定义参数:', a, b);
alert('发表时间: ' + a);
}
// 4. onChange:React 的 change 实际绑定的是原生 input 事件
function change(e) {
console.log('e.target.value =', e.target.value);
}
root.render((
<div>
<button onClick={click1}>click1(只填函数名)</button>
{/* 错误示范:onClick={click3(1, 2)} 会在渲染时立刻执行,绑上去的是 undefined */}
<button onClick={() => click3(1, 2)}>click3(箭头函数包裹后传参)</button>
<a href="https://www.baidu.com" onClick={(e) => click4(e, '参数A', '参数B')}>
点我不跳转(既拿事件对象又传参)
</a>
<div>
<input type="text" onChange={change} />
</div>
</div>
))
</script>
</body>
</html>关键行与易错点
- 事件名首字母大写(
onClick、onChange),值是函数的引用(函数名),不是函数调用。 onClick={click3(1, 2)}是典型错误:加括号就变成了「调用后的返回值」,渲染时立刻执行一次,真正绑给事件的是undefined,点击什么都不会发生。- 要传参就得外面再包一层函数,最简写法是箭头函数:
onClick={() => click3(1, 2)}。此时真正的事件回调是外层箭头函数,点击时才执行。 - 事件回调由 window 帮我们调用,会默认把事件对象作为第一个实参传进来,所以形参
e就能收到。严格模式下this是undefined(不是 window,也不是 button)。 - 既要事件对象又要自定义参数:用箭头函数的形参接住
e,再作为实参传给内部函数,位置自己定:onClick={(e) => click4(e, '参数A', '参数B')},函数定义处第一个形参接收事件对象。 - React 的事件对象是包装过的(做过兼容处理,常用属性基本都有),
e.preventDefault()用法与原生一致;想拿原生事件对象用e.nativeEvent。 - React 的
onChange与原生不同:原生是失去焦点才触发,React 里输入即触发,因为其底层是原生的input事件;取值用e.target.value。 - JSX 里写标签必须是成对标签或自闭合:
<input />可以,<input>会报错;<br />、<hr />同理。
常见错误
- 现象:
style="color:red"或style={{color:red}}无效/报错 → 原因:style必须是对象,复合属性需驼峰,px可写数字 → 修法:style={{ color: 'red', backgroundColor: 'pink', height: 500 }}。 - 现象:
className={['box','c1']}渲染成逗号连接 → 原因:数组直接转字符串不会自动空格拼接 → 修法:className={['box','c1'].join(' ')}或直接写字符串。 - 现象:列表渲染控制台提示缺少
key→ 原因:遍历生成的元素未提供唯一稳定标识 → 修法:key={item.id};无id时谨慎用索引。 - 现象:
onClick={handleClick()}页面加载就执行,点击反而无效 → 原因:填的是函数调用结果,不是函数引用 → 修法:onClick={handleClick}。 - 现象:想传参写成
onClick={handleClick(1)},点击不触发或立即执行 → 原因:仍填了调用结果 → 修法:onClick={() => handleClick(1)}。 - 现象:既要用事件对象又要传参时拿不到
e或参数 → 原因:外层箭头函数才是事件回调 → 修法:onClick={(e) => handleClick(e, 1)}。 - 现象:点击
<a>内内容后页面刷新/跳转 → 原因:事件冒泡触发a的默认行为 → 修法:在a的onClick中调用e.preventDefault()。 - 现象:
<input>、<hr>在 JSX 中报错 → 原因:JSX 标签需成对或自闭合 → 修法:写成<input />、<hr />。
练习
- 给评论列表每项绑定点击事件,点击内容区域弹出该项
time;同时阻止外层a标签默认跳转。要求:传参并使用事件对象。 - 将用户数组渲染为列表,每项展示姓名和年龄;无数据时显示“暂无评论!”。要求:使用
map、唯一key和条件渲染。 - 用行内样式实现:标题字号
25px、内容字号20px、粉色背景、蓝色边框。要求:使用style对象写法。 - 给定类名数组
['box','c1','f1'],分别用字符串和数组两种方式渲染两个div。要求:数组方式类名之间不能出现逗号。 - 写一个输入框,输入时实时显示当前值;点击一个链接时阻止默认跳转。要求:分别处理
onChange和onClick中的事件对象。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | jsx中的事件回调函数 | 15:00 |
| 2 | jsx中事件回调参数传递 | 06:59 |
| 3 | 即传递事件对象又传递普通参数 | 03:36 |
| 4 | jsx中的onChange事件 | 05:00 |
| 5 | jsx标签语法补充说明 | 01:16 |
| 6 | 案例综合练习-点击事件处理 | 03:06 |
小结
- 行内样式用对象,复合属性用驼峰,
px单位可省略为数字。 className推荐字符串;数组类名需join(' ')。- 列表渲染用
map生成 React 元素数组,key优先使用稳定唯一id。 - 事件绑定传函数引用,不能直接传函数调用结果。
- 需要传参时用箭头函数包裹;需要事件对象时通过外层箭头函数形参接收。
- React 中
input的onChange表现为实时输入触发,取值用e.target.value。 - 阻止链接默认行为用
e.preventDefault(),并注意事件冒泡影响。 - JSX 标签必须成对或自闭合;事件回调不要依赖
this指向按钮。