【翻译】Refined 插件发布:让 React Native 样式实现默认优化

1 阅读6分钟

原文链接:Introducing Refined: optimize your React Native styles by default

作者:Enzo Manuel Mangano

多年的 UI 开发工作中,我总结出了一套样式开发的最佳实践,并且在开发中反复运用。但问题在于,这些细节很容易被遗忘,代码评审时也常常被忽略。

于是我决定开发一款 ESLint 插件,让这些最佳实践能够自动落地执行。

细节的力量是积少成多的。这些技巧听起来或许微不足道,但你又有多少次把它们抛之脑后?现在,Refined 能为你做好全面把控。

插件地址:enzomanuelmangano/eslint-plugin-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 库内置了PressableScalePressableOpacity组件,如果你想自定义点击动画,还可以使用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(弹簧动画配置一致性)

强制要求弹簧动画要么同时配置质量、阻尼、刚度三个物理参数(massdampingstiffness),要么都不配置。

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-consistencyavoid-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。