Appearance
三元运算符渲染
学习目标
通过本章学习,你将全面掌握:
- 三元运算符的基本语法和原理
- 在JSX中使用三元运算符的各种场景
- 嵌套三元运算符的技巧和陷阱
- 三元运算符vs其他条件渲染方式的对比
- 性能优化策略
- 可读性提升技巧
- React 19中的最佳实践
第一部分:三元运算符基础
1.1 基本语法
jsx
// JavaScript三元运算符语法
// condition ? expressionIfTrue : expressionIfFalse
// 基本示例
const result = age >= 18 ? '成年' : '未成年';
const status = isOnline ? 'online' : 'offline';
const price = isMember ? 99 : 199;
// React中的使用
function AgeCheck({ age }) {
return (
<div>
{age >= 18 ? (
<p>您是成年人</p>
) : (
<p>您是未成年人</p>
)}
</div>
);
}
// 内联使用
function StatusBadge({ isOnline }) {
return (
<span className={isOnline ? 'online' : 'offline'}>
{isOnline ? '在线' : '离线'}
</span>
);
}1.2 简单条件渲染
jsx
function SimpleConditional({ isLoggedIn, hasMessages, isLoading }) {
return (
<div>
{/* 欢迎消息 */}
{isLoggedIn ? (
<h1>欢迎回来!</h1>
) : (
<h1>请登录</h1>
)}
{/* 消息提示 */}
{hasMessages ? (
<div className="alert">您有新消息</div>
) : (
<div className="info">暂无新消息</div>
)}
{/* 加载状态 */}
{isLoading ? (
<div className="spinner">加载中...</div>
) : (
<div className="content">内容已加载</div>
)}
</div>
);
}
// 内联文本切换
function InlineText({ count, isActive, hasError }) {
return (
<div>
{/* 单复数 */}
<p>
你有 {count} {count === 1 ? '条消息' : '条消息'}
</p>
{/* 状态文本 */}
<p>
状态:{isActive ? '激活' : '未激活'}
</p>
{/* 错误提示 */}
<p className={hasError ? 'error' : 'success'}>
{hasError ? '操作失败' : '操作成功'}
</p>
</div>
);
}
// 条件渲染属性
function ConditionalProps({ isActive, isPrimary, size }) {
return (
<button
className={isActive ? 'active' : 'inactive'}
type={isPrimary ? 'submit' : 'button'}
disabled={!isActive}
style={{
fontSize: size === 'large' ? '20px' : '14px',
padding: size === 'large' ? '12px 24px' : '8px 16px'
}}
>
{isActive ? '激活中' : '已禁用'}
</button>
);
}1.3 null值的使用
jsx
function NullInTernary({ showDetails, hasData, isExpanded }) {
return (
<div>
<h1>标题</h1>
{/* 不显示时返回null */}
{showDetails ? (
<div className="details">
<p>详细信息</p>
<p>更多内容</p>
</div>
) : null}
{/* 可以省略null(但显式写出更清晰) */}
{hasData ? (
<DataDisplay />
) : null}
{/* 展开/收起 */}
{isExpanded ? (
<div className="expanded-content">
<p>展开的内容</p>
</div>
) : null}
</div>
);
}
// 对比:逻辑与运算符(更简洁)
function WithLogicalAnd({ showDetails }) {
return (
<div>
<h1>标题</h1>
{/* 使用&&更简洁(只有true分支) */}
{showDetails && (
<div className="details">
<p>详细信息</p>
</div>
)}
</div>
);
}
// 何时用三元,何时用&&?
// - 有true和false两个分支 → 用三元
// - 只有true分支 → 用&&第二部分:嵌套三元运算符
2.1 两层嵌套
jsx
function TwoLevelNesting({ status, userType }) {
// 状态渲染
return (
<div>
{status === 'loading' ? (
<Spinner />
) : status === 'error' ? (
<ErrorMessage />
) : (
<Content />
)}
</div>
);
}
// 格式化提升可读性
function FormattedNesting({ status }) {
return (
<div>
{status === 'loading' ? (
<Spinner />
) : status === 'error' ? (
<ErrorMessage />
) : (
<Content />
)}
</div>
);
}
// 用户类型渲染
function UserTypeDisplay({ type }) {
return (
<div>
{type === 'admin' ? (
<AdminDashboard />
) : type === 'user' ? (
<UserDashboard />
) : (
<GuestView />
)}
</div>
);
}2.2 多层嵌套
jsx
function MultiLevelNesting({ score, grade }) {
// 分数等级
return (
<div>
等级:
{score >= 90 ? (
'A'
) : score >= 80 ? (
'B'
) : score >= 70 ? (
'C'
) : score >= 60 ? (
'D'
) : (
'F'
)}
</div>
);
}
// 更好的格式(单行)
function BetterFormatting({ score }) {
return (
<div>
等级:
{score >= 90 ? 'A' :
score >= 80 ? 'B' :
score >= 70 ? 'C' :
score >= 60 ? 'D' : 'F'}
</div>
);
}
// 但多层嵌套不推荐,应该重构
function Refactored({ score }) {
// 方案1:使用函数
const getGrade = (score) => {
if (score >= 90) return 'A';
if (score >= 80) return 'B';
if (score >= 70) return 'C';
if (score >= 60) return 'D';
return 'F';
};
return <div>等级:{getGrade(score)}</div>;
}
function RefactoredWithMap({ score }) {
// 方案2:使用映射表
const gradeRanges = [
{ min: 90, max: 100, grade: 'A' },
{ min: 80, max: 89, grade: 'B' },
{ min: 70, max: 79, grade: 'C' },
{ min: 60, max: 69, grade: 'D' },
{ min: 0, max: 59, grade: 'F' }
];
const grade = gradeRanges.find(
range => score >= range.min && score <= range.max
)?.grade || 'F';
return <div>等级:{grade}</div>;
}2.3 何时避免嵌套
jsx
// ❌ 不好:难以阅读的深层嵌套
function BadNesting({ user, loading, error, data }) {
return (
<div>
{loading ? (
<Spinner />
) : error ? (
<Error />
) : user ? (
user.isPremium ? (
user.isActive ? (
user.hasAccess ? (
data ? (
<PremiumContent data={data} />
) : (
<NoData />
)
) : (
<NoAccess />
)
) : (
<InactiveAccount />
)
) : (
user.trialExpired ? (
<TrialExpired />
) : (
<FreeContent />
)
)
) : (
<Login />
)}
</div>
);
}
// ✅ 好:提前返回(Early Return)
function GoodEarlyReturn({ user, loading, error, data }) {
if (loading) return <Spinner />;
if (error) return <Error />;
if (!user) return <Login />;
if (!user.isActive) return <InactiveAccount />;
if (!user.hasAccess) return <NoAccess />;
if (!data) return <NoData />;
if (user.isPremium) {
return <PremiumContent data={data} />;
}
if (user.trialExpired) {
return <TrialExpired />;
}
return <FreeContent />;
}
// ✅ 好:提取为函数
function GoodExtractedFunction({ user, loading, error, data }) {
function renderContent() {
if (loading) return <Spinner />;
if (error) return <Error />;
if (!user) return <Login />;
if (!user.isActive) return <InactiveAccount />;
if (!user.hasAccess) return <NoAccess />;
if (!data) return <NoData />;
return user.isPremium ? <PremiumContent data={data} /> : <FreeContent />;
}
return <div className="container">{renderContent()}</div>;
}
// ✅ 好:拆分为组件
function GoodComponentSplit({ user, loading, error, data }) {
if (loading) return <Spinner />;
if (error) return <Error />;
if (!user) return <Login />;
return <UserContent user={user} data={data} />;
}
function UserContent({ user, data }) {
if (!user.isActive) return <InactiveAccount />;
if (!user.hasAccess) return <NoAccess />;
if (!data) return <NoData />;
return user.isPremium ? <PremiumContent data={data} /> : <FreeContent />;
}第三部分:三元运算符的高级用法
3.1 条件渲染组件
jsx
function ConditionalComponent({ type, variant, mode }) {
// 根据type选择组件
const Component = type === 'button' ? Button : Input;
return <Component />;
}
// 动态选择组件
function DynamicComponent({ componentType }) {
const components = {
button: Button,
input: Input,
select: Select,
textarea: Textarea
};
const Component = components[componentType] || DefaultComponent;
return <Component />;
}
// 带Props的动态组件
function DynamicWithProps({ type, ...props }) {
const Component = type === 'primary' ? PrimaryButton : SecondaryButton;
return <Component {...props} />;
}3.2 条件Props
jsx
function ConditionalProps({ variant, size, disabled, loading }) {
return (
<button
// 条件className
className={variant === 'primary' ? 'btn-primary' : 'btn-secondary'}
// 条件disabled
disabled={disabled ? true : false}
// 条件style
style={{
backgroundColor: variant === 'danger' ? 'red' : 'blue',
fontSize: size === 'large' ? '20px' : size === 'small' ? '12px' : '16px',
padding: size === 'large' ? '12px 24px' : '8px 16px'
}}
// 条件aria属性
aria-disabled={disabled ? 'true' : 'false'}
aria-busy={loading ? 'true' : 'false'}
>
{loading ? '加载中...' : variant === 'primary' ? '确认' : '取消'}
</button>
);
}
// 条件展开props
function ConditionalSpread({ isEditable, data, mode }) {
return (
<Input
value={data.value}
{/* 根据isEditable条件展开不同props */}
{...(isEditable ? {
onChange: handleChange,
placeholder: '请输入',
autoFocus: true
} : {
readOnly: true,
disabled: true
})}
{/* 根据mode展开样式 */}
{...(mode === 'dark' ? {
className: 'input-dark',
style: { background: '#333', color: '#fff' }
} : {
className: 'input-light',
style: { background: '#fff', color: '#000' }
})}
/>
);
}
// 条件事件处理器
function ConditionalEventHandlers({ isEnabled, isReadOnly }) {
return (
<input
onClick={isEnabled ? handleClick : undefined}
onChange={isReadOnly ? undefined : handleChange}
onFocus={isEnabled ? handleFocus : undefined}
/>
);
}3.3 条件children
jsx
function ConditionalChildren({
hasHeader,
hasFooter,
hasSidebar,
layoutMode,
children
}) {
return (
<div className="page-container">
{/* 条件Header */}
{hasHeader ? (
<header className="page-header">
<h1>网站标题</h1>
<nav>导航菜单</nav>
</header>
) : null}
<div className="page-content">
{/* 条件Sidebar */}
{hasSidebar ? (
<aside className="sidebar">
<p>侧边栏内容</p>
</aside>
) : null}
{/* 主内容区域 */}
<main className={layoutMode === 'wide' ? 'content-wide' : 'content-narrow'}>
{children}
</main>
</div>
{/* 条件Footer */}
{hasFooter ? (
<footer className="page-footer">
<p>版权信息 © 2024</p>
<nav>底部链接</nav>
</footer>
) : null}
</div>
);
}
// 复杂的布局切换
function LayoutSwitch({ layout, children }) {
return (
<div>
{layout === 'single' ? (
<div className="single-column">{children}</div>
) : layout === 'double' ? (
<div className="double-column">
<div className="col-1">{children[0]}</div>
<div className="col-2">{children[1]}</div>
</div>
) : layout === 'triple' ? (
<div className="triple-column">
<div className="col-1">{children[0]}</div>
<div className="col-2">{children[1]}</div>
<div className="col-3">{children[2]}</div>
</div>
) : (
<div className="default-layout">{children}</div>
)}
</div>
);
}第四部分:三元运算符vs其他模式
4.1 vs if-else语句
jsx
// if-else(适合复杂逻辑,多条语句)
function WithIfElse({ status, data, user }) {
if (status === 'loading') {
console.log('正在加载...');
trackEvent('loading');
return <Spinner />;
}
if (status === 'error') {
console.error('加载错误');
trackError();
return <ErrorMessage />;
}
if (!user) {
redirectToLogin();
return <Login />;
}
return <DataDisplay data={data} />;
}
// 三元运算符(适合简单二选一,单一表达式)
function WithTernary({ status, data }) {
return (
<div>
{status === 'loading' ? (
<Spinner />
) : status === 'error' ? (
<ErrorMessage />
) : (
<DataDisplay data={data} />
)}
</div>
);
}4.2 vs逻辑与运算符
jsx
// 逻辑与(适合单一条件,只有true分支)
function WithLogicalAnd({ showDetails, hasPermission }) {
return (
<div>
<h1>标题</h1>
{showDetails && <Details />}
{hasPermission && <AdminPanel />}
</div>
);
}
// 三元运算符(适合有true和false两个分支)
function WithTernary({ showDetails, hasPermission }) {
return (
<div>
<h1>标题</h1>
{showDetails ? <Details /> : <Placeholder />}
{hasPermission ? <AdminPanel /> : <AccessDenied />}
</div>
);
}
// 选择建议:
// - 只有一个分支 → 用&&
// - 有两个分支 → 用三元
// - 有多个分支 → 用if-else或switch4.3 vs switch语句
jsx
// switch(适合多个明确的分支,每个分支逻辑复杂)
function WithSwitch({ type }) {
switch (type) {
case 'info':
console.log('信息类型');
return <InfoIcon />;
case 'warning':
console.log('警告类型');
return <WarningIcon />;
case 'error':
console.log('错误类型');
return <ErrorIcon />;
case 'success':
console.log('成功类型');
return <SuccessIcon />;
default:
return <DefaultIcon />;
}
}
// 三元运算符(嵌套复杂,可读性差)
function WithTernary({ type }) {
return (
<div>
{type === 'info' ? (
<InfoIcon />
) : type === 'warning' ? (
<WarningIcon />
) : type === 'error' ? (
<ErrorIcon />
) : type === 'success' ? (
<SuccessIcon />
) : (
<DefaultIcon />
)}
</div>
);
}
// 对象映射(更简洁的替代方案)
function WithMapping({ type }) {
const icons = {
info: InfoIcon,
warning: WarningIcon,
error: ErrorIcon,
success: SuccessIcon
};
const Icon = icons[type] || DefaultIcon;
return <Icon />;
}第五部分:性能优化
5.1 避免重复计算
jsx
// ❌ 不好:重复计算
function BadRepeatCalculation({ items }) {
return (
<div>
{items.length > 0 ? (
<div>
{/* items.length计算多次 */}
<p>共 {items.length} 项</p>
<p>最后一项:{items[items.length - 1]}</p>
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
) : (
<div>暂无数据</div>
)}
</div>
);
}
// ✅ 好:提取变量
function GoodExtractVariable({ items }) {
const hasItems = items.length > 0;
const itemCount = items.length;
const lastItem = items[itemCount - 1];
return (
<div>
{hasItems ? (
<div>
<p>共 {itemCount} 项</p>
<p>最后一项:{lastItem?.name}</p>
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
) : (
<div>暂无数据</div>
)}
</div>
);
}5.2 使用useMemo缓存条件
jsx
function MemoizedTernary({ showExpensive, data }) {
// 缓存昂贵的计算
const expensiveResult = useMemo(() => {
if (!showExpensive) return null;
console.log('执行昂贵计算');
return computeExpensive(data);
}, [showExpensive, data]);
return (
<div>
{showExpensive ? (
<ExpensiveComponent result={expensiveResult} />
) : (
<SimpleComponent />
)}
</div>
);
}
// 缓存条件判断结果
function CachedConditions({ users, filters }) {
const filteredUsers = useMemo(() => {
return users.filter(user => {
// 复杂的过滤逻辑
return matchesFilters(user, filters);
});
}, [users, filters]);
const hasResults = filteredUsers.length > 0;
return (
<div>
{hasResults ? (
<UserList users={filteredUsers} />
) : (
<EmptyState />
)}
</div>
);
}第六部分:实战案例
6.1 表单验证反馈
jsx
function FormField({ value, error, touched, valid }) {
return (
<div className="form-field">
<input value={value} />
{touched ? (
error ? (
<span className="error-message">
✗ {error}
</span>
) : valid ? (
<span className="success-message">
✓ 格式正确
</span>
) : null
) : null}
</div>
);
}
// 完整的表单验证
function ValidatedForm() {
const [email, setEmail] = useState('');
const [touched, setTouched] = useState(false);
const isValid = /\S+@\S+\.\S+/.test(email);
const error = email && !isValid ? '邮箱格式不正确' : '';
return (
<div>
<input
value={email}
onChange={e => setEmail(e.target.value)}
onBlur={() => setTouched(true)}
className={touched ? (isValid ? 'valid' : 'invalid') : ''}
/>
{touched ? (
isValid ? (
<span className="success">✓ 邮箱格式正确</span>
) : (
<span className="error">✗ {error}</span>
)
) : (
<span className="hint">请输入邮箱</span>
)}
</div>
);
}6.2 购物车显示
jsx
function CartButton({ itemCount, total }) {
return (
<button className="cart-button">
<span className="icon">🛒</span>
<span className="text">购物车</span>
{/* 数量徽章 */}
{itemCount > 0 ? (
<span className="badge">
{itemCount > 99 ? '99+' : itemCount}
</span>
) : null}
{/* 总价显示 */}
{total > 0 ? (
<span className="total">
¥{total > 10000 ? `${(total / 10000).toFixed(1)}万` : total}
</span>
) : null}
</button>
);
}
// 购物车详情
function CartDetails({ items }) {
const itemCount = items.length;
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const hasDiscount = total > 500;
return (
<div className="cart">
<h2>购物车</h2>
{itemCount > 0 ? (
<>
<ul>
{items.map(item => (
<li key={item.id}>
{item.name} × {item.quantity}
<span>¥{item.price * item.quantity}</span>
</li>
))}
</ul>
<div className="summary">
<p>小计:¥{total}</p>
{hasDiscount ? (
<p className="discount">
满500减50,优惠:¥50
</p>
) : (
<p className="tip">
再购¥{500 - total}可享优惠
</p>
)}
<p className="total">
总计:¥{hasDiscount ? total - 50 : total}
</p>
</div>
</>
) : (
<div className="empty-cart">
<p>购物车是空的</p>
<button>去购物</button>
</div>
)}
</div>
);
}6.3 用户状态显示
jsx
function UserStatus({ user }) {
const isOnline = user.lastSeen && Date.now() - user.lastSeen < 300000; // 5分钟内
const isPremium = user.subscription === 'premium';
const isVerified = user.emailVerified;
return (
<div className="user-status">
{/* 在线状态 */}
<span className={isOnline ? 'status-online' : 'status-offline'}>
{isOnline ? '● 在线' : '○ 离线'}
</span>
{/* 会员状态 */}
{isPremium ? (
<span className="badge-premium">VIP会员</span>
) : user.trialDaysLeft > 0 ? (
<span className="badge-trial">
试用中(剩余{user.trialDaysLeft}天)
</span>
) : (
<span className="badge-free">免费用户</span>
)}
{/* 认证状态 */}
{isVerified ? (
<span className="verified">✓ 已认证</span>
) : (
<a href="/verify" className="verify-link">去认证</a>
)}
{/* 最后在线时间 */}
<span className="last-seen">
{isOnline ? (
'现在在线'
) : user.lastSeen ? (
`最后在线:${formatTimeAgo(user.lastSeen)}`
) : (
'从未在线'
)}
</span>
</div>
);
}
function formatTimeAgo(timestamp) {
const seconds = Math.floor((Date.now() - timestamp) / 1000);
if (seconds < 60) return '刚刚';
if (seconds < 3600) return `${Math.floor(seconds / 60)}分钟前`;
if (seconds < 86400) return `${Math.floor(seconds / 3600)}小时前`;
return `${Math.floor(seconds / 86400)}天前`;
}第七部分:可读性优化
7.1 提取条件变量
jsx
// ❌ 不好:内联复杂条件
function BadInlineCondition({ user }) {
return (
<div>
{user && user.subscription === 'premium' && user.credits > 100 ? (
<PremiumFeature />
) : (
<StandardFeature />
)}
</div>
);
}
// ✅ 好:提取为变量
function GoodExtractedCondition({ user }) {
const canAccessPremium = user &&
user.subscription === 'premium' &&
user.credits > 100;
return (
<div>
{canAccessPremium ? (
<PremiumFeature />
) : (
<StandardFeature />
)}
</div>
);
}
// ✅ 更好:提取为函数
function BestExtractedFunction({ user }) {
const canAccessPremium = () => {
if (!user) return false;
if (user.subscription !== 'premium') return false;
if (user.credits <= 100) return false;
return true;
};
return (
<div>
{canAccessPremium() ? (
<PremiumFeature />
) : (
<StandardFeature />
)}
</div>
);
}7.2 使用常量提升可读性
jsx
// ❌ 不好:魔法字符串
function BadMagicStrings({ status }) {
return (
<div>
{status === 'pending' ? (
<Pending />
) : status === 'approved' ? (
<Approved />
) : status === 'rejected' ? (
<Rejected />
) : (
<Unknown />
)}
</div>
);
}
// ✅ 好:使用常量
const STATUS = {
PENDING: 'pending',
APPROVED: 'approved',
REJECTED: 'rejected'
};
function GoodWithConstants({ status }) {
return (
<div>
{status === STATUS.PENDING ? (
<Pending />
) : status === STATUS.APPROVED ? (
<Approved />
) : status === STATUS.REJECTED ? (
<Rejected />
) : (
<Unknown />
)}
</div>
);
}第八部分:边界情况处理
8.1 处理null和undefined
jsx
function HandleNullUndefined({ data }) {
return (
<div>
{/* 检查data是否存在 */}
{data ? (
data.items ? (
data.items.length > 0 ? (
<ItemList items={data.items} />
) : (
<Empty message="列表为空" />
)
) : (
<Empty message="无items数据" />
)
) : (
<Empty message="无数据" />
)}
{/* 使用可选链简化 */}
{data?.items?.length > 0 ? (
<ItemList items={data.items} />
) : (
<Empty />
)}
</div>
);
}8.2 处理空数组和空对象
jsx
function HandleEmptyCollections({ items, config }) {
const hasItems = Array.isArray(items) && items.length > 0;
const hasConfig = config && Object.keys(config).length > 0;
return (
<div>
{hasItems ? (
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
) : (
<p>没有项目</p>
)}
{hasConfig ? (
<ConfigDisplay config={config} />
) : (
<p>没有配置</p>
)}
</div>
);
}练习题
基础练习
- 使用三元运算符实现登录/未登录显示
- 创建一个显示成绩等级的组件
- 实现条件样式应用
- 处理0值的正确渲染
进阶练习
- 重构复杂的嵌套三元运算符
- 实现表单验证反馈系统
- 创建条件渲染的导航菜单
- 对比不同条件渲染方式的性能
高级练习
- 实现一个智能组件选择器
- 创建复杂的布局切换系统
- 优化三元运算符的渲染性能
- 实现功能开关(Feature Toggle)系统
通过本章学习,你已经全面掌握了三元运算符在React中的应用。合理使用三元运算符能让你的代码更加简洁明了。记住:简单场景用三元,复杂逻辑用if-else!继续学习,探索更多渲染技巧!