【Day.30】React进阶 - Styled-Components: React的CSS解决方案 | 系列总结

在过去的29天内,除了直接绑在JSX元素上外,我们从来都没有提过要如何在React处理CSS code。其实只要设定好打包工具,我们就能直接在任何元件档使用import引入css档。例如如果你是使用create-react-app建立专案的朋友,就能这样写:

import ".css档路径";

然而这样做有个问题。

一般在做SPA的时候,通常是把所有css档打包成一个或多个档案,并在第一次载入网页时就全部引入。但这会让开发者原本想要隶属於个别元件的css程序码同时生效,导致本来应该分开的css程序码互相影响。 如果想要最简易的解决这个问题,除了把style写在JSX的props上外,就要引用第三方套件。

除非你能保证,你自己、你的同事、你未来的接班人、你过去的古人(?) 都没有使用同样的class、id 或是其他哩哩抠抠的选取器......

现在,我们就来介绍一款热门的React style处理套件 - Styled-Components

安装Styled-Components

请打开terminal,输入:

npm install --save styled-components

Styled-Component基础使用

Styled-Component可以让我们撰写css code後,产生「专属这组css」的React元件。他的语法很特别:

import styled from 'styled-components';

const 元件 = styled.你想使用的DOM元素`css程序码`

//在JSX使用时
<元件></元件>

css程序码要在.js档以字串的方式写在最後面。举例来说,本来我们的MenuItem长这样:

  • src/component/MenuItem.js
import React, { memo } from 'react';

const menuItemStyle = {
    marginBottom: "7px",
    paddingLeft: "26px",
    listStyle: "none"
};

function MenuItem(props){
    return <li style={menuItemStyle}>{props.text}</li>;
}

export default memo(MenuItem);

切换成Styled-Components後就会变这样:

import React, { memo } from 'react';
import styled from 'styled-components';

const MenuStyleItem = styled.li`
    margin-bottom: 7px;
    padding-left: 26px;
    list-style: none;
`;

function MenuItem(props){
    return  <MenuStyleItem>{props.text}</MenuStyleItem>;
}

export default memo(MenuItem);

实际观看执行结果,你会发现显示的虽然是一般的<li>,但它上面多了一组特别的class,而且我们撰写的css程序码自动以这个class为选取器运作:

因为相同的Styled-Components元件会产生同样且不与其他元件重复的class,所以我们就能避免在不同地方使用到相同css选取器而互相影响。

另外,一般会习惯把定义Styled-Components的地方拉出来和本来的元件分开。就跟以前会把css跟html档分开的感觉很像。只是现在你又能更方便的制造相同style的元素:

  • (新创建)src/component/MenuItemStyle.js
import styled from 'styled-components';

const MenuStyleItem = styled.li`
    margin-bottom: 7px;
    padding-left: 26px;
    list-style: none;
`;


export { MenuStyleItem };
  • src/component/MenuItem.js
import React, { memo } from 'react';
import { MenuStyleItem } from './MenuItemStyle';

function MenuItem(props){
    return  <MenuStyleItem>{props.text}</MenuStyleItem>;
}

export default memo(MenuItem);

传递参数给Styled-Components

你可以在Styled-Components上直接绑定本来原生DOM元素就会运作的props,例如onClick,该props会自动被给予DOM元素,不需要做任何而外的事情。

另外,我们也能透过ES6的字串模板,让css根据props的值而变动。像是下面我们给了MenuStyleItem一个color={"blue"}:

  • src/component/MenuItem.js
import React, { memo } from 'react';
import { MenuStyleItem } from './MenuItemStyle';

function MenuItem(props){
    return  <MenuStyleItem color={"blue"}>{props.text}</MenuStyleItem>;
}

export default memo(MenuItem);

我们就能把props.color设为color的值(如果没有给props.color则把color设定为"black")。

  • src/component/MenuItemStyle.js
import styled from 'styled-components';

const MenuStyleItem = styled.li`
    margin-bottom: 7px;
    padding-left: 26px;
    list-style: none;
    color: ${props => props.color ? props.color : "black"};
`;


export { MenuStyleItem };

以预设props设定style主题

你可以透过Styled元件.defaultProps来设定给参数预设值。藉此达到制作「主题」的效果。当使用元件的人没有给对应的style的props时,Styled元件就会以预设的参数造型显示:

  • src/component/MenuItemStyle.js
