WebComponents関連のフレームワークと戦った話

2019-12-28

仕事でいろんなプロダクトから共通で呼ぶSDKのComponent集みたいなものが作りたく、利用側がフレームワークに依存せず使えるようにしたかったのでWebComponentsを利用することにした。

WebComponentsを利用するあたってVanillaで作るのではなく適当なフレームワーク?を下記を考慮して選定することにした。

Twirp使う以外は、そこまで特殊な要件でもないと思ったので割となに使ってもできたり事例があるんだろうなと思ってました。

検証について

まず軽量で簡単そうでそれなりに人気のありそうなものから検証開始した。

LitElement

Polymer Projectが作っている軽量なWebComponents利用者向けのライブラリ

<a href="https://lit-element.polymer-project.org/" class="embedly-card" data-card-width="100%" data-card-controls="0">Embedded content: https://lit-element.polymer-project.org/</a>

簡単なLifeCycleがあり、TypeScriptですんなり書き始めることができた。

import { LitElement, html, property, customElement } from 'lit-element';

@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
  @property() name = 'World';

  render() {
    return html`<p>Hello, ${this.name}!</p>`;
  }
}

ただし自分のWebpack力不足なのかIE11で上手く動かすことができず、断念。。。。

関連Issue: https://github.com/Polymer/lit-element/issues/54

lit-html自体は軽量でいい感じだったのでlit-htmlを使った別の方法を考えていった。

SkateJS with lit-html

SkateJSを次に検証した。 SkateJSは簡単にWebComponentsを利用するためのライブラリでViewライブラリとしてReact/Preact/lit-htmlから選択してレンダリングすることができる。

<a href="https://skatejs.netlify.com/" class="embedly-card" data-card-width="100%" data-card-controls="0">Embedded content: https://skatejs.netlify.com/</a>

今回は先ほど好感触だったのでlit-htmlを選択した。

import Element, { h as html } from '@skatejs/element-lit-html';

class SimpleGreeting extends Element {
    static props = {
        name: String
    }
    name = 'World';
    render() {
        return html`<p>Hello, ${this.name}!</p>`;
    }
}

customElements.define('simple-greeting', SimpleGreeting);

TypeScriptで動かすことやlit-htmlをes5に変換することに少し手間取ったがIE11でも動かすことができた。

関連Issue: https://github.com/Polymer/lit-html/issues/516

いざ実装を進めてみると、IE11でStyleが崩れていて、vendor-prefixが付いていないことに気づいた。(手で書いてはいないので当然だが、普段はstyled componentに大分甘えていた) lit-htmlにAutoprefixerなどで自動でつけることを試みたが上手くいかず断念。 手でvendor-prefixを書く選択肢は自分にはないので(そこまでCSSの知識がない)lit-htmlを使うことをやめた。

またTypeScriptを利用するために諸々設定するのも辛くなったのでDefaultがTypeScriptで書けるものを検証していった。

StencilJS

次にIonicが作っているStencilの検証を始めた。 StencilはWeb Componentsのコンパイラーです。

<a href="https://stenciljs.com/" class="embedly-card" data-card-width="100%" data-card-controls="0">Embedded content: https://stenciljs.com/</a>

TypeScript前提でReact likeにComponentを書くことができる。Stateも利用できる。

import { Component, Host, h, State } from "@stencil/core";

@Component({
    tag: "simple-greeting",
    styleUrl: "index.css",
    shadow: true
})
export class SimpleGreeting {
    @Prop() name: string = 'World';

    render() {
        return (<p>Hello, ${this.name}!</p>);
    }
}

またbuildすると自動で色んなプラットフォーム向けのJSファイルを生成される。 @stencil/postcss のプラグインを入れることによってPostCSSを使うことができAutoprefixerを導入することができた。(賛否あると思うがついでにpostcss-nestedも入れた。)

Twirp使う上でProtoから作成した生成Fileを使うことにRollupに慣れておらず苦労したが、ts-protoとConfigに下記を設定することで乗り越えた。

export const config: Config = {
    ...,
    commonjs: {
        namedExports: {
            "protobufjs/minimal": ["Reader", "Writer"]
        }
    },
    ...
}

要件を満たすことができたのでStencilを使っていくことにしました。

未検証

検証しようと思ったが全く触っていないものがこちらです。

Svelte/ElmについてはTwirpなどまだそこまで広く使われていないものを利用していく上で不安があり検証しなかった。(検証全くしていない上での感想) Hybridsに関してはOpen Source Award in 2019でBreakthrough of the Yearの賞を取っていたこともあり気になっていたがTypeScript使う上で大変そうだったのでやめました。 要件的に合いそうなものがあっればまた検証して行きたいなと思いました。

まとめ

色々試してみたが、IE11で動かすとなると設定が大変だったものが多かった。 一昔前だとIEだけでなくEdgeも気にしなくてはいけなかったが、2020/1/15からEdgeがChromiumベースになるので特別対応は不要そうでよかった。 MacのChromium版のEdgeを触っているがそこは普通に使えた。