ReactのコンポーネントのCSSをもっとスマートにしたいというのが発端です。
CSS-in-JSやCSS Moduleを使ってCSSを管理していると<style>...</style>
がたくさんできてしまってスマートでない。
そこで、<style>
タグひとつにまとめる方法を考えてみました。
スマートになっていい感じ。
方法
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用に書き換える必要がない