Web Componentsを試してみる
【Web ComponentsとPolymerをさわってみる。】の続きですが、今回はWeb Componentsで軽くサンプル作りつつポイントを書いていきたいと思います。
Polymerについては触れないです。
今回サラっと触っているのが、下記のAPIです。
Custom Elements
HTML Imports
HTML Templates
Shadow DOM
ちなみに、筆者の実行環境は【Google Chromeのバージョン 37.0.2062.120】です。
サンプルコード
このコードは、ホバーすることで色の変わる【x-sample】というタグを作っています。
そして、コード中にある、8つの【※注目※】については下部で説明します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html lang="ja"> | |
<head> | |
<meta charset="UTF-8"> | |
<title></title> | |
</head> | |
<body> | |
<style type="text/css"> | |
/* ※注目1※ */ | |
:unresolved { | |
visibility: hidden; | |
} | |
</style> | |
<link rel="import" href="sample.html"> | |
<x-sample></x-sample> | |
<x-sample2></x-sample2> | |
<sample></sample> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="sample.js"></script> | |
<template id="x-sample"> | |
<!-- ※注目2※ --> | |
<link rel="stylesheet" href="sample.css"> | |
<style type="text/css"> | |
[is="color"] { | |
transition: color 400ms ease-in-out, background 400ms ease-in-out; | |
background: red; | |
color: black; | |
} | |
[is="color"]:hover { | |
background: black; | |
color: red; | |
} | |
</style> | |
<section class="x-sample" is="color"> | |
test | |
</section> | |
</template> | |
<script> | |
(function() { | |
// ※注目3※ | |
var currentScript = document._currentScript || document.currentScript; | |
var doc = currentScript.ownerDocument; | |
var prototype = Object.create(HTMLElement.prototype, { | |
createdCallback: { | |
value: function() { | |
// ※注目4※ | |
console.log("createdCallback"); | |
var t = doc.querySelector('#x-sample'); | |
var clone = doc.importNode(t.content, true); | |
this.createShadowRoot().appendChild(clone); | |
} | |
}, | |
attachedCallback: { | |
value: function() { | |
// ※注目5※ | |
console.log("attachedCallback"); | |
} | |
}, | |
detachedCallback: { | |
value: function() { | |
// ※注目6※ | |
console.log("detachedCallback"); | |
} | |
}, | |
attributeChangedCallback: { | |
value: function() { | |
// ※注目7※ | |
console.log("attributeChangedCallback"); | |
} | |
} | |
}); | |
// ※注目8※ | |
document.registerElement('x-sample', {prototype: prototype}); | |
})(); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
console.log("sample.js"); |
- 注目1
:unresolved
というCSSの擬似クラスが適用されるのは、Custom Elementsが未定義の場合。
サンプルコードを実行すると、<x-sample>は適用されておらず、<x-sample2>は適用されている。
<sample>は命名規則からズレているからか、適用されず。
「:unresolved」の使いドコロは、未定義状態の要素が見えてしまうことによって起こる表示のチラツキを抑えるとかになります。
というCSSの擬似クラスが適用されるのは、Custom Elementsが未定義の場合。
サンプルコードを実行すると、<x-sample>は適用されておらず、<x-sample2>は適用されている。
<sample>は命名規則からズレているからか、適用されず。
「:unresolved」の使いドコロは、未定義状態の要素が見えてしまうことによって起こる表示のチラツキを抑えるとかになります。
- 注目2
<link rel="stylesheet" href="sample.css">
でcssを読み込もうとしているが、読み込みされない。
http://www.w3.org/TR/shadow-dom/#inert-html-elements
直書きすれば問題はないが、それでは色々と辛いので下記のような手法がある。
Vulcanizeで減らすHTML ImportsのHTTPリクエスト
また、Polymerの場合は良しなにインライン展開してくれるらしい。
でcssを読み込もうとしているが、読み込みされない。
http://www.w3.org/TR/shadow-dom/#inert-html-elements
![]() |
図. 通信さえ走らない |
Vulcanizeで減らすHTML ImportsのHTTPリクエスト
また、Polymerの場合は良しなにインライン展開してくれるらしい。
- 注目3
ここでdocumentを参照すると、インポートした側の参照になってしまう。
インポートされる側を参照できるようにしておく。
インポートされる側を参照できるようにしておく。
- 注目4〜7
4〜7で指定しているコールバックの実行タイミングは下記です。
実際に、Developer Toolsのconsoleから実行してみればよく分かると思います。
実際に、Developer Toolsのconsoleから実行してみればよく分かると思います。
// 要素が作成されたタイミング
var sample = document.createElement('x-sample');
⇒ createdCallback
// 要素の属性が変更されたタイミング
sample.id = "sample";
⇒ attributeChangedCallback
// 要素がDOMツリーへ挿入されるタイミング
document.body.appendChild(sample);
⇒ attachedCallback
// 要素がDOMツリーから削除されるタイミング
document.body.removeChild(sample);
⇒ detachedCallback
- 注目8
Custom Elementsで使用できるタグの名前には、ハイフンが必須です。
下記の場合にはこんなエラーに。
下記の場合にはこんなエラーに。
document.registerElement('sample')
SyntaxError: Failed to execute 'registerElement' on 'Document': Registration failed for type 'sample'. The type name is invalid.
終わりに
Web Componentsネタは、ちょいちょい書くかもです!以上!
JavaScriptでファイルを即時ダウンロードさせる処理を作ってみた
表題のとおりですが、詳細はこんなんです。
1. WebRTCでPeer to Peerする2. 自身のブラウザで画像ファイルとかをドラッグ&ドロップする
3. 自身のブラウザでそのファイルを送信処理をする
4. 接続している相手のブラウザで勝手に即時ダウンロード
(ブラウザの設定で、保存ディレクトリを固定だったら勝手に保存される)
↑これ、結構ひどいことできますよね。。
ということで、
この記事はWebSocketやWebRTCを使って他人と接続し、他人からのPUSHを受け取れる状態になっておくことが前提になります。
で、どうやるの?
自身がPUSHして、相手側の受信イベントのコールバックでこんなスクリプトを実行するだけです。// dataの内容は下記
var filename = data.filename;
var blob = new Blob([data.file], {type: data.mimeType});
// ファイルを自動でダウンロードするこの受信処理で、PDF・PNG・GIFは問題なくダウンロードできたのは確認しました。
var element = document.createElement("a");
element.download = filename;
element.href = window.URL.createObjectURL(blob);
element.click();
これだけではアレなので、筆者の場合…↓
筆者の場合 その1(送信ファイルの準備)
上記コードの、「data」の内容は下記です。ドラッグ&ドロップできるdivを用意し、ファイルをドロップする。
そのdropイベントで、受け取ったファイルの内容が「data」です。
dropイベントのコールバック
var files = event.dataTransfer.files;
for (var i = 0, l = files.length; i < l; i++) {
var file = files[i];
// ★★★data★★★↓
var data = {
file: file,
filename: file.name,
mimeType: file.type
};
}
筆者の場合 その2(送信処理)
筆者が作ってみた環境では、PeerJSを使いました。前回はライブラリ使わずに色々やりましたが、これはめっちゃ便利でした…。
dataConnection.sendってやるだけで、JSONやバイナリBlobs、ArrayBuffersを良しなに送れます。
(PeerJSはBinaryPackシリアライゼーションフォーマットを利用しているらしい)
http://nttcom.github.io/skyway/docs/
ということで、上記の「data」は dataConnection.sendで送っているだけです。
※ connection時のオプション指定で、serializationを「binary」にするのをお忘れなく。
おわりに
aタグにdownload属性なんてあったんだっていう発見ができました。そして、PeerJSの便利さも覚えてきたので、これらを使って何か作りたいと思ってます。
以上!
JavaScriptのメモリ消費について
今回は、メモ書き...テーマはJavaScriptのメモリ消費についてです。
JavaScriptのメモリモデルはガベージコレクションという技術を用いています。
この手法は、ガベージコレクター(以下、GC)が最適と判断したタイミングでメモリの開放を行います。
問題点としては、下記があげられます。
・タイミングが制御できない
・GCの処理中は、プログラムが利用可能な処理時間を奪う
要は、メモリ消費が大きいWebアプリはGCが頻発し、パフォーマンス劣化に繋がるよってこと。
GCの頻度はどうやって確認する?
下記の画像は、YahooのトップページをDeveloper ToolsのTimelineタブでレコーディングしたものです。山になっている水色部分がメモリの使用量で、一回ガクッと下がっています。
これがGCのタイミングで、ギザギザになってるほどGCの頻度が高いです。
(「バージョン 39.0.2159.0 canary (64-bit)」の画面です。)
![]() |
図1. Developer ToolsのTimeline |
![]() |
図2. Developer ToolsのTimeline |
GCの対象は?
JavaScript内ではすべての値がオブジェクトグラフに属しており、グラフにはルートが存在します。そのルートからの参照がなくなった時点で、値はガベージコレクションの対象になります。
(そして、GCの処理中はオブジェクトグラフがルートから順に走査される間はページのスクリプト実行は中断されるって感じになる。)
GCの頻度を下げるにはどうしたらいいか?
- メモリリークについて理解する
これについては説明が長くなりそうなので、下記の記事が非常に参考になります。
要は、不必要なクロージャに使用を避ける&循環参照を避けるってことがポイント。
【JavaScript】メモリの浪費を避けるコーディング
http://utage.headwaters.co.jp/blog/?p=1116
JavaScript アプリケーションのメモリー・リークを理解する
http://www.ibm.com/developerworks/jp/web/library/wa-jsmemory/
- 大量のオブジェクトを作成/破棄することをなるべくしないようにすればよい
スタティックメモリJavaScriptというテクニック
(があるらしい。こういう実装したことないので知識だけ書いておきます。)
1. 同じ型を持つオブジェクトを必要な分だけセットして保持しておく、オブジェクトプールと呼ばれるものを作る。
2. 新しいオブジェクトが必要となった場合、システムのヒープメモリから取ってくるのではなく、プール内の使用されていないオブジェクトを再利用する。
3. 不要になったオブジェクトは、メインメモリではなくプールに返却する。
つまり、最初にオブジェクトをゴソッと作って、必要な場合はゴソッと作った中のものを使う。
【メリット】
フレーム毎に生成/破棄されるオブジェクトの数を必要最低限に抑えることができる
【デメリット】
初期化時の挙動が重い
メモリをあまり使用していないときも、使用メモリ量は減らない
(その他..)
また、
'hoge'.slice() とか 'hoge'.substr() とか非破壊関数は新しく配列・文字列を作って返すので注意すべし。
終わりに
ここに書いたことは、担当している案件で動作が遅いとか、パフォーマンスチューニングして?って言われたときに気にするポイントの1つです。
ネットワーク部分以外のチューニングとしては、他に描画処理のパフォーマンスチューニングやJavaScriptのコード最適化(JSXみたいな)などがあります。
今後記事にできたらいいなーと思います。
以上!
参考
オブジェクトプールを使った静的メモリ JavaScripthttp://www.html5rocks.com/ja/tutorials/speed/static-mem-pools/
Gmail スケールの効率的メモリ管理術
http://www.html5rocks.com/ja/tutorials/memory/effectivemanagement/
Web Speech APIを使ってみる
Google ChromeでJavaScriptを使った音声認識ができるので、試してみました。Web Speech APIというのを使いますが、これは結構前から実装されています。
仕様はこちらに載っていますが、W3C標準ではありません。
https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
各ブラウザへの実装状況はこちらです。(赤はサポートしてない)
![]() |
図. Web Speech API対応状況 |
http://caniuse.com/#feat=web-speech
サンプル
このAPIを使うと、音声解析とテキストの読み上げを実現することができます。サンプルは下記のコードです。(楽だったのでKnockout.js)
使い方(今年、1回でもアップデートしてるGoogle Chromeなら動くはず)
1. 「録音開始」ボタンを押す
2. マイクへのアクセス許可をします(ページ上部に確認が出る)
3. 「開始前」から「録音スタート」と文言が変化するので、適当に喋ってみる
4. 「録音開始」ボタン下部に喋った内容と「読み上げる」というボタンが表示される
5. 「読み上げる」ボタンを押すことで、喋った内容が読み上げられます
※ 間隔を空けながら喋れば、喋った内容の文字が繋がらず、下にどんどん追加されます。
ちなみに…
var recognition = new webkitSpeechRecognition();上記を実行した後のイベントは、下記で制御できます。
recognition.start();
onstart: 音声認識サービスのスタート時に発生。
onend: 音声認識サービスが切断したときに発生。onstart後に発生するイベント。
onaudiostart: オーディオをキャプチャするために開始したときに発生。
onaudioend: オーディオをキャプチャを終了したときに発生。onaudiostart後に発生するイベント。
onsoundstart: いくつかの音が検出されたときに発生。クライアント側の検出器を用いることによって、低レイテンシで生成される。(静かな環境で何も喋らないと、発生しないイベントでした)
onsoundend: 音が検出されないときに発生。onsoundstart後に発生するイベント。
onspeechstart: 音声認識のために使用される音声が開始されたときに発生。
onspeechend: 音声認識のために使用される音声が終了したときに発生。onspeechstart後に発生するイベント。
onerror: 音声認識サービスのエラーが発生したときに発生。SpeechRecognitionErrorのインターフェイスが返却される。
onresult: 音声認識サービスが結果を返すときに発生。SpeechRecognitionEventのインターフェイスが返却される。
onnomatch: 音声認識サービスは、信頼閾値を満たさないときに発生。
おわりに
これ、もっと精度が上がるか、精度を上げるような仕組みをちゃんと入れるか、テキスト解析APIに投げつけるかとか、色々応用することができそうです。WebRTCと一緒に使ってウンヌン…とかもできそう。
以上!
AngularJSとKnockout.js
筆者の担当していた案件では、Knockout.jsを使用していました。(詳しくはこっちの記事で)今回は双方向バインディングのお友達のAngularJSをいじりつつ、Knockout.jsだったらどうなる?的なことをチョコっと書いてみます。
どっちか知ってる人はきっと分かりやすいのかなーと思います。
AngularJSをいじる
AngularJSの基本的なディレクティブ↓を触ってみました。ng-model、ng-repeat、ng-init、ng-show、ng-hide、ng-if、ng-class、ng-click
コメント入れながら書いてみたので、大体意味は伝わるのかな?と思います。
他にもディレクティブはこんなにあります。
https://docs.angularjs.org/api/ng#directive
Knockout.jsをいじる
Knockout.jsで同じようなことをするとこうなります。
まとめ
両方共おんなじようなことはできるし、ディレクティブもバインディングも自身で追加することもできる。
便利。
そして、AngularJSはもっと色々な機能がついてる。
用途にって使い分けるとよさそう!
そして、AngularJSはもっと色々な機能がついてる。
用途にって使い分けるとよさそう!
以上!
CSSの設計について。
CSSは単純に書くだけであれば簡単ですが、運用していくと扱いが非常に難しいです。とくに、サーバーサイドあがりのフロントエンジニアにはCSS設計って何をどうしたらいいのさ?ってことになりがちです。
なので、今回はCSSの設計を考えさせられる題材にしました。
よりよいCSSとは?
Philip Walton氏のブログ記事の引用ですが、よりよいCSSは下記のような点があげられます。参考:http://urx.nu/bdMt
・よりよいCSS
予測しやすい
⇒ ルールが期待通りに振る舞うこと。
再利用しやすい
⇒ ルールが抽象的で、問題を書き直す必要がなく、既存のパーツから新しいコンポーネントを速く作れるということ。
保守しやすい
⇒ 新しいコンポーネントと機能が追加・更新されるとき、既存のCSSのリファクタリングを必要としない。
拡張しやすい
⇒ CSS設計の学習コストが低く、安易に管理できること。
・よくないCSS
親に基づいてコンポーネントを修正する
⇒ クラス定義を一旦終えてから、特定のユースケースのためだけにそのクラス定義を変更するようなこと。再利用性・保守性がない。
過剰に複雑なセレクタ
⇒ HTMLが今後まったく変わらないのであればメリットはありえるが、そんなケースはほぼない。再利用性・予測ができない。
過剰に一般的なクラス名
⇒ 「.name」「.title」などのクラスでは、何の名前なのか・タイトルなのか、予測しづらい。そして、一般的なクラス名はコンポーネント内で重複しやすい。
1つのルールで過剰にスタイルする
⇒ 1つのルールで全てを指定する場合、見た目は再利用可能だが、配置に関しては再利用しづらい。
言ってることはリーダブルコードなどに載っているようなことと大体一緒。
コンポーネント設計の概念的なもの
CSSにも設計について色々な概念があります。いくつか紹介していきます。
- OOCSS(Object Oriented CSS)
オブジェクト指向のスタイルシートのこと。
構造とスキンを分離してクラス定義して、それらを組み合わせてスタイルを定義するという方法。
詳細は下記の記事が分かりやすかったです。
難しいOOCSS(オブジェクト指向CSS)の設計: http://hijiriworld.com/web/oocss-design/
クラス数が増えるし、どこまで細かく分けて実装していくかなどに統制とれるようにしなきゃならないし、
セマンティックな人とも戦いが起こるかもしれない。
OOCSSとSass: http://takazudo.github.io/blog/entry/2012-12-10-oocsssass.html
大規模サイトとかになると、パフォーマンスに影響でてくるボリュームになるかも。
- BEM(ベム)
Block、Element、Modifierの略称で、CSSの構成をこの3つにカテゴライズする。
そして、そのカテゴリにそったクラス名をつける方法。
Block: 各パーツ(ヘッダー・フッター・商品説明・検索ボックスなど)のルートとなる要素。
⇒ 「Block名」のようなクラス名となる。
Element: Blockの構成要素で、クラス名には必ずBlock名を含める。
⇒ 「Block名__Element名」のようなクラス名となる。
Modifier: 基本のレイアウトは一緒だが、見た目や動きをつけたい場合に使う。そして、名前(name)と値(value)を使い、複数パターンの状態を作る。
⇒ 「Block名_name_value」のようなクラス名をつける。
こちらの記事が分かりやすかったです。
BEMによるフロントエンドの設計 基本概念とルール: https://app.codegrid.net/entry/bem-basic-1
クラス名はかなり長くなりそう。
- SMACSS(スマックス)
Scalable and Modular Architecture for CSSの略称。
基本的な概念はBEMと同じようなもので、CSSの構成を5つのカテゴリに分ける。
(ベース・レイアウト・モジュール・状態・テーマ)
ベース: サイト全体で要素そのもののデフォルトスタイルを定義する。また、CSSリセットもベースルールに含まれる。
⇒ IDやクラスは使わず、要素自体にスタイルをあてる。
レイアウト: ヘッダー・フッター・コンテンツエリア・サイドバーなどの構成の大枠を作る要素へのルール。IDを使って指定するのもあり。
ページのエリア分け。
⇒ ひと目で意味がわかるように、クラス名の接頭辞に「l-*」又は「layout-*」をつけることを推奨している。
モジュール: レイアウト以外の構成要素は大体モジュールとして捉える。(ボタン・見出しなど)
⇒ 命名規則は特にないが、親モジュール名を接頭辞につけたり、「m-*」や「module-*」などをつけてもよい。
状態: モジュールの状態を表すルール。is-状態というカタチでクラス名を指定する。
⇒ クラス名の接頭辞に「is-*」をつける。(is-toggle-activeとか
テーマ: テーマを切り替える機能が必要な場合のみ使うカテゴリ。body要素に対してtheme-sky-blueみたいなクラスを付与して切り替える、とか。
⇒ クラス名の接頭辞に「theme-*」をつける。
カテゴライズが多いが、案件内でキチッと決まってたらとても読みやすそうだし、作る際にも名前に悩まなくなりそう。
個人的にはBEMのような長いクラス名より好ましい。
こちらの記事に詳しく書かれていました。
SMACSSによるCSSの設計 ベースとレイアウト: https://app.codegrid.net/entry/smacss-1
SMACSS 読んだ: http://chroma.hatenablog.com/entry/2013/07/22/120818
今必要なCSSアーキテクチャ: http://www.slideshare.net/MayuKimura/css-25547100
- その他
MCSS(Multilayer CSS)
BEMとOOCSSの原理を基にした構成らしい。
Multilayer CSS: http://operatino.github.io/MCSS/ja/
FLOCSS(フロックス)
OOCSSやSMACSS、BEM、SuitCSSの原理を基にした構成らしい。
MCSSのレイヤー構成にも大きな影響を受けている。
FLOCSS: https://github.com/hiloki/flocss
他にも、色々ある。
まとめ
調べてて思ったのは、これらの設計思想を全部理解するのは辛いので、これらを元にしながら案件独自のルールに落としこむのが良さそう。
運用中などに破綻して、ベースから書き換えなきゃいけない(SASSを使ってる場合だと修正は一瞬で済んでもCSS差分は膨大)とかにならないよう、はじめにしっかりと決めたい部分です。
以上!
登録:
投稿
(
Atom
)
0 件のコメント :
コメントを投稿