项目背景

在项目的初始阶段,我们需要建立自己的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 as tailwind('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的更多相关文章

  1. React Native踩坑之旅

    原文连接:http://www.studyshare.cn/blog-front/blog/details/1137 最近做一个app,使用React Native实现,如果严格按照RN官方文档去配置 ...

  2. React Native踩坑Tip

    最近在使用React Native(以下简称RN)中踩了个坑,RN只能异步调用原生方法,所以在原生方法直接调用UI刷新操作需要将任务递交到主线程才可以. RCT_EXPORT_METHOD(finis ...

  3. react Native 踩坑记录

    应用 1 安卓打包 经验 解决方案 ,官方 解决方案 2 调试 用 React-Native-Debugger 教程 3 微信分享和登录 使用 react-native-wechat    地址 设计 ...

  4. React Native踩坑之FlatList组件中的onEndReached

    最近在做一个RN项目,有使用到FlatList这样一个RN封装的组件去做上拉加载更多功能,在iOS和Android平台上,总结了以下几个遇到的问题及解决方案 1. 进入页面onReached开始就被触 ...

  5. React Native踩坑之The SDK directory 'xxxxx' does not exist

    相信和我一样,自己摸索配置环境的过程中,第一次配,很可能就遇到了这个比较简单地错误,没有配置sdk环境 解决办法 在电脑,系统环境变量中,添加一个sdk的环境变量 uploading-image-95 ...

  6. React Native踩坑之无法启动Debug

    问题 在chrome启动debug模式,连接不到地址 解决办法 在模拟器中,ctrl+m调出command,选择dev setting,然后设置debug地址为localhost:8081

  7. React Native踩坑之启动android模拟器失败

    报错 Could not install the app on the device, read the error above for details.Make sure you have an A ...

  8. React Native踩坑之Unable to load script from assets

    报错: Unable to load script from assets 'index.android.bundle'. Make sure your bundle is packaged corr ...

  9. react native 踩坑之 SectionList state更新 不执行render重新渲染页面

    官方文档中指出 SectionList 本组件继承自PureComponent而非通常的Component,这意味着如果其props在浅比较中是相等的,则不会重新渲染.所以请先检查你的renderIt ...

随机推荐

  1. 从理发店小弟到阿里P10大牛,一位高中学渣的“登天”之路

    蚂蚁金服,可能是众多程序猿眼中梦寐以求的归宿,无数拿过数不清奖状的各个高校走出的学子精英都挤破头皮,只为能在蚂蚁占有一席之地. 蚂蚁金服里不乏各种深藏不露的大佬,到了那里才会深刻明白一山还有一山高这句 ...

  2. Moco框架jar下载

    下载地址: https://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/0.10.0/ 选择如下图下载 下载成功即可使用

  3. Quartz部署Linux的一个坑

    前言 最近做了一个项目,使用Quartz做定时任务,然后部署到了Linux服务器上,但是竟然很惊奇的跑不起来,已经在阿里云上的Linux上验证无数次了,后来经过不懈努力,终于发现了问题,我自己的Lin ...

  4. ES6与ES2015、ES2016以及ECMAScript的区别

    1. ECMAScript 和 JavaScript 的关系 ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现. javascript是netscape ...

  5. 【Lua篇】静态代码扫描分析(四)规则检查

    一.前言 通过前面三篇文章已经初步实现了将Lua源代码文件读取解析成语法树,现在就可以通过得到的语法树进行指定规则的代码扫描检查.下图简单列举了一下单个Lua文件内部的语法关系情况(注意并非真正的类图 ...

  6. 浅谈C#取消令牌CancellationTokenSource

    前言 相信大家在使用C#进行开发的时候,特别是使用异步的场景,多多少少会接触到CancellationTokenSource.看名字就知道它和取消异步任务相关的,而且一看便知大名鼎鼎的Cancella ...

  7. 在 CSS 中表示颜色的hex code方法和rgb方法

    hexadecimal code(十六进制编码),简写为 hex code. 我们通常使用 decimals,也就是十进制数字,它对每一位数字使用符号0到9来表示.Hexadecimals (或 he ...

  8. 生成树-RSTP基础配置

    实验内容: 一.实验拓扑: 二.实验编址: 三.实验步骤: 1. 基本IP配置 2.启动设备 3.测试连通性 4.配置RSTP基本功能: 华为交换机默认开启了MSTP生成树模式,所以我们修改生成树模式 ...

  9. 008 PHY(Physical Layer,PHY)

    一.PHY PHY((Physical Layer,PHY))是IEEE802.3中定义的一个标准模块,STA(station management entity,管理实体,一般为MAC或CPU)通过 ...

  10. 【网络编程】TCPIP-小笔记集合

    目录 前言 TCP/IP 网络编程笔记散集 参考 前言 主要记录TCPIP一些小笔记 说明: demo 基于 Linux. TCP/IP 网络编程笔记散集 快速入门: I/O 缓冲: I/O 缓冲在每 ...