CSS BEMとは何か? [30seconds of interviews]

What is CSS BEM?

BEMとは、スコープ問題を解決するために名前空間を定義することでよりCSSをメンテナンスしやすくするCSSクラスの命名規則

BEMはblock, element, modifier の略。blockはプロジェクトを通して再利用可能なスタンドアロンコンポーネントであり、サブコンポーネント(elements)の名前空間として機能する。modifiersは blockまたはelementが特定の状態にあるとき、または異なる構造やスタイルのときにフラグとして利用される。

/* block component */
.block {
}

/* element */
.block__element {
}

/* modifier */
.block__element--modifier {
}

マークアップの例

<nav class="navbar">
  <a href="/" class="navbar__link navbar__link--active"></a>
  <a href="/" class="navbar__link"></a>
  <a href="/" class="navbar__link"></a>
</nav>

上のケースの場合、navbar はblock, navbar__linkはelement(navberコンポーネントの外では意味をなさない)、navbar__link--activeはmodifier(navbar__link 要尾とは異なる状態を示す)。

modifiersは冗長なので、多くの場合、modifierとしてis- *フラグを使用される。

<a href="/" class="navbar__link is-active"></a>

これらは、必ずelementに連鎖し、決して単独で使用しない。

.navbar__link.is-active {
}

30secondsofinterviews.org

HTML5 Web Storage(ローカルストレージ/セッションストレージ)とは何か [30seconds of interviews]

HTML5 Web Storageとは何か

HTML5では、ウェブサイトはブラウザのローカルにデータを保存できる。データはname-valueペアで格納され、ウェブページは自身で格納したデータにのみアクセスができる。

localStorageとsessionStorage

ライフタイムの違い

  • localStorageに格納されたデータは永続的。ウェブアプリケーションが削除する、またはユーザが自らブラウザのデータを削除するまでは、データは永続する。
  • sessionStorageはウィンドウまたはデータを格納したブラウザタブと同じライフタイムを持つ。タブが完全に閉じられると、sessionStorageに格納されたデータは削除される。

スコープの違い

どちらのstorageも、オリジンが異なると格納されたオブジェクトは共有されないため、スコープはオリジン単位となる。

  • sessionStorageは、同じオリジンの2つのブラウザのタブに、別々のsessionStorageデータを持つ。
  • sessionStorageは、localStorageとは異なり同じオリジンの同じスクリプトでも、タブが異なる場合お互いのsessionStorageにアクセスできない。
tips
  • 以前はクッキーでストレージが行われていた。
  • この保存サイズの制限は、クッキーより遥かに大きく(少なくとも5MB)、高速である。
  • データは決してサーバーに転送されず、クライアントが要求した場合のみ使用される。

30secondsofinterviews.org

scriptタグのdefer/async属性の違い [30seconds of interviews]

30secondsofinterviews.org

30 seconds of interviewsを一通り読んで面白かったので紹介していきたい

What are defer and async attributes on a <script> tag?

<script src="myscript.js"></script>
<script src="myscript.js" defer></script>
<script src="myscript.js" async></script>
  • いずれの属性も指定しない場合、スクリプトは同期的にダウンロード・実行され、ドキュメントのパースはスクリプトの実行が終了するまで中断される。スクリプトは順番に実行される

  • defer属性はドキュメントのパース中にスクリプトをダウンロードする。DOMContentLoadedイベントリスナ内のスクリプト実行と同様に、ドキュメントのパース完了後に実行される。deferスクリプトは順番に実行される。

  • async属性はドキュメントのパース中にスクリプトをダウンロードする。パースが完全に終了する前に、スクリプトを実行する。asyncスクリプトは必ずしも順番に実行されない。

  • tips

    • スクリプトが互いに依存している場合は、deferを使用する。
    • スクリプトが独立している場合は、asyncを使用する。
    • DOMが必ず準備されていなければならない、かつ実行するスクリプトがDOMContentLoaded内に置かれていない場合、deferを使用する。

Vue.js 基底inputコンポーネントを作る

inputの基底コンポーネントを作る。

v-modelは使わずに、@inputでそのまま親に変更されたvalueをemitする。

# input基底コンポーネント
Vue.component('baseInput', {
  props: ['initValue'],
  template: `
    <input
      type="text"
      :value="initValue"
      @input="$emit('inputText', $event.target.value)"
    />
  `
})

example

See the Pen vue.js base-input component by tic40 (@ccpzjoh) on CodePen.

