原文链接:Introducing Refined: optimize your React Native styles by default
多年的 UI 开发工作中,我总结出了一套样式开发的最佳实践,并且在开发中反复运用。但问题在于,这些细节很容易被遗忘,代码评审时也常常被忽略。
于是我决定开发一款 ESLint 插件,让这些最佳实践能够自动落地执行。
细节的力量是积少成多的。这些技巧听起来或许微不足道,但你又有多少次把它们抛之脑后?现在,Refined 能为你做好全面把控。
Refined 插件介绍
eslint-plugin-refined 是一款 ESLint 插件,助力开发者践行样式开发最佳实践,让你的应用界面视觉效果更精致、交互体验更流畅。
安装命令
bun add -d eslint-plugin-refined
为何选择开发成 ESLint 插件?
我本可以只写一篇博文来分享这些最佳实践,但博文容易被遗忘,文档也总有人跳过不看。话说回来,你现在真的在认真读这篇内容吗?🤔
而 ESLint 插件能直接集成到你的开发工作流中,在你编写代码时实时发现问题,提供自动修复功能,还能确保整个团队的代码风格保持一致。
你无需具备设计师的专业素养,也能发现这些样式细节问题,甚至在代码进入评审阶段前,就能将问题解决。
快速配置
import refined from 'eslint-plugin-refined';
export default [
{
plugins: {
refined,
},
rules: refined.configs.recommended.rules,
},
];
配置就是这么简单。接下来我们深入了解插件的各项规则。
核心规则说明
1. border-radius-with-curve(圆角与连续曲线搭配)
使用borderRadius属性时,应同时指定borderCurve: 'continuous',让 iOS 端的圆角呈现更美观的视觉效果。
const styles = StyleSheet.create({
submitButton: {
alignItems: 'center',
backgroundColor: '#fff',
borderRadius: 10,
borderCurve: 'continuous',
flex: 1,
justifyContent: 'center',
paddingVertical: 14,
},
});
连续曲线的圆角样式能让边角过渡更顺滑、更自然,这也是苹果在整个 iOS 系统中采用的样式。虽是细微的设计差异,却能让你的 UI 更贴合原生系统的视觉体验。
该规则会智能忽略圆形元素(当borderRadius大于等于宽 / 高的一半,或大于等于 9999 时),因为这类场景下borderCurve不会产生任何视觉效果。
如果你对圆角矩形的样式有更高要求,还可以了解一下 react-native-fast-squircle 这个库。
2. prefer-box-shadow(优先使用盒阴影)
这条规则并非为了让代码更简洁,而是能让 Android 端也实现精美的阴影效果(需开启新架构newArchEnabled)。
const styles = StyleSheet.create({
card: {
justifyContent: 'space-between',
alignItems: 'center',
shadowColor: 'black',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.5,
shadowRadius: 10,
boxShadow: '0px 3px 10px rgba(0, 0, 0, 0.5)',
},
});
开启新架构后,统一的boxShadow语法可实现跨平台兼容,再也不用为 Android 端的阴影效果写各种elevation的兼容技巧了!
3. prefer-hairline-width(优先使用细边线宽)
建议开发者在边框宽度小于等于配置阈值(默认 0.3)时,使用StyleSheet.hairlineWidth。
const styles = StyleSheet.create({
divider: {
borderWidth: 0.3,
borderWidth: StyleSheet.hairlineWidth,
borderColor: 'rgba(255,255,255,0.3)',
},
});
StyleSheet.hairlineWidth能为你提供设备屏幕上可显示的最细边框,它会自动适配不同设备的像素密度,确保你的细边框在所有设备上都清晰锐利。
4. avoid-touchable-opacity(避免使用 TouchableOpacity)
别再在 React Native 中使用TouchableOpacity组件了。
你的应用值得拥有更优质的点击交互体验。
不推荐的写法:
<TouchableOpacity
onPress={handlePress}
style={styles.button}>
<Text>Press me</Text>
</TouchableOpacity>
推荐方案:使用我专为该场景开发的 Pressto 库,它基于 Reanimated 和 Gesture Handler 打造,能实现 60 帧流畅动画的可点击组件:
import { PressableScale } from 'pressto';
<PressableScale onPress={handlePress} style={styles.button}>
<Text>Press me</Text>
</PressableScale>;
Pressto 库内置了PressableScale和PressableOpacity组件,如果你想自定义点击动画,还可以使用createAnimatedPressable方法。所有动画均在 UI 线程运行,即便 JavaScript 线程繁忙,你的应用交互也能保持流畅响应。
5. require-hitslop-small-touchables(小尺寸可点击元素必设点击区域)
要求尺寸小于配置阈值(默认 40 点)的可点击元素必须设置hitSlop属性,扩大点击目标区域。
<Pressable
onPress={toggleMute}
style={styles.icon}
hitSlop={8}>
<MaterialCommunityIcon
name="plus"
size={25}
color={Palette.background}
/>
</Pressable>
苹果的人机界面指南建议,可点击目标的最小尺寸为 44 点。这条规则能确保你的小尺寸交互元素依然易于点击,提升所有用户的操作体验和应用可访问性。
该规则会自动计算所需的hitSlop值,让可点击元素的实际点击区域达到最小尺寸要求。
6. spring-config-consistency(弹簧动画配置一致性)
强制要求弹簧动画要么同时配置质量、阻尼、刚度三个物理参数(mass、damping、stiffness),要么都不配置。
const animatedValue = withSpring(targetValue, {
damping: 25,
stiffness: 120,
mass: 4,
});
Reanimated v4 版本修改了弹簧动画的默认参数值。如果你在 v3 版本中只指定了damping参数,升级到 v4 后,即便未修改任何代码,动画效果也会发生变化。这条规则能确保你的弹簧动画物理参数完全显式配置,避免因库版本更新导致动画效果改变。
该规则同时兼容 Reanimated v3 和 v4 版本,并为两个版本提供了对应的默认配置:
'refined/spring-config-consistency': ['warn', { reanimatedVersion: 'v4' }]
版本迁移技巧:升级到 Reanimated v4 前,先临时将该规则的reanimatedVersion设为v3并启用,修复所有警告,让弹簧动画配置全部显式化;再升级到 v4 版本,你的动画效果就能和之前完全一致。
该规则同样适用于布局过渡动画:
<Animated.View layout={LinearTransition.springify().damping(20)} />
<Animated.View
layout={LinearTransition.springify().damping(20).mass(4).stiffness(900)}
/>
配置选项
Refined 插件提供了三套预设配置,满足不同开发需求:
基础配置(Base Config)
仅启用核心样式规则(不包含spring-config-consistency和avoid-touchable-opacity):
rules: refined.configs.base.rules;
推荐配置(Recommended Config)
启用所有规则,触发级别为警告:
rules: refined.configs.recommended.rules;
严格配置(Strict Config)
启用所有规则,触发级别为错误:
rules: refined.configs.strict.rules;
自定义配置
你也可以对单个规则的选项进行自定义配置:
rules: {
...refined.configs.base.rules,
'refined/prefer-hairline-width': ['warn', { threshold: 0.5 }],
'refined/require-hitslop-small-touchables': ['warn', { minSize: 44 }],
'refined/spring-config-consistency': ['warn', { reanimatedVersion: 'v4' }],
'refined/avoid-touchable-opacity': 'error',
}
随时欢迎各位贡献代码,大家可放心提交 Issue 或发起 Pull Request。