アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Vue.jsでWeb Componentを作ってみた

morishitaです。

先日、Web Componentついて書きました。

tech.actindi.net

多くのブラウザで動作できるようになってきていることは確認できました。
ただ、実装方法についてはもっと良い方法がないものかと思いました。

と思っていたら、Vue.jsが Web Components もサポートしているではないですか!

で、やってみました。

環境準備

手っ取り早く試すためにVue CLIを使います。 次のコマンドでインストールできます。

$ yarn global add @vue/cli

インストールできたらプロジェクトを作ります。
このエントリではvue-web-componentsという名前で作りますが、 なんでも構いません。

次のコマンドを実行するといろいろ質問されます。

$ vue create vue-web-components

今回は次の様に答えました。

? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, CSS Pre-processors, Linter
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)
? Pick a linter / formatter config: Airbnb
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

見ての通りTypescript で、class-style のコンポーネント実装を選択しました。
VueによるWeb Componentsのお試しなので、テスト系は選択しませんでした。そのあたりは必要に応じて選択してください。

ということで本エントリで示すコードはTypescriptとなります。

モジュールのインストール等が走るので終わるまで待ちます。
次のメッセージが表示されたら完了です。

🎉  Successfully created project vue-web-components.
👉  Get started with the following commands:

 $ cd vue-web-components
 $ yarn serve

指示に従って、yarn serveでローカルサーバを立ち上げてみます。

f:id:HeRo:20190626082421p:plain
Vue APP

Vue.jsを触ったことがあればおなじみのページが表示されればOKです。

出来上がったファイル群は次の通りです。

f:id:HeRo:20190626082846p:plain
生成されるファイル群

上のページは、src/App.vueが表示された結果です。
public/index.htmlsrc/main.tsが読み込まれてsrc/App.vueがVueアプリケーションとして起動されます1

Vueアプリケーションを起動するは src/main.ts のようなVueインスタンスを生成するコード(次のnew Vue(...)の部分)が必要です。

import Vue from 'vue';
import App from './App.vue';

Vue.config.productionTip = false;

new Vue({
  render: h => h(App),
}).$mount('#app');

VueアプリケーションをWeb Components化してみる

Vue CLIのドキュメントWeb ComponentにVueの単一ファイルコンポーネント(*.vue)を Web Component としてビルドする方法が記述されています。

それに従って、src/App.vue をWeb Component化してみます。
といっても次のコマンドを実行するだけです。

$ vue-cli-service build --target wc --name vue-app src/App.vue

この結果、次のファイル群が生成されます。

dist
├── demo.html
├── img
│   └── logo.82b9c7a5.png
├── vue-app.js
├── vue-app.js.map
├── vue-app.min.js
└── vue-app.min.js.map

dist/demo.html は次のとおりです。

<meta charset="utf-8">
<title>vue-app demo</title>
<script src="https://unpkg.com/vue"></script>
<script src="./vue-app.js"></script>


<vue-app></vue-app>

とてもシンプルです。
先程、ビルドしたWeb Componentのコードであるvue-app.js を読み込み、カスタム要素<vue-app>を表示しているだけです。

でこれをブラウザで表示してみると次の通りです。

f:id:HeRo:20190626082742p:plain
Vue APP as Web Components

さっきと同じ画像を再掲載しているのかと思うかもしれませんが、URLバーを見るとdist/demo.htmlを表示していることがわかると思います。
すぐ試せるようにvue-app demoにも置いておきます。

実は、src/App.vue はその中でコンポーネントsrc/components/HelloWorld.vueを利用しています。ということは複数のVueコンポーネントで構成されたVueアプリケーションをまるごとWeb Component化できるということです。

また、通常のVueアプリケーションは前述のsrc/main.tsの様に起動処理が必要でした。一方、Web Component化すると表示したい場所でカスタム要素を書くだけです。

ビルドも、その利用もとても簡単です!

PopupをVueで実装してWeb Component化してみる

先日のエントリ「Web Componentsを試してみた」で作ったPopupと同等のものをVueコンポーネントとして実装してみます。

Pugを使いたいので次のコマンドで必要なモジュールを追加します。

$ yarn add -D pug pug-plain-loader

これだけで特に追加の設定なしで使えるようになります。vue-loaderバンザイ!!

で、Popupのコードsrc/components/Popup.vueは次のとおりです。

<template lang="pug">
div
  #popup(v-if="isOpen" @click="close")
    .dialog
      .title {{title}}
      .content {{content}}
  button(@click="open") OPEN
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class Popup extends Vue {
  @Prop() private title!: string;
  @Prop() private content!: string;
  private isOpen = false;

  private open() {
    this.isOpen = true;
  }

  private close() {
    this.isOpen = false;
  }
}
</script>

<style lang="sass">
#popup
  position: fixed
  background-color: rgba(0,0,0,0.3)
  width: 100vw
  height: 100vh
  top: 0
  left: 0

.dialog
  background-color: white
  width: 80vw
  margin: 100px auto 0 auto
  padding: 10px
  border-radius: 5px

.title
  font-weight: bold
</style>

次のコマンドでサクッとWeb Componentとしてビルドできます。

vue-cli-service build --target wc --name vue-popup src/components/Popup.vue

実行後 dist/ を見るとトランスパイルされたファイルが出力されています。

で動かしてみた様子が次のとおりです。 当たり前ですが、先日のエントリのものと同じ動作をします。

f:id:HeRo:20190626082929p:plain
Vue Popup
ここから試せます。 => Web Components - Vue Popup

@vue/web-component-wrapper

Vueコンポーネントは@vue/web-component-wrapperによってWeb Component化されています。
Requires ES2015 classesとのことですが、Can I use... で確認するとIE以外の主要なブラウザでは使えるようです。
Web Componentsの仕様に未対応ブラウザではwebcomponents.jsを使えば動くようです。

Vue CLIを使っていないプロダクトでもこのラッパーを使えばWeb ComponentsとしてVueコンポーネントを利用できそうです。

まとめ

すでに単一ファイル形式のVueコンポーネントがあるなら簡単にWeb Component化できます2。個人的にはVueの単一ファイル形式は、コンポーネントの要素すべてが1ファイルにまとまるのでポータブルでメンテナンス性もいいと思っています3。なのでWeb Componentを作る方法としてもおすすめな方法だと思います。

ただ、ビルドされたスクリプトにはVue本体を含まないので、HTMLには<script src="https://unpkg.com/vue"></script>を挿入する必要があります。これはCDNからVueのランタイムライブラリを読み込みます。これがVueクラスをページのコンテキストに晒してしまうのですでにVueを導入しているなら、バージョンには注意が必要でしょう4

使い所としては、モダンフレームワークを使っていない既存のWebアプリケーションに部分的にVue.jsを導入したいというケースにはうってつけだと思います。
メインコンテンツは普通にレンダリングして、どのページにも置くサイドコンテンツを遅延させたり動的に表示したいケースっていうのは結構あるんじゃないでしょうか。

最後に

アクトインディではエンジニアを募集しています。


  1. ソースコードを見てもわかりにくいと思いますが、yarn build でビルドしてみればわかります。public/index.htmlを雛形にdist/index.htmlが出力されます。その中でsrc/main.tsをトランスパイルしたものを読み込む<script>タグが挿入されています。

  2. ReactのドキュメントにもWeb Componentsの記述があります。また、adobe/react-webcomponent(Adobe !!)でReactコンポーネントをWeb Component化できそうです。

  3. ポータブルと言っても他のスクリプトをimportしていればそれらも必要になりますが…。

  4. https://unpkg.com/vue@2.6のようにバージョンを指定することもできます。