Vue.js inheritAttrsの挙動を調べる

https://vuejs.org/v2/api/#inheritAttrs

v2.4から inheritAttrsが使えるようになった。

By setting inheritAttrs to false, this default behavior can be disabled. The attributes are available via the $attrs instance property (also new in 2.4) and can be explicitly bound to a non-root element using v-bind.

inheritAttrs: falseを設定することで、属性を継承しなくなるようだ。実際にどのような挙動になるか試してみた。

js

// hrefにsrcを指定する<a>タグ基底コンポーネント
Vue.component('inherit-attrs-link', {
  inheritAttrs: true,
  props: ['src'],
  template: '<a :href="src">{{ src }}</a>'
})

Vue.component('not-inherit-attrs-link', {
  inheritAttrs: false,
  props: ['src'],
  template: '<a :href="src">{{ src }}</a>'
})

new Vue({
  el: '#app'
})

html

<div id="app">
    <!-- src, href の両属性を指定して結果を見る -->
    <inherit-attrs-link
      src="http://example.com"
      href="http://inherit-href.example.com"
      target="_blank"
    />
    <not-inherit-attrs-link
      src="http://example.com"
      href="http://inherit-href.example.com"
      target="_blank"
    />
</div>

Result

<!-- inheritAttrs: true -->
<a href="http://inherit-href.example.com" target="_blank">http://example.com</a>

<!-- inheritAttrs: false -->
<a href="http://example.com">http://example.com</a>
  • inheritAttrs: trueの場合
    • 親の属性が継承される。
    • コンポーネントで指定したhrefよりも親のhref指定が優先されている。
  • inheritAttrs: falseの場合
    • 親の属性が継承されないため、target属性がつかなかった。
    • コンポーネントで指定したhrefが反映された。

Source

See the Pen inheritAttrs by tic40 (@ccpzjoh) on CodePen.

v-bind="$attrs" を加えてみる

公式でv-bind="$attrs" を使う例が提示されていたので、そちらの挙動も確認してみる。

js

// v-bind="$attrs" をtemplateに加える
Vue.component('inherit-attrs-link', {
  inheritAttrs: true,
  props: ['src'],
  template: '<a v-bind="$attrs" :href="src">{{ src }}</a>'
})

Vue.component('not-inherit-attrs-link', {
  inheritAttrs: false,
  props: ['src'],
  template: '<a v-bind="$attrs" :href="src">{{ src }}</a>'
})

html

前例と同じ

Result

<!-- inheritAttrs: true -->
<a href="http://inherit-href.example.com" target="_blank">http://example.com</a>

