zoukankan      html  css  js  c++  java
  • 我的前端故事----来聊聊怎么写react-native上的样式吧

    我遇到了什么问题?

    不久之前我重构了一个古老的项目,总结了一些js方面的想法,不过对于一个前端项目而言不仅仅只由js组成的嘛,上学的时候老师和我说HTML+CSS+JS对应的是页面的骨架、皮肤和肌肉。既然骨架我们有了,肌肉也聊完了,今天我们就来聊聊“皮肤”吧。

    由于我重构的是一个react-native项目,所以我们先来说说在react-native上是怎么写样式的吧,和传统的web不一样的是,在react-native上面是没有css代码,不过得益于Yoga,我们可以在客户端上像写css一样的去书写我们的样式。我们来看看react-native文档上是怎么说的吧:

    在React Native中,你并不需要学习什么特殊的语法来定义样式。我们仍然是使用JavaScript来写样式。所有的核心组件都接受名为style的属性。这些样式名基本上是遵循了web上的CSS的命名,只是按照JS的语法要求使用了驼峰命名法,例如将background-color改为backgroundColor。

    style属性可以是一个普通的JavaScript对象。这是最简单的用法,因而在示例代码中很常见。你还可以传入一个数组——在数组中位置居后的样式对象比居前的优先级更高,这样你可以间接实现样式的继承。

    没错,你几乎不需要什么成本就可以按照写css一样的写法去写我们的rn样式,我们来看一下文档中的例子:

    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文件中。这是个非常不错的习惯,但是也造成了一些困扰。

    面对一个页面,我该怎么去模块化它的样式呢?在之前的项目中虽然做到了样式和页面的分离,让页面“看起来”干净了很多,但是在 style.js 文件中仍然是杂乱的代码,大量重复的变量、重复的内容、重复的声明。。。这个时候又有同学说了,我们可以把这些公共的变量、代码分离出来放到一个主题文件中呀,于是项目中除了各个页面的style.js之外又在全局出现了一个theme.js文件,在这里大家愉快的把诸如颜色、大小、布局等公共的代码放了进来。

    这看似解决了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之外,你就只能每次回到头部去看看自己引入了些什么。这是我不能忍受的。。。

    为你的样式分类

    除了由于一次性引入太多的公共样式导致我要来回滑动之外,当我再去写一个新的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个左右:

    • 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中样式的话题了,我们下次见~

  • 相关阅读:
    hdu 1269 迷宫城堡 (并查集)
    hdu 1272 小希的迷宫 (深搜)
    hdu 1026 Ignatius and the Princess I (深搜)
    hdu 1099 Lottery
    hdu 1068 Girls and Boys (二分匹配)
    几个基础数位DP(hdu 2089,hdu 3555,uestc 1307 windy 数)
    hdu 1072 Nightmare (广搜)
    hdu 1398 Square Coins (母函数)
    hdu 1253 胜利大逃亡 (深搜)
    hdu 1115 Lifting the Stone (求重心)
  • 原文地址:https://www.cnblogs.com/fuhuixiang/p/8461200.html
Copyright © 2011-2022 走看看