LoginSignup
4
7

More than 5 years have passed since last update.

ReactコンポーネントのCSSをひとまとめにする

Last updated at Posted at 2017-11-26

ReactのコンポーネントのCSSをもっとスマートにしたいというのが発端です。

CSS-in-JSやCSS Moduleを使ってCSSを管理していると<style>...</style>がたくさんできてしまってスマートでない。

<実装前>
ss 1.jpg

そこで、<style>タグひとつにまとめる方法を考えてみました。

<実装後>
ss 2.jpg

スマートになっていい感じ。

方法

ReactのcomponentDidMountもしくはcomponentWillMountでCSSを生成し、それを<style></style>タグの中へ入れる。

component.js
import { h, Component } from 'preact';
import { Link } from 'preact-router/match';
import Client from '../../../functions/client/index';
import Logo from '../../../src/assets/logo.svg'

export default class Header extends Component {
    constructor(props) {
        super(props);
        this.f = new Client;
        this.id = 'x' + this.f.sid();
    }

    componentDidMount() {
        let s = {};
        let q = {};
        let i = this.id;

        s['i'] = `
        position: relative;
        text-align: center;
        background: #000;
        `
        s['.l'] = `
        padding: 12px 0 11px;
        border-bottom: 1px solid #fff;
        display: inline-block
        `

        s['.l img'] = `
        width: 148px;
        height: auto
        `

        q[600] = [{
            '.l img' : `
            width: 111px;
            height: auto
            `
        },{
            '.l .test' : `
            width: 111px;
            height: auto
            `
        }];

        this.f.styles(s,i,q);
    }

    render() {
        return (
            <header class={this.id}>
                <a href="/" className="l">
                    <img src={Logo} alt="" />
                </a>
            </header>
        );
    }
}

なんども使うので、関数はClientという名前で別ファイルのclassからインポートして使っています。
実行はthis.f.styles(s,i,q)というところです。
クラスはランダムなidを生成してそれをクラス名としているので、ローディングの度に動的に作り出されます。

実行側の関数はこんな感じ。

client.js
// クライアント用の関数群

class Client {
    constructor() {
    }

    sid = () => {
        var firstPart = (Math.random() * 46656) | 0;
        var secondPart = (Math.random() * 46656) | 0;
        firstPart = ("000" + firstPart.toString(36)).slice(-3);
        secondPart = ("000" + secondPart.toString(36)).slice(-3);
        return firstPart + secondPart;
    }

    styles = (styles, id, mediaQueries) => {

        let val, style = document.getElementsByTagName('style');

        if (style.length === 0) {
            let elm = document.createElement('style'), h = document.getElementsByTagName('head');
            h[0].appendChild(elm);
        }

        let results = '';

        Object.keys(styles).forEach(function(key) {
            if (key === 'i') {
                let c = styles[key];
                c = c.replace( /\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '' );
                c = c.replace( / {2,}/g, ' ' );
                c = c.replace( / ([{:}]) /g, '$1' );
                c = c.replace( /([;,]) /g, '$1' );
                c = c.replace( / !/g, '!' );
                results += '.'+ id + '{' + c + '}';
            } else {
                let c = styles[key];
                c = c.replace( /\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '' );
                c = c.replace( / {2,}/g, ' ' );
                c = c.replace( / ([{:}]) /g, '$1' );
                c = c.replace( /([;,]) /g, '$1' );
                c = c.replace( / !/g, '!' );
                results += '.'+ id + ' ' +  key + '{' + c + '}';
            }
        })

        if (mediaQueries !== undefined) {
            var keys = Object.keys(mediaQueries);
            for (let i=keys.length; i--;) {
                var query = keys[i];
                var arr = mediaQueries[keys[i]];
                let set = '';
                arr.map(function(element, index, array) {
                    Object.keys(element).forEach((key) => {
                        let obj = element[key];
                        let c = obj;
                        c = c.replace( /\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '' );
                        c = c.replace( / {2,}/g, ' ' );
                        c = c.replace( / ([{:}]) /g, '$1' );
                        c = c.replace( /([;,]) /g, '$1' );
                        c = c.replace( / !/g, '!' );
                        set += '.' + id + ' ' + key + '{' + c + '}'
                    })
                });
                results += '@media screen and (max-width:' + keys[i] + 'px){' + set + '}';
            }
        }

        style[0].innerText += results;
    }

}

export default Client; 

ブラッシュアップは必要だけど、とりあえず使うには問題ないと思います。

ポイントとメリット

  • CSSのスコープはコンポーネント単位になる
  • レスポンシブにも対応(指定時配列でpushするようにしています。)
  • <style>が一つに集約される
  • Object.keysのときにkeyの順序が昇順になるので、処理して大きいmedia queryから追加されるようにしています
  • ざっくりminifyされる
  • cssをjs用に書き換える必要がない
4
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7