<!-- inheritAttrs: false -->
<a href="http://example.com" target="_blank">http://example.com</a>
  • inheritAttrs: trueの場合
    • 同様の結果
  • inheritAttrs: falseの場合
    • v-bind="$attrs"を指定することで、実質属性が継承されるようになり、target属性が反映された。
    • v-bind="$attrs"でのhref指定(http://inherit-href.example.com)より、:href="src"の指定(http://example.com)が優先された。

Source

See the Pen inheritAttrs-bind$attrs by tic40 (@ccpzjoh) on CodePen.

まとめ

  • 最後の例のように、受け取るprops名と、付与する属性名が異なっている場合は、inheritAttrs: falseにして置いたほうが副作用がない。
  • 基底コンポーネントは、基本的には、inheritAttrs: false にしておいた方が良さそう

GCPでクラウド破産しかけた話

初学のために、GCPインスタンスを立ち上げたり消したりしていたところ、思わぬ請求が来てしまった。

事実としては、App Engine Flex を3バージョン作成し、それぞれ2インスタンス、そのインスタンスには2つのvCPUと2GB RAMを割り当てたものを一ヶ月以上放置していた。その結果一ヶ月で数万円の請求が来るという事態になった。

f:id:tic40:20180630233427p:plain

なぜ起きたか

  • 不要なインスタンスを立ち上げたまま放置してしまった。削除のし忘れ。
  • 支払いアラートを低い金額で設定していたにもかかわらず、アラートメールに気づかず無視してしまった。

支払いをどうしたか

情けない話だが、GCPのサポートに経緯を連絡し、その結果請求額を半分にしてもらった。 とはいえ、不正利用ではない限り当然全額支払わなければならないので、次回は対応できないと言われた。

GCPクラウド破産しないために気をつけること

支払い(予算)アラートを設定する

Billingから予算の設定が可能。そこで、その予算の*%使用したときにアラートを飛ばす、という設定ができる。

f:id:tic40:20180701122928p:plain

支払いアラートメールは必ずチェックする

アラート設定しても見逃すと意味がない。アラートメールは Billing Alert という件名で、Google Cloud Platformから送信される。 見落とさないようにフィルターしておいたほうがいい。

f:id:tic40:20180701123324p:plain

猛省してます。以上。

はてブクライアントを作った

Rails, React/Redux構成で習作として何かWebアプリケーションを作ろうと思い、GW終わりから3日ぐらいでWebアプリケーションを作った。

f:id:tic40:20180510174108p:plain

こちら: https://tic40-emperor.herokuapp.com

*HerokuのFreeプランなので、サーバがsleep中だと表示にかなり時間がかかります

やりたいこと

  • Rails, React/Redux構成でWebアプリケーションを作る
  • できれば無料で

アプリケーション内容

特にアイデアが浮かばなかったので、普段よく使ってるサービスで何か作れないかなと思い、はてなブックマークの閲覧用クライアントを作ることにした。

  • はてなが提供しているAPI*1RSSからエントリー情報を取得する
  • はてなブックマークのホットエントリー一覧表示
  • エントリーの詳細情報(ブクマ数、コメント数、投稿日など)の表示
  • エントリーのコメント表示

技術構成

技術構成は以下

  • インフラ: Heroku(Free Dyno plan)
  • DB: Postge
  • キャッシュサーバ: Redis
  • CI/CD: CircleCI
  • 開発環境: Docker
  • Back-end FW: Rails v5.2.0, Node.js v9.5.0
  • Front-end FW: React v16 / Redux
  • CSS FW: Bulma

開発環境構築

Dockerで構築した

https://github.com/tic40/emperor/blob/master/Dockerfile https://github.com/tic40/emperor/blob/master/docker-compose.yml

DB設計

ログインもユーザ管理もしないので、ぱぱっと書いておわり

f:id:tic40:20180510180457j:plain

  • feeds: rssフィード情報を保持する
  • entries: エントリー情報を保持する
  • comments: エントリーに紐づくコメントを保持する

API設計

ReactからAPI経由でデータを取得する必要があるため、APIを設計する。

基本的にREST準拠で、レスポンスはjsonで返却する。

  • GET /api/v1/entries
    • エントリー情報全取得
  • GET /api/v1/entries/:feed_id
    • エントリー情報をfeed_id指定で取得

クローリング設計

はてブRSS*2から最新のホットエントリ一覧を取ってくる。 例えば総合カテゴリなら以下のURIから取得できる。

RSSのフィードはこちらから定期的に取りに行く必要があるので、スケジューラーでクローリングタスクを回すことにした。 Herokuアドオンのスケジューラー(無料)を使い、1時間おきに以下のクローリングタスクを走らせている。

https://github.com/tic40/emperor/blob/master/lib/tasks/task_crawl.rake

エントリー詳細情報の取得

RSSからホッテントリ一覧は取得できるが、エントリーに対するコメントなどの詳細情報は取得できない。 はてなが提供している はてなブックマークエントリー情報取得API*3 から取得することにした。

デプロイ

GitHubのmasterブランチ更新時に自動デプロイが走るようにCircleCIのデプロイレシピを記述した。

https://github.com/tic40/emperor/blob/master/.circleci/config.yml

実装

  • RailsAPIモード、かつv5.1から追加されたwebpackerオプションを指定して作成した
Rails new . --webpack=react --api
  • APIのレスポンスはHerokuのRedisアドオン(無料)を使い、キャッシュすることで高速した
  • フロントエンドの構成はReduxを使うため、以下のような構造にした。
# directories
app/javascript/packs/
   |-actions
   |-components
   |-containers
   |-reducers
   |-utils

感想とか

  • はてぶのAPIは user-agent ヘッダーをつけてリクエストする必要がある
  • Heroku無料プランだとDBレコードが10000までという制限がある。エントリーに対するコメントもDBにインサートしているため、あっという間に10000行を越えてしまった。特にデータを溜める意味はなかったので、DBにデータをためないようにRSS更新の際にレコード削除して直近のRSSに含まれているエントリー情報だけDBに保持することにした。
  • 1時間に一度しかクローリングしてないので、リアルタイム性は全然ない。
  • Herokuは楽
  • やっぱり個人で開発するならReactよりVueの方がいいかな。

GitHub

ソースはこちら

github.com