来聊聊怎么写react-native上的样式吧

我遇到了什么问题?

不久以前我重构了一个古老的项目,总结了一些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以外,你就只能每次回到头部去看看本身引入了些什么。这是我不能忍受的。。。bash

为你的样式分类

除了因为一次性引入太多的公共样式致使我要来回滑动以外,当我再去写一个新的styel.js文件时,复制这么多引入也是一件头疼的事情,那么我能不能每次只须要写一行import呢?若是个人样式都是按固定规则分类放好的是否是每次就能够只import这几个类了呢?less

常常写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个左右:

  • color:用于存放整个项目的所有颜色,这也表明着,在组件的style内部,咱们不该该再显示的书写诸如backgroundColor: '#fff'这样的代码了。
  • size:用于存放整个项目的通用大小,好比说行高、间距、字体大小等公共的数值参数。
  • layout:用于存放整个项目的公共布局,例如控制布局的flex属性、通用的padding、margin、position定位。

那么第二级中的属性我也建议控制在5个左右:

  • 颜色:边框颜色、背景颜色、字体颜色。。。
  • 大小:边框大小、间距大小、字体大小。。。
  • 布局:flex布局、position定位。。。

这样虽然增长了深度,可是分类清晰,结构明确,复用性也比较高。虽然可能会增长项目新建时的成本(建立各类分类),可是会给后续的开发、迁移、重构、复用等带来极大的便捷。但这就结束了嘛?有的同窗和我说,我有不少的边框啊,我有不少样式要复用啊,到最后个人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中样式的话题了,咱们下次见~

相关文章
相关标签/搜索