Skate.jsは、Web Componentsを薄くラッピングし、React風味で扱えるようなライブラリです。React風味とはいっても、JSXが欠かせないとかではなく、レンダリングは、ノーマル版のほかに、LitHTML、Preact、Reactから選択ができますし、加えて自分でカスタムレンダラーを作ることもできます。
軽量で、疎結合性が高いSkate.jsですが、すでにバージョンも5となり、元々多くなかったドキュメント群も古くなったりしている箇所があり、今からやる人はたぶん詰まると思います。そこで、各レンダラー向けのビルドまでをちょっとまとめてみました。違っている箇所やもっといい箇所があったらどんどんツッコミお願いします!
前提
Skate.jsはWebpackとかBabelとか使わなくていいはずなのですが、例えばちょこっとサンプルを試そうとして、HTMLに
<script src="https://unpkg.com/skatejs/dist/index-with-deps.min.js"></script>
こういうのをサンプルなどから見つけて挿入しようとすると、Cannot find module "/dist/index-with-deps.min.js" in package skatejs@5.1.1
とか言われてお詰みにになってしまいます。
また、skatejs-web-components
を使うらしいというドキュメントを見つけて、サイトへ飛ぶと、「DEPRECATED」とか書いてあって、「お、おう」となります。
運良く様々なブログなどからサンプルコードらしきものを見つけてWebpack + Babelで組むと、たいてい発生するのが「Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.」というエラーで、いろいろいじってもこれがやっぱり頻出して、やはりお詰みます。
このあたりで悩むのは、つらいので、それぞれのポイントみたいなのを書いていきます。
プロジェクトの構成について
まず、プロジェクトのディレクトリ構成として、
- index.html
- index.js // ここがエントリポイント
- bundle.js // これ吐き出されたJavaScript
という感じでプロジェクト直下に各種ファイルがある簡易的な構成ということで進めます。
同一ディレクトリ内に、.babelrcやwebpack.config.js、package.jsonもいます。
まずは、webpack.config.js
。
LitHTML、Preact、React共通で、下記のようにします。
module.exports = {
entry: ['babel-polyfill', './index.js'],
output: {
path : __dirname,
filename: 'bundle.js'
},
resolve: {
extensions: ['.js']
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
};
続いて、共通部分として、index.html
を作っておきましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Skate</title>
<script type="text/javascript" src="./bundle.js"></script>
</head>
<body>
<hello-my-component name="BOOB"><strong>Hello!!</strong></hello-my-component>
</body>
</html>
内容としては、hello-my-component
というカスタムタグを作るというものです。
これを、LitHTML、Preact、Reactそれぞれのやり方で書いてみます。
.babelrc
ですが、これはそれぞれ異なるので、ここからは別々に解説してきます。
標準構成の場合
標準構成の場合は、結論からいくと、.babelrc
のみでOKです。
{
"presets": ["es2015"]
}
index.jsは下記のようにします。
import 'skatejs-web-components';
import { props, withComponent } from 'skatejs';
class HelloMyComponent extends withComponent() {
static get props() {
return {
name: props.string,
};
}
render({ name }) {
return `
<span>${name}! : <slot></slot></span>
`;
}
}
customElements.define('hello-my-component', HelloMyComponent);
ポイントは、skatejs-web-components
を最初にインポートしておくことで、これをしないといろいろと問題が発生します。DEPRECATEDなので、のちのちは外していくことになります。slot
タグで、子タグが出力されます。
LitHTMLの場合
LitHTMLの場合は、標準構成の.babelrc
と同じで大丈夫です。skate.jsのレンダー系は、これが一番易しいと思いました。
index.js
は、下記のような感じ。
import 'skatejs-web-components';
import { props, withComponent } from 'skatejs';
// lit-html
import withLitHtml from '@skatejs/renderer-lit-html';
import { html } from 'lit-html';
class HelloMyComponent extends withComponent(
withLitHtml()
) {
static get props() {
return {
name: props.string,
};
}
render({ name }) {
return html`
<p>LitHtml's ${name}! : <slot></slot></p>
`;
}
}
customElements.define('hello-my-component', HelloMyComponent);
ポイントとして、withLitHtml
をかましている点、html
でテンプレートスニペットを作成している点でしょうか。これもslot
タグで、子タグが出力されます。割と素直です。
Preactの場合
Preact、Reactとの組み合わせはいくつかハマりポイントがありました。
.babelrc
は下記のようにおさまりました。
{
"presets": ["es2015", "preact"]
}
preact
プリセットは、babel-preset-preact
というものが存在しているので、こちらを使います。
index.js
は、こうなりました。
// @jsx h
import 'skatejs-web-components';
import { props, withComponent } from 'skatejs';
// preact
import withPreact from '@skatejs/renderer-preact';
import { h } from 'preact';
class HelloMyComponent extends withPreact(withComponent()) {
static get props() {
return {
name: props.string,
};
}
render({ name }) {
return <p>Preact's {name}! : {this.props.children}</p>;
}
}
customElements.define('hello-my-component', HelloMyComponent);
冒頭のjsxプラグマは、preactプリセットを最終的に使うことになったため不要かもしれません。.babelrc
の設定は、いろいろな手法があり、pluginを使う方法などもありますが、いろいろ試して上記に落ち着きました。
Preact、React両方のハマりポイントがありまして、LitHTMLでは、withComponent(withLitHtml())
と書く部分が、Preact、Reactでは、withPreact(withComponent())
と入れ子が逆になる必要があるということです。
ちなみに、children
を使わない場合は逆でもちゃんと動作するのですが、実はchildren
を使い始めるとうまくいきません。そもそもPreactやReactではslot
タグが使えないので注意が必要です。
Reactの場合
.babelrc
、Reactは、いつもの通りでOKです。
{
"presets": ["es2015", "react"]
}
Reactは基本的にPreactと同等のやり方で動きます。なので、JavaScript側も、PreactがReactになるのみでOKです。
import 'skatejs-web-components';
import { props, withComponent } from 'skatejs';
// preact
import withReact from '@skatejs/renderer-react';
import React from 'react';
class HelloMyComponent extends withReact(withComponent()) {
static get props() {
return {
name: props.string,
};
}
render({ name }) {
return <p>React‘s {name}! : {this.props.children}</p>;
}
}
customElements.define('hello-my-component', HelloMyComponent);
ということで、ここまで無事に表示できれば、あとは、Skate.jsのライフサイクルなどを使って開発していけるはずです。結構スタート時に大変だったので誰かの役に立てばとまとめておきます。