React Native踩坑日记 —— tailwind-rn
项目背景
在项目的初始阶段,我们需要建立自己的design system,我们spike了一些方案,tailwind-rn就是其中一种,如果有用到或者即将用到tailwind-rn的,可以进来看一看,避免踩坑。
后来觉得项目比较简单,tailwind对新上项目小伙伴确实不太友好,所以我们最终没有采用。
简介
GitHub - vadimdemedes/tailwind-rn: Use Tailwind CSS in React Native projects
Tailwind 提倡了原子型的CSS类,旨在灵活、复用,减少CSS重复,同时对于强迫症患者也有一定的治疗效果(毕竟有时候想类名是一件头疼的事)。当然,对于初学者有一定的熟悉成本,你需要要知道它的一些规则,熟悉它的命名系统等等。不太了解的可以自行google一下,这里不再赘述tailwind的使用。
tailwind-rn 就是基于tailwind的实现,使用tailwind生成的css类,然后再进行一些处理(CSS声明的转换、去除一些在RN上不适用的CSS声明等等),最终转化成适用于RN的Styles格式。
All styles are generated from Tailwind CSS source and not hard-coded, which makes it easy to keep this module up-to-date with latest changes in Tailwind CSS itself.
使用
我们大致来看看,tailwind-rn应该怎么在RN中使用。
import React from 'react';
import {SafeAreaView, View, Text} from 'react-native';
import tailwind from 'tailwind-rn';
const App = () => (
<SafeAreaView style={tailwind('h-full')}>
<View style={tailwind('pt-12 items-center')}>
<View style={tailwind('bg-blue-200 px-3 py-1 rounded-full')}>
<Text style={tailwind('text-blue-800 font-semibold')}>
Hello Tailwind
</Text>
</View>
</View>
</SafeAreaView>
);
export default App;
tailwind这个方法是从tailwind.ts中重新暴露出来的,伴随暴露的还有一个getColor方法。
import { create } from 'tailwind-rn';
import styles from './styles.json';
const { tailwind, getColor } = create(styles);
tailwind('text-blue-500 text-opacity-50');
//=> {color: 'rgba(66, 153, 225, 0.5)'}
styles.json是通过cli创建出来的,这个文件就是tailwind CSS类 → RN Styles 的映射。所以如果开启了purge功能,同时又添加了一些自定义的Style,需要每次都手动执行cli的命令行重新生成新的styles.json。
简便的方法是,可以监听tailwind.config.js是否更改,然后自动生成styles.json,因为一般自定义的Style都会更改这个文件。
一些优点
purge功能
打开purge配置,能让你尽可能地生成最小化的原子型RN styles,最大化的减少体积。建议在开发的时候不要开启,在打包的时候执行一次就好了。
// Default tailwind config
// Refer to https://unpkg.com/browse/tailwindcss@2.2.9/stubs/defaultConfig.stub.js
module.exports = {
purge: {
enabled: true,
content: ['../../src/**/*.ts', '../../src/**/*.tsx']
},
variants: {
extend: {}
},
plugins: []
};
自定义theme
借助于tailwind的design system,tailwind-rn也同样地能够适用,你可以什么都不需要配置,直接使用它内置的设计系统,也可以新增、覆盖一些数值,来定义符合自己的设计系统。
theme: {
fontSize: {
xs: 12,
sm: 14,
base: 16,
lg: 18,
},
extend: {
// 自定义的extend,会在生成tailwind默认的同时,额外生成自定义的类
colors: {
brand: {
primary: {
100: '#2c28f7'
},
secondary: {
100: '#146d23'
},
}
},
padding: {
7.5: 30
},
width: {
12.5: 50,
'3/7': '42.857143%',
'4/7': '57.142857%'
},
borderWidth: {
1.5: 6
}
}
}
一些坑
不支持各个边框(上、下、左、右)颜色属性
tailwind只提供整个边框颜色的支持,但是对于上、下、左、右四个边的边框是不支持的。
border-black {
--tw-border-opacity: 1;
border-color: rgba(0, 0, 0, var(--tw-border-opacity));
}
Tailwind ships with 73 colors out of the box and 5 breakpoints, which means there are already 365 border color utilities. If we added per-side border colors, that would jump to 1825 classes.
简单搜索一下,官方也有人提了这个issue,官方给出的答案是因为如果添加了四个边框的颜色支持,tailwind可能需要额外增加1825个css声明,所以暂时没有在考虑的范围之内。【issue链接】
给出的解决方案是使用@apply创建一个新的component class
.code-block {
@apply .border .border-grey-light;
border-left-width: config('borderWidths.4');
border-left-color: config('borderColors.grey-dark');
}
tailwind-rn本身没有@apply 的方式去重新定义一个自定义的类,官方给出的解释是使用@apply的本身其实就是使用tailwind('xx')【尽管我觉得有点扯,我其实是希望tailwind-rn能直接帮我把自定义的类打进styles.json,而不是我自己再手动定义一个tailwind('xx'),然后再手动引入】
I think
tailwind()function itself is an equivalent of@applyalready.@apply py-2 px-2 bg-blue-500is the same astailwind('py-2 px-2 bg-blue-500'). Support @apply
所以在RN上面的实现也是类似的
arrow: {
...tailwind(['w-0', 'h-0', 'border-solid', 'border-1.5']),
borderTopColor: getColor('bg-black-medium'),
borderLeftColor: getColor('bg-white-dark'),
borderBottomColor: getColor('bg-black-medium'),
borderRightColor: getColor('bg-black-medium')
}
不支持StyleSheet.hairLineWidth
React Native定义的逻辑像素单位是没有单位,为什么?
因为RN是个跨平台的框架,在IOS上通常以逻辑像素单位pt描述尺寸,在Android上通常以逻辑像素单位dp描述尺寸,RN选哪个都不好,既然大家意思相同,干脆不带单位,在哪个平台渲染就默认用哪个单位。
RN提供给开发者的就是已经通过DPR(设备像素比)转换过的逻辑像素尺寸,开发者无需再关心因为设备DPR不同引起的尺寸数值计算问题
通过上面的解释,所以width为1的,在ios上代表的是1pt,在android上代表的是1dp,表现为的设备像素在二倍屏上是即是2物理像素,三倍屏则是3物理像素,而一像素边框其实是代表的物理像素,所以ios在三倍屏上要想显示一像素的边框,对应的应该是0.33333pt.
由于我们需要使用RN的hairLineWidth来帮我们自动根据设备来计算,所以就不能使用配置文件来处理,所以解决的方案也比较硬核,就是直接往styles.json中塞值。
自定义一个custom.style.ts,专门用来处理一些tailwind-rn理不了的类声明。
// custom.styles.ts
import { StyleSheet } from 'react-native';
export const customStyles = {
'border-hair': {
borderWidth: StyleSheet.hairlineWidth
},
'border-t-hair': {
borderTopWidth: StyleSheet.hairlineWidth
},
'border-b-hair': {
borderBottomWidth: StyleSheet.hairlineWidth
},
'border-l-hair': {
borderLeftWidth: StyleSheet.hairlineWidth
},
'border-r-hair': {
borderRightWidth: StyleSheet.hairlineWidth
},
'width-hair': {
width: StyleSheet.hairlineWidth
}
};
export type CustomStylesKey = keyof typeof customStyles;
然后在tailwind.ts中merge一下
// tailwind.ts
import { create } from 'tailwind-rn';
import { assign } from 'lodash';
const { tailwind } = create(assign(styles, customStyles));
不支持智能提示
在现在主流的IDE上,都存在tailwind的智能提示插件,但是对于tailwind-rn的提示却不友好,要解决也挺简单
- 自己实现一个插件,兼容各个IDE
- 重新定义下类型,一个讨巧的做法,这里讲一下这种方法
编辑器不支持智能提示,我们可以利用Typescript的类型系统来稍微改造一下,让其能够自己推断
// tailwind.ts
import { create } from 'tailwind-rn';
import styles from 'src/styles/styles.json';
import { assign } from 'lodash';
import { customStyles, CustomStylesKey } from 'src/styles/custom.style';
const { tailwind } = create(assign(styles, customStyles));
export type TailwindKey = keyof typeof styles | CustomStylesKey;
export default (keys: TailwindKey[]) => tailwind(keys.join(' '));
这里强行将之前的string变成了一个数组,目的就是为了让IDE去识别自己定义的tailwind key类型
// 推荐使用
tailwind('h-11 bg-red-100')
// 改造之后
tailwind(['h-11', 'bg-red-100'])
getColor与purge冲突
当使用tailwind-rn提供的getColor方法,并开启了purge配置时
// tailwind.config.js
module.exports = {
purge: {
enabled: true,
content: ['../../src/**/*.ts', '../../src/**/*.tsx']
},
// ...
}
由于tailwind默认不支持边框颜色,所以我不得不使用RN提供的方法。但是这样使用,我就需要使用getColor方法。
// PageA.styles.ts
const styles = StyleSheet.create({
container: {
...tailwind('w-11 h-11 bg-black text-white'),
borderTopColor: getColor("blue-500")
}
})
但是在我使用purge之后,tailwind扫描了默认已经在使用的CSS类,所以blue-500没有被识别,也没有被打包到styles.json中。
这就是问题所在。解决的方法也比较简单,就是使用tailwind提供的css类
// PageA.styles.ts
const styles = StyleSheet.create({
container: {
...tailwind('w-11 h-11 bg-black text-white'),
borderTopColor: getColor("bg-blue-500 bg-opacity-50")
}
})
源代码中的getColor是会默认扫描background,所以默认拼接了bg-,那么干掉就成了
// Pass the name of a color (e.g. "bg-blue-500") and receive a color value (e.g. "#4399e1"),
// or a color and opacity (e.g. "bg-black bg-opacity-50") and get a color with opacity (e.g. "rgba(0,0,0,0.5)")
const getColor = name => {
// const style = tailwind(name.split(' ').map(className => `bg-${className}`).join(' '));
const style = tailwind(name);
return style.backgroundColor;
};
针对这个问题,我给官方提了个PR,但是不知道何时才能merge了。
Purge function will conflict with getColor by Rynxiao · Pull Request #96 · vadimdemedes/tailwind-rn
显然修改源代码是不可靠的,一下次的更新可能就会干掉你原先apply的代码,所以我们自己实现一遍就好。
// tailwind.ts
export const getColor = (keys: TailwindKey[]) => {
const style = tailwind(key.join(' '));
return style.backgroundColor;
};
总结
- 使用初期确实挺烦的,一个类一个类去找,但是熟悉了它的命名规范之后,其实写起来还挺顺畅的。
- 有一些坑,但都不是不能解决的问题,大不了使用原生的RN Style撸一撸。
参考链接
React Native踩坑日记 —— tailwind-rn的更多相关文章
- React Native踩坑之旅
原文连接:http://www.studyshare.cn/blog-front/blog/details/1137 最近做一个app,使用React Native实现,如果严格按照RN官方文档去配置 ...
- React Native踩坑Tip
最近在使用React Native(以下简称RN)中踩了个坑,RN只能异步调用原生方法,所以在原生方法直接调用UI刷新操作需要将任务递交到主线程才可以. RCT_EXPORT_METHOD(finis ...
- react Native 踩坑记录
应用 1 安卓打包 经验 解决方案 ,官方 解决方案 2 调试 用 React-Native-Debugger 教程 3 微信分享和登录 使用 react-native-wechat 地址 设计 ...
- React Native踩坑之FlatList组件中的onEndReached
最近在做一个RN项目,有使用到FlatList这样一个RN封装的组件去做上拉加载更多功能,在iOS和Android平台上,总结了以下几个遇到的问题及解决方案 1. 进入页面onReached开始就被触 ...
- React Native踩坑之The SDK directory 'xxxxx' does not exist
相信和我一样,自己摸索配置环境的过程中,第一次配,很可能就遇到了这个比较简单地错误,没有配置sdk环境 解决办法 在电脑,系统环境变量中,添加一个sdk的环境变量 uploading-image-95 ...
- React Native踩坑之无法启动Debug
问题 在chrome启动debug模式,连接不到地址 解决办法 在模拟器中,ctrl+m调出command,选择dev setting,然后设置debug地址为localhost:8081
- React Native踩坑之启动android模拟器失败
报错 Could not install the app on the device, read the error above for details.Make sure you have an A ...
- React Native踩坑之Unable to load script from assets
报错: Unable to load script from assets 'index.android.bundle'. Make sure your bundle is packaged corr ...
- react native 踩坑之 SectionList state更新 不执行render重新渲染页面
官方文档中指出 SectionList 本组件继承自PureComponent而非通常的Component,这意味着如果其props在浅比较中是相等的,则不会重新渲染.所以请先检查你的renderIt ...
随机推荐
- Java Stream 自定义Collector
Collector的使用 使用Java Stream流操作数据时,经常会用到各种Collector收集器来进行数据收集. 这里便深入了解一点去了解Collector的工作原理和如何自定义Collect ...
- Solidity
起因是Xenc师傅给我截了张图,我日 居然看不懂 ,一搜才知道,之前学的版本有些老了.. 这次学下新一点的记录下 HelloWorld pragma solidity ^0.6.0; // versi ...
- 【Lua篇】静态代码扫描分析(一)初步介绍
一.静态代码分析 静态代码分析是一种通过检查代码而不是执行程序来发现源代码中错误的手段.通常可以帮助我们发现常见的编码错误,例如: 语法错误 违反制定的标准编码 未定义的变量 安全性问 ...
- Python3中的字符串相关操作
Python3的字符串操作相关函数详解 字符串内建函数 1. capitalize() 将字符串中的第一个字符转换成大写,其他字母变成小写.例: >>> "hello Wo ...
- Ivy入门笔记
安装过程 命令行安装 下载和安装JDK5.Eclipse3.5.Ant 1.8.Ivy 2.2: 安装JDK:成功标志:在命令行下运行java命令,得到java命令行帮助: 安装Ant:解压Ant,在 ...
- chromium调试技巧
调试技巧: 1.多进程不方便跟踪渲染进程,单渲染进程的设置方法 command_line->AppendSwitchWithValue("--renderer-process- ...
- 【笔记】二分类算法解决多分类问题之OvO与OvR
OvO与OvR 前文书道,逻辑回归只能解决二分类问题,不过,可以对其进行改进,使其同样可以用于多分类问题,其改造方式可以对多种算法(几乎全部二分类算法)进行改造,其有两种,简写为OvO与OvR OvR ...
- How to build your custom release bazel version?
一般情况下用源代码编译,生成的都是开发版本,这种版本做版本号校验方面会有很多问题,所以需要编译自己的release版本. export USE_BAZEL_VERSION=1.2.1 # 选择使用版本 ...
- SQL 练习12
查询和" 01 "号的同学学习的课程 完全相同的其他同学的信息 分析 如果某同学学的某一个课程和01同学所学的课程有对应,那么子查询返回false. 如果没有对应,子查询返回tru ...
- NOIP 模拟 $32\; \rm Walker$
题解 \(by\;zj\varphi\) 发现当把 \(\rm scale×cos\theta,scale×sin\theta,dx,dy\) 当作变量时只有四个,两个方程就行. 当 \(\rm n\ ...