WebComponentsを各種ライブラリを使って試したメモ

## web componentでHello World

WEB+DBで記事の特集をしていたので試してみた。
ビルド環境の作成にはdockerを使用。
ついでに、angularやelmなども試した。

[この時点のソース](https://github.com/hibohiboo/develop/tree/5fd952635f8399eb2716636e9ddeabaa23cf05bf/tutorial/lesson/web-components/web-components)  

## サイズ比較
Hello worldを表示するだけのWeb Componentでの比較なので、
ほぼライブラリ部分の容量。

```console
vagrant@ubuntu-bionic[master]: $ ls ../server/dist/ -alh
total 448K
drwxrwxrwx 1 vagrant vagrant    0 May 10 08:33 .
drwxrwxrwx 1 vagrant vagrant    0 May 10 08:33 ..
-rwxrwxrwx 1 vagrant vagrant 3.2K May 10 20:55 tsApp.js
-rwxrwxrwx 1 vagrant vagrant  26K May 10 20:56 elmApp.js
-rwxrwxrwx 1 vagrant vagrant  79K May 10 20:57 vueApp.js
-rwxrwxrwx 1 vagrant vagrant 120K May 10 20:47 reactApp.js
-rwxrwxrwx 1 vagrant vagrant 215K May 10 08:07 angularApp.js
```

## 記述比較

### Vue

```vue:vue-app/src/App.vue
<template>
  <div>
    <p>Vue-app</p>
  </div>
</template>
```

```js:vue-app/src/index.js
import Vue from 'vue';
import App from './App';
import vueCustomElement from 'vue-custom-element';

Vue.use(vueCustomElement);

Vue.customElement('vue-app', App);
```

### React

```react:react-app/src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  render() {
    return <div>React-app</div>;
  }
}

// Custom ElementとするためにHTMLElementを継承
class CustomElementsApp extends HTMLElement {
  connectedCallback() {
    const mountPoint = document.createElement('div');
    this.attachShadow({ mode: 'open' }).appendChild(mountPoint);
    ReactDOM.render(<App />, mountPoint);
  }
}

customElements.define('react-app', CustomElementsApp);
```

### Elm

```elm:elm-app/src/App.elm
module App exposing (main)

import Html exposing (Html, text)


main : Html msg
main =
    text "Elm App"
```

```js:elm-app/src/index.js
const { Elm } = require("./App.elm");

class ElmApp extends HTMLElement {
  connectedCallback() {
    Elm.App.init({ node: this });
  }
}

customElements.define("elm-app", ElmApp);
```

### Angular

```ts:angular-app/src/app.component.ts
import { Component, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { HelloComponent } from './hello/hello.component';

@Component({
  selector: 'angular-app',
  template: ``,
})
export class AppComponent {
  constructor(
    private injector: Injector,
  ) {
    const AppHelloElement = createCustomElement(
      HelloComponent,
      { injector: this.injector }
    );
    customElements.define('angular-app', AppHelloElement);
  }
}
```

```ts:angular-app/src/app/hello/hello.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-hello',
  templateUrl: './hello.component.html',
  styleUrls: ['./hello.component.css']
})
export class HelloComponent {
  title = 'angular-app';
}
```

```html:angular-app/src/app/hello/hello.component.html
<!--The content below is only a placeholder and can be replaced.-->
<div>
  {{ title }}!
</div>
```

```ts:angular-app/src/app/app.module.ts
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello/hello.component';

@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [AppComponent, HelloComponent],
  bootstrap: [AppComponent],
  entryComponents: [HelloComponent]
})
export class AppModule { }
```

```ts:angular-app/src/main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));
```

### Typescript

```ts:ts-app/src/index.ts
class TsApp extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    const wrapper = document.createElement('span');
    wrapper.setAttribute('class', 'wrapper');
    const info = document.createElement('span');
    info.setAttribute('class', 'info');
    info.textContent = 'ts-app';
    shadow.appendChild(wrapper);
    wrapper.appendChild(info);
  }
}
customElements.define('ts-app', TsApp);
```


## ひっかかったところ

angularは最新版のtsやcore-jsではエラーとなるらしい。。
あとコンテナにchromeが入っていなかったのでインストール。これはkarma用。

```Dockerfile:angular-app/docker/angular/Dockerfile
FROM node:11.15.0

# コンテナ上の作業ディレクトリ作成
WORKDIR /app

RUN yarn add --dev \
  @angular/compiler-cli \
  @angular-devkit/build-angular \
  @angular/cli \
  @angular/language-service \
  @types/jasmine \
  @types/jasminewd2 \
  @types/node \
  codelyzer \
  jasmine-core \
  jasmine-spec-reporter \
  karma \
  karma-chrome-launcher \
  karma-coverage-istanbul-reporter \
  karma-jasmine \
  karma-jasmine-html-reporter \
  protractor \
  ts-node \
  tslint

RUN yarn add \
  @angular/animations \
  @angular/common \
  @angular/compiler \
  @angular/core \
  @angular/elements \
  @angular/forms \
  @angular/http \
  @angular/platform-browser \
  @angular/platform-browser-dynamic \
  @angular/router \
  rxjs \
  zone.js \
  document-register-element 

# ERROR in The Angular Compiler requires TypeScript >=3.1.1 and <3.3.0 but 3.4.5 was found instead.
RUN yarn add --dev typescript@3.2.x
# core-js 3.0系だとエラー
RUN yarn add core-js@2.6.x
# test用
RUN \
  apt-get update && \
  apt-get install -y wget curl unzip apt-utils && \
  mkdir -p /home/root/src && cd $_ && \
  wget -q -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip && \
  unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/ && \
  apt-get install -y libappindicator1 fonts-liberation libasound2 libnspr4 libnss3 libxss1 lsb-release xdg-utils && \
  touch /etc/default/google-chrome && \
  wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 
RUN apt-get install -y libappindicator3-1 \
  libatk-bridge2.0-0 \
  libatspi2.0-0 \
  libgtk-3-0 
RUN cd /home/root/src && wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb  && dpkg -i google-chrome-stable_current_amd64.deb && \
  apt-get install -y fonts-migmix
```


## 参考

[WEB+DB PRESS vol.110](https://gihyo.jp/dp/ebook/2019/978-4-297-10534-1)
[単一コンポーネントのエラーの」解決](https://qiita.com/magaya0403/items/3fbe9aa20c6a66b76662)
[Docker Composeを使ってサクッとNginxコンテナを起動する](https://qiita.com/NagaokaKenichi/items/c03366f0b84b12c0a30d)
[Elm と他のフレームワークを組み合わせる](https://qiita.com/calmery/items/d85e4755a4d0644c0610)
[npm](https://stackoverflow.com/questions/55308769/module-not-found-error-cant-resolve-core-js-es6)
[angular component](https://mae.chab.in/archives/60134#post60134-5)
[docker-composeでangular-cliの環境を作ってみる](https://qiita.com/friedaji/items/c3aba48542872f029c21)
[Dockerでheadless-chromeを使ったスクレイピング環境を整える](https://qiita.com/rotelstift/items/7dafcdcae3ca18b65b26)
[UbuntuにChromeをインストールする](https://yoshinorin.net/2017/07/24/install-chrome-to-ubuntu/)
[karma headless chrome](https://github.com/karma-runner/karma-chrome-launcher/issues/158)
[lit-htmlとバニラWeb Componentsでコンポーネントを実装する](https://qiita.com/aggre/items/3ed2558a9fb8ba887385)
[“Web Componentsだけ” で新サービスを実装して見えたこと](https://qiita.com/aggre/items/9d77085a32764ffe994d)