Development of Custom Element using Vue.js #kyotojs

このスライドは社内勉強会で発表しようと思って作ったけど、年末でほとんど人がいなかったので供養として #kyotojs vol.15 で発表したものとなります。

- - - 

<img src="https://potato4d.me/real.png" width="120" height="120">

- potato4d
  - real name: HANATANI Takuma
- organizations
  - ElevenBack (self-employed)
  - LINE Corp
- Maintainer at
  - vuejs/jp.vuejs.org
  - nuxt/docs
- Web Application Developer
  - ♥ Vue.js, Nuxt.js, Node.js(w/ TypeScript), Serverless
  - ♥ User interface, Micro interaction, Product development

- - - 

## 今日話すこと

- 開発したものデモ
- Web Components - Custom Elements の開発手法について
- Vue CLI v3 を利用した、 Vue.js ベースの Custom Elements 開発
- 実際の開発フローについて
- Vue.js での開発フローの罠
- 使いどころ

- - - 

## 注意

- - - 

<img width="1353" alt="Screen Shot 2018-12-28 at 12.47.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/75633/ccb72774-db55-68ef-a245-a8426a791f29.png">

- - -

## 開発したもの

- - - 

## Kamishibai Viewer

[![Image from Gyazo](https://i.gyazo.com/447d26f36fd0d346464be0901ee51ddc.gif)](https://gyazo.com/447d26f36fd0d346464be0901ee51ddc)

- [アドカレのネタ](https://qiita.com/potato4d/items/44477faf766463f06ec9)
- Qiita のスライドモードの埋め込みビューアー
- SlideShare / Slides.com / SpeakerDeck はサクッと埋め込めるけど Qiita は無理だった
- OSS で作ったらみんな嬉しそうなので開発
- **Vue.jsで作られている**

- - - 

## Custom Elements の開発手法

そのままやるなら

- [lit-html](https://github.com/Polymer/lit-html)
  - 一番薄い
  - そこまで嬉しくはない
- [lit-element](https://github.com/Polymer/lit-element)
  - 人間向き
  - class & render 関数
  - 最近 rc が出てそろそろ stable に？

- - - 

## Custom Elements の開発手法

フレームワークでやるなら(1)

- [Angular Elements](https://angular.jp/guide/elements)
  - Angular で作れる
  - まだばりばり開発中(らしい)
  - バンドルサイズがでかい(Angular全部入る)
  - Ivy がきたら min 12kb からになる(らしい)

- - - 

## Custom Elements の開発手法

フレームワークでやるなら(2)

- [Vue CLI v3](https://cli.vuejs.org/guide/build-targets.html#web-component)
  - Vue.js で作れる
  - バンドルサイズはマシ(でもまぁフルで入る)
  - ちょっと癖がある

- - - 

## 言いたいこと→Custom Elements はフレームワークで作れる 

- - - 

## Vue CLI v3 での開発

- - - 

## ざっくりと手順

- - - 

## 環境の構築

```bash:terminal
$ vue create my-wc-project
$ yarn add @vue/web-component-wrapper
```

- - - 

## package.json の編集

```json:package.json
{
  "name": "my-wc-project",
  "scripts": {
    "build": "VUE_CLI_CSS_SHADOW_MODE=true vue-cli-service build --target wc --name my-component ./src/wc.ts",
    "...": "..."
  },
  "...": "..."
}
```

- - - 

## 専用の Entrypoint の作成

```ts:src/wc.ts
import Vue from 'vue'

// TS の定義はない
const { default: wrap } = require('@vue/web-component-wrapper')

// ?shadow を使って Shadow DOM での Scoped CSS を有効化するのでこう読む
const { default: MyComponent } = require('./MyComponent.vue?shadow')

window.customElements.define('my-component', wrap(Vue, MyComponent))
```

- - - 

## コンポーネントのスクラッチ

```vue:src/MyComponent.vue
<template>
  <div>
    <p>
      <span>Hi, {{username}}</span>
    </p>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
  props: {
    username: String
  }
})
</script>

<style scoped>
p span {
  padding-bottom: 8px;
  border-bottom: solid 2px #77CB5B;
}
</style>
```

- - - 

## ビルド

```bash:terminal
$ yarn build
```

- - - 

## :tada: 完成 :tada:

<img width="800" alt="Screen Shot 2018-12-28 at 14.55.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/75633/74c7fc0d-feff-d3d6-7386-d64b2ce6fb07.png">

- - - 

## 実際やってみる(時間があれば)

- - - 

## スライドだけみてるかたは⇣

https://github.com/potato4d/vue-cli-wc-export-minimal-example

- - - 

## ……

- - - 

## ……で、これなにが嬉しいんだっけ

- - - 

## Custom Element の嬉しさ(1)

- (半)静的コンテンツへの埋め込み
  - 今回の Embed みたいに SPA とは疎結合で使いたいものは良さそう
  - Twitter Button は Web Components 製
  - Ad とかには最適なのでは
- 内部実装の共通化
  - 最終的に出てくるのが WC という共通言語なら技術間の取り回しが良い
  - けどこれはまだまだ先だと思う

- - -

## Custom Element の嬉しさ(2)

- メモリから素直な DOM の世界に
  - 仮想 DOM とかの都合もあって今は Fw のメモリ上に DOM がある
  - 隔離された空間から public な DOM になるのは良し悪し含めある
- Shadow DOM(Scoped CSS) 周りの統一
  - 仕様上は Shadow DOM に統合されている(2018/12現在)
  - ようやっと Fw によるオレオレ Scoped CSS じゃなくなる？

- - -

## その他所感

- 正直 SPA 作るだけなら Fw から吐き出せても「それで？」感
- Embed / Ad などでは普通に使われそうなので良いのでは
- Draggable みたいな Vanilla & Fw wrapper の世界にはまだ早そう
  - ああいうのはラッパーが便利な点に価値が
- 一応多少触っておくと雑談力にはなりそう　

- - - 

## Thanks