我遇到了什么问题?

不久之前我重构了一个古老的项目,总结了一些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中样式的话题了,我们下次见~

我的前端故事----来聊聊怎么写react-native上的样式吧的更多相关文章

  1. 我的前端故事----来聊聊react-native应用的健康监控

    监控什么 今天我们来聊聊如何监控你的应用程序,这里的监控说的不是让我们去监控用户,而是监控应用的健康状态,什么是健康状态呢?对于后端的同学来说,在微服务的架构下,每个子服务是否正常工作.返回的结果是否 ...

  2. 程序员面试京东前端,现场JavaScript代码写出魔方特效

    程序员面试京东前端,现场JS代码写出魔方特效,成功搞定20K月薪 今天小编我逛论坛,看到了一位程序员小伙子,因为是有了两年工作经验,然后去京东面试前端岗,一面二面轻松就过了,到了技术面这一块,小伙干脆 ...

  3. iOS 写给iOS开发者的React Native学习路线(转)

    我是一名iOS开发者,断断续续一年前开始接触React Native,最近由于工作需要,专职学习React Native也有一个多月了.网络上知识资源非常的多,但能让人豁然开朗.迅速学习的还是少数,我 ...

  4. 【转】前端框架天下三分:Angular React 和 Vue的比较

    前端框架天下三分:Angular React 和 Vue的比较 原文链接:http://blog.csdn.net/haoshidai/article/details/52346865 前端这几年的技 ...

  5. Docz 用 MDX 写 React UI 组件文档

    Docz 用 MDX 写 React UI 组件文档 前言 为了提升开发效率,创建一套 UI 组件库是一种较为有效的方式之一:可以减少重复工作.提高可复用,所以现在越来越多团队开始创建自己的 UI 组 ...

  6. 试着用React写项目-利用styled-components解决样式问题

    转载请注明出处:王亟亟的大牛之路 啰嗦之前先安利,会渐渐丰富前端的知识点 https://github.com/ddwhan0123/Useful-Open-Source-Android 昨天用web ...

  7. 写给移动开发者的 React Native 指南

    本文原创版权归 简书 wingjay 所有,如有转载,请于文章篇头位置显示标注原创作者及出处,以示尊重! 作者:wingjay 出处:http://www.jianshu.com/p/b8894425 ...

  8. 写给iOS开发者的React Native学习路线(转)

    我是一名iOS开发者,断断续续一年前开始接触React Native,最近由于工作需要,专职学习React Native也有一个多月了.网络上知识资源非常的多,但能让人豁然开朗.迅速学习的还是少数,我 ...

  9. 三种Web前端框架比较与介绍--Vue, react, angular

    一.Angular 1.MVVM(Model)(View)(View-model): 2.模块化(Module)控制器(Contoller)依赖注入: 3.双向数据绑定:界面的操作能实时反映到数据,数 ...

随机推荐

  1. React Native学习(六)—— 轮播图

    本文基于React Native 0.52 Demo上传到Git了,有需要可以看看,写了新内容会上传的.Git地址 https://github.com/gingerJY/React-Native-D ...

  2. zzuli oj 1135 算菜价

    题目: Description 妈妈每天都要出去买菜,但是回来后,兜里的钱也懒得数一数,到底花了多少钱真是一笔糊涂帐.现在好了,作为好儿子(女儿)的你可以给她用程序算一下了,呵呵. Input 输入含 ...

  3. SQL的各种连接(cross join、inner join、full join)的用法理解

    SQL中的连接可以分为内连接,外连接,以及交叉连接 . 1. 交叉连接CROSS JOIN 如果不带WHERE条件子句,它将会返回被连接的两个表的笛卡尔积,返回结果的行数等于两个表行数的乘积: 举例, ...

  4. 织梦CMS首页调用分类信息栏目及列表方法

    不懂代码,搜索学习一晚上,都是说调用特定栏目分类信息列表的,用这个代码 {dede:arclistsg row='10' titlelen='24' orderby='pubdate' channel ...

  5. dedecms列表页有图调用缩略图无图留空的方法

    默认情况下,织梦的文章列表页会调用出当前栏目下的文章列表,并且调用出每个文章的缩略图:如果文章本身就有图,会调用出一张小图,如果没有,则会显示默认的织梦图片.这种处理方式有时候比较影响美观,其实可以修 ...

  6. HttpUrlConnection使用与总结

    /*     * URL请求的类别分为二类,GET与POST请求.二者的区别在于:      * a:) get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,      ...

  7. 利用xcode生成的app生成可以在iphone和itouch上运行的ipa安装包

    在编译好的真机版目录下的.app文件,至于生成真机可以运行的app的方法,有两种方式,一种是交99美元获得一个证书,另外一种是破解的方式,在此不再详述,本文假设你已经生成了真机上可以运行的app包了( ...

  8. 使用 IDEA和Maven 整合SSH框架

    1.创建web工程 一路next 下去就行.完成后,IDEA会自动构建maven工程. 2.创建如下项目结构 需要将 java文件夹设置为SourcesRoot目录,否则无法创建package 设置操 ...

  9. PowerShell 异常处理

    在使用 PowerShell 的过程中,发现它的异常处理并不像想象中的那么直观,所以在这里总结一下. Terminating Errors 通过 ThrowTerminatingError 触发的错误 ...

  10. CentOS 7安装Oracle 11gR2以及设置自启动

    一.环境准备 1.正确无误的CentOS 7系统环境 CentOS 7安装:http://www.cnblogs.com/VoiceOfDreams/p/8043958.html 2.正确的JDK环境 ...