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@apply
already.@apply py-2 px-2 bg-blue-500
is 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 ...
随机推荐
- Salesforce Integration 概览(四) Batch Data Synchronization(批量数据的同步)
本篇参考:https://resources.docs.salesforce.com/sfdc/pdf/integration_patterns_and_practices.pdf 前两篇博客讲了一下 ...
- @Value(value="${***.***}")配置文件赋值给static静态变量
public static String topicName; @Value("${activemq.topicName}") public void setTopicName(S ...
- 移动端 CPU 的深度学习模型推理性能优化——NCHW44 和 Record 原理方法详解
用户实践系列,将收录 MegEngine 用户在框架实践过程中的心得体会文章,希望能够帮助有同样使用场景的小伙伴,更好地了解和使用 MegEngine ~ 作者:王雷 | 旷视科技 研发工程师 背景 ...
- Typora+Markdown便捷发布blog
参考文章:https://www.cnblogs.com/Heroge/p/12459762.html 需要下载Typora和dotnet Typora下载链接:https://www.typora. ...
- 安全工具推荐之Goby篇
Goby(鰕虎鱼) 这个东西出来也很久了,有一年多了吧,个人感觉用起来还不错(当然见仁见智哈,别喷我),今天拿来水一篇 官网有很详细的使用说明,所以本文纯属发表一下感慨,非技术贴 官网在此:https ...
- Tomcat配置SSL证书(PFX证书)
公司项目,应该是阿里云服务器 在windows2008 R2搭建的 Tomcat部署SSL证书,本文以PFX证书为例. 配置好之后开始 一.什么是SSL(证书)? SSL证书服务(Alibaba Cl ...
- Nginx-出现-403-Forbidden
步骤一: 检查目录权限.权限不足的就加个权限吧. 例子:chmod -R 755 / var/www 步骤二: 打开nginx.conf 例子:vim /etc/nginx/nginx.conf 把 ...
- log4j.properties配置文件及详解
log4j配置文件有三个主要的组件:Logger,Appender和Layout,分别为日志类型,日志输出目的地,日志输出格式. 1. 配置日志级别及appenderName log4j.rootLo ...
- spring的异常处理
出自于:https://blog.csdn.net/he90227/article/details/46309297 ---- 利用Spring进行统一异常处理的两种方式. 原文:https:// ...
- Saruman's Army
直线上有N个点. 点i的位置是Xi.从这N个点中选择若干个,给它们加上标记. 对每一个点,其距离为R以内的区域里必须有带有标记的点(自己本身带有标记的点, 可以认为与其距离为 0 的地方有一个带有标记 ...