import styled from 'styled-components';

const MenuStyleItem = styled.li`
    margin-bottom: 7px;
    padding-left: 26px;
    list-style: none;
    color: ${props => props.theme.color};
`;

MenuStyleItem.defaultProps = {
    theme: {
        color: "mediumseagreen"
    }
}

export {MenuStyleItem};
  • src/component/MenuItem.js
import React, { memo } from 'react';
import { MenuStyleItem } from './MenuItemStyle';

function MenuItem(props){
    return  <MenuStyleItem>{props.text}</MenuStyleItem>;
}

export default memo(MenuItem);

另外你也可以透过搭配useContext或是Redux达到制造相同主题的效果,这里就不示范了。

比较好的分档方式

最後,比较乾净的分档方式应该是为单一元件创立一个资料夹,在里面放置专属於它的元件程序和style程序。但这个就是不同人/团队的习惯问题了。

  • src/component/MenuItem/index.js
import React, { memo } from 'react';
import { MenuStyleItem } from './style';

function MenuItem(props){
    return  <MenuStyleItem onClick={()=>{console.log(props.handleClick)}}>{props.text}</MenuStyleItem>;
}

export default memo(MenuItem);
  • src/component/MenuItem/style.js
import styled from 'styled-components';

const MenuStyleItem = styled.li`
    margin-bottom: 7px;
    padding-left: 26px;
    list-style: none;
    color: ${props => props.theme.color};
`;

MenuStyleItem.defaultProps = {
    theme: {
        color: "mediumseagreen"
    }
}

export {MenuStyleIStyleItem};

另外,StyleComponent中也能撰写像是伪元素的语法,更多进阶使用可以参考官方文件

30天结束 | 系列总结

在这个系列中,除了这两个hook之外

  • useImperativeHandle
  • useDebugValue

我们已经把所有其他官方提供的React hook、现今业界React专案开发一定会用到的语法、套件以及他们需要用到的对应情境都讲解了一遍。上面这两个有需要的时候再去查就好。

有关React SSR的文章我应该会後续几个月内再继续发,到时候也是会发在这个系列。

最後,我希望看这系列文的读者不是只为了工作才逼自己跟随React语法写程序,而是能够理解框架是长期演变而来的。所谓的框架,只是把过去开发者发现中大型专案几乎一定会用到的Design Pattern、架构都帮你封装好。我们应该思考的是如何让「元件化的架构」搭配框架提供的功能而变得更乾净、更好用、更能弹性的封装,而不是单纯写出一个能用的React Component。

这30天的内容如果都能理解并熟悉,我相信在2020年你绝对能够用React找到一份工作。

因为2020年只剩2个月了,读者练完这系列、投完履历,2个月应该也过了......

後记

今年我没有先准备。而且在参赛的期间,我同时要实习、准备推甄、准备另一个比赛、准备通识报告......可以完赛我真的觉得不可思议。

在撰写技术教学文的时候,我会希望在介绍一个新单元时,应该要从「为什麽需要这个工具」开始,慢慢从「为什麽架构要这样设计」,再继续去提他的语法跟用法。虽然自己不是读者很难判断是不是这样,但希望这次我有做到这个目标。

如果这中间有哪篇不清楚、有写错的地方,或是你看完这系列之後有什麽想跟我交流的,都欢迎留言在底下跟我说。


<<:  Sass 基础教学 DAY31

>>:  [Python]如何使用selenium

第41天~这里就是把我觉得不错的范例来练习一下

这篇的上一篇:https://ithelp.ithome.com.tw/articles/10283...

Day18:今天来聊一下使用Microsoft 365 Defender 缓和incidents

Azure Defender提供目的导向的使用者介面,可管理和调查 Microsoft 365 服务...

【图解演算法教学】【Tree】二元树遍历 vs LeetCode 501 找众数

Youtube连结:https://bit.ly/3m1VQWV 在我们了解Binary Tree...

Day21 Raid原理

Raid可以理解为是将多个硬碟组合在一起,利用虚拟储存技术,形成一个硬碟阵列,用来提升储存空间和制造...

[番外] 来个音乐拨放器 Play! (序)

前言 参考 Tyler Potts 的 Demo 影片- Build a Music app usi...