不久以前我重构了一个古老的项目,总结了一些js方面的想法,不过对于一个前端项目而言不只仅只由js组成的嘛,上学的时候老师和我说HTML+CSS+JS对应的是页面的骨架、皮肤和肌肉。既然骨架咱们有了,肌肉也聊完了,今天咱们就来聊聊“皮肤”吧。javascript
因为我重构的是一个react-native项目,因此咱们先来讲说在react-native上是怎么写样式的吧,和传统的web不同的是,在react-native上面是没有css代码,不过得益于Yoga,咱们能够在客户端上像写css同样的去书写咱们的样式。咱们来看看react-native文档上是怎么说的吧:css
在React Native中,你并不须要学习什么特殊的语法来定义样式。咱们仍然是使用JavaScript来写样式。全部的核心组件都接受名为style的属性。这些样式名基本上是遵循了web上的CSS的命名,只是按照JS的语法要求使用了驼峰命名法,例如将background-color改成backgroundColor。前端
style属性能够是一个普通的JavaScript对象。这是最简单的用法,于是在示例代码中很常见。你还能够传入一个数组——在数组中位置居后的样式对象比居前的优先级更高,这样你能够间接实现样式的继承。java
没错,你几乎不须要什么成本就能够按照写css同样的写法去写咱们的rn样式,咱们来看一下文档中的例子:react
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; export default class LotsOfStyles extends Component { render() { return ( <View> <Text style={styles.red}>just red</Text> <Text style={{ color: 'blue', fontWeight: 'bold', fontSize: 30, }}>just bigblue</Text> <Text style={[styles.bigblue, styles.red]}>bigblue, then red</Text> <Text style={[styles.red, styles.bigblue]}>red, then bigblue</Text> </View> ); } } const styles = StyleSheet.create({ bigblue: { color: 'blue', fontWeight: 'bold', fontSize: 30, }, red: { color: 'red', }, }); AppRegistry.registerComponent('LotsOfStyles', () => LotsOfStyles);
在上面的demo中,咱们有两种方式去写咱们的样式,它和咱们在写css时候遇到的外联式样式、内联式样式很类似,而项目中咱们老是习惯将样式和页面分离,而后把他们都放在另一个style.js文件中。这是个很是不错的习惯,可是也形成了一些困扰。web
面对一个页面,我该怎么去模块化它的样式呢?在以前的项目中虽然作到了样式和页面的分离,让页面“看起来”干净了不少,可是在 style.js 文件中仍然是杂乱的代码,大量重复的变量、重复的内容、重复的声明。。。这个时候又有同窗说了,咱们能够把这些公共的变量、代码分离出来放到一个主题文件中呀,因而项目中除了各个页面的style.js以外又在全局出现了一个theme.js文件,在这里你们愉快的把诸如颜色、大小、布局等公共的代码放了进来。react-native
这看似解决了style.js里重复冗余的代码,可是也会让个人import变得混乱:数组
import { StyleSheet } from 'react-native'; import { px, COLOR_BG_RED, COLOR_BG_GREEN, STYLE_FR_VC_HSB, STYLE_FR_VC_HC, STYLE_FR_VC_HFS, } from 'MyStyle'; export default StyleSheet.create({ // TODO });
看到这里,我想你除了知道我import进来了两个颜色以外,对于其它变量会一头雾水吧,除非你去MyStyle模块里面亲眼看一下才会知道真正引入进来的是些什么了,若是这里的样式特别的多的话,除了再新建一个sytle.js以外,你就只能每次回到头部去看看本身引入了些什么。这是我不能忍受的。。。less
除了因为一次性引入太多的公共样式致使我要来回滑动以外,当我再去写一个新的styel.js文件时,复制这么多引入也是一件头疼的事情,那么我能不能每次只须要写一行import呢?若是个人样式都是按固定规则分类放好的是否是每次就能够只import这几个类了呢?模块化
常常写css的同窗必定注意过样式的书写顺序,某一类的属性写在一块儿,虽然在web中,这样写是为了优化css引擎,可是这也体现出了样式是有必定类型的,控制颜色的、控制边距的、控制布局的,那么咱们的公共变量是否是也能够按照这样的规则来声明呢?
import { color, size, layout } from 'MyStyle';
这样咱们文件的头部是否是就清晰多了呢?在写代码的时候,也不须要再关心我以前引入了些什么了,只要只要关注咱们要写什么就好了:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, borderLeftWidth: size.border, borderRightWidth: size.border, borderBottomWidth: size.border, borderColor: color.border, // 子元素横向排列,垂直居中,水平分布,中间用空格填满,最两边元素各自靠边 ...layout.flex.vchbs, }, });
在个人项目中默认边框的大小就是一个像素(1px),那么只要在最外层声明了 size.border
的大小,后面写代码的时候就能够畅行无阻的书写下去了,其实咱们已经模块化了,只是咱们还不够完全,不完全就表明着咱们的代码不完美,并且可复用性差,就如上面的demo,若是咱们这里须要一个三面的边框,那么其它组件需不须要呢?若是须要的话是否是也能够像我这样写呢?
固然是不能够!为何?由于咱们是在复用这个边框,因此咱们就不应再写一份如出一辙的代码了,而是应该写相似这样的:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, // 一个边框粗细为1px的红色边框 ...layout.border // 子元素横向排列,垂直居中,水平分布,中间用空格填满,最两边元素各自靠边 ...layout.flex.vchbs, }, });
这样咱们的代码不只少了不少,结构也清楚了,并且到时候替换或者修改的时候也容易一些了,不过写成这样就结束了嘛?固然不是了,咱们如今有一个红色的边框,因此咱们在layout模块下新增了一个border属性,那么若是咱们有一个蓝色的边框呢?一个绿色的粗边框呢?咱们会一直往layout模块上新增属性嘛?那最后你知道layout上面究竟有多少属性嘛?那不就又回到一开始了嘛。。。
因此,个人建议是,处于根节点的模块最好控制在3个左右:
backgroundColor: '#fff'
这样的代码了。那么第二级中的属性我也建议控制在5个左右:
这样虽然增长了深度,可是分类清晰,结构明确,复用性也比较高。虽然可能会增长项目新建时的成本(建立各类分类),可是会给后续的开发、迁移、重构、复用等带来极大的便捷。但这就结束了嘛?有的同窗和我说,我有不少的边框啊,我有不少样式要复用啊,到最后个人layout也会大到看不懂啊。。。还有的同窗说我没有那么多可复用的样式啊,那是否是你总结的思路就用不上了啊。固然不是咯,咱们只完成了样式模块化的第一步(抽离样式),接下来开始第二步。
如今不少web开发者在书写css的时候已经再也不去写原生的css了吧,而是采用例如scss、less这样的预编译语言去写样式了,那么这些预编译语言给咱们带来了哪些方便呢?我想大多数同窗第一时间都会想到Mixin。
利用混合器,能够很容易地在样式表的不一样地方共享样式。若是你发现本身在不停地重复一段样式,那就应该把这段样式构形成优良的混合器,尤为是这段样式自己就是一个逻辑单元,好比说是一组放在一块儿有意义的属性。
在react-native上面,咱们的样式代码是js代码,因此很自然的就自带预编译,不须要其它额外的语言去处理它,要作的只是判断你的属性是否须要一个Mixin。
判断一组属性是否应该组合成一个混合器,一条经验法则就是你可否为这个混合器想出一个好的名字。若是你能找到一个很好的短名字来描述这些属性修饰的样式,好比rounded-cornersfancy-font或者no-bullets,那么每每可以构造一个合适的混合器。若是你找不到,这时候构造一个混合器可能并不合适。
那么在js上面,我该如何实现一个Mixin呢?太简单了!咱们只须要一个函数就能够了,没错,只须要一个返回对象的函数就能够作到这样的效果了,加上ES7的拓展运算符,咱们就能够作到一个混合器的效果:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, ...layout.border(1px, '#fff') }, });
常写react-native的同窗必定都头疼过这样一个问题吧,就是咱们并不能像写css样式同样在一行中把全部的属性都写完,在css中咱们若是想要声明一个四面边框的大小,能够这样写:
.border { border: 10px 5px 10px 5px; }
那么在咱们写样式的时候是否是也能够这样写:
export default StyleSheet.create({ lines: { height: px(88), backgroundColor: color.background, ...layout.border(10px, 5px, 10px, 5px), }, });
咱们能够经过函数的不一样数量的参数来模拟传统css开发的简写属性,不少时候咱们更习惯在View上面去作样式的运算,利用react-native样式的覆盖数组去不断的覆盖以前的样式来达到运算的结果,这就致使View中除了须要计算你的组件要不要展现、如何展现以外,还要去计算样式该如何写,既然咱们要作样式和页面的分离,那就应该作完全一些,将样式的计算也放在style.js中。
最后总结一下咱们所作的:
我建议不管你的项目多大,代码多少,前三步都应该是一个必备的环节,可能你的项目不复杂,暂时用不到第四点,但前三条不管如何都应该尽早的去完善,这不只仅能帮助你实现后续的迭代,也能在你的脑中保留出一个对于项目完整结构的印象,要知道样式是寄生于页面的,清楚了样式,那么页面如何你也多少会烂熟于心了。而相比于经过梳理js的逻辑去了解整个项目,我想经过页面也许会更快吧,这对刚刚接手项目的新同窗来讲,是很是友善的。
通常到这里,就该放上本身开源的项目地址或者安利一波做者写的库了,不过和上一篇同样,这里咱们只讨论思路,表述想法,而具体的实践和代码仍是要靠咱们本身在项目中不断的总结和积累~
我相信不少同窗对于我提到的前三点都会很快的理解,而对于第四点可能就有些懵了,该怎么去理解这个混合器呢?我该怎么用js去实现一个呢?下面我就用一段代码来举个例子,该如何实现一个Mixin:
const layout = { // 这里的形参顺序遵循css中的 “上、右、下、左” margin(...arg) { let margin = {}; switch (arg.length) { case 1: margin = { marginTop: arg[0], marginRight: arg[0], marginBottom: arg[0], marginLeft: arg[0], }; break; case 2: margin = { marginVertical: arg[0], marginHorizontal: arg[1], }; break; case 3: margin = { marginTop: arg[0], marginHorizontal: arg[1], marginBottom: arg[2], }; break; case 4: margin = { marginTop: arg[0], marginRight: arg[1], marginBottom: arg[2], marginLeft: arg[3], }; break; default: break; } return margin; }, };
这是一个最简易的Mixin,你能够根据你的需求去写更多这样的Mixin,其实我我的以为在项目一开始的时候是不必定须要这个的,这个存在的意义是对于复杂样式书写的,更多的状况下,你的项目只要作到了前三点,在样式这一块就已经很是的整洁、完善了,多数状况下你不须要Mixin就能组织好你的代码。
好了,以上就是此次我想和你们聊的关于react-native中样式的话题了,咱们下次见~