レガシィシステムでVueとPHPの連携を考える

※考察なので無駄に長いです。

結論から書くと**[SystemJS](https://github.com/systemjs/systemjs)**がベストかな。

ヾ(・ω<)ﾉ"　三三三●　ⅱⅲ コロコロ♪


----
------------------- ↓ 余談はここから ↓-------------------


Angularが出たときは、酷ぇ物が流行ってるなぁと思い、
Reactが出たときは、思想は素晴らしいけど使い物にならないなぁと思い、
webpackやらBabelやらバッチやモジュールにあふれる世界をみると、
近寄りたくねぇと思っていた。

半年も経てばシステム丸っと過去のものになるJavascript界隈ですが、
ようやく使えそうなフレームワークに出会った。
それが**[Vuejs](https://jp.vuejs.org/index.html)**。
これもVer1の頃は様子見でVer2になってから触り始め、
最近漸く[Vuejs](https://jp.vuejs.org/index.html)を仕事で扱う機会があったという感じ。

[Vuejs](https://jp.vuejs.org/index.html)には単一ファイルコンポーネント(Single File Component, SFC)という\<template\>, \<script\>, \<style\>を一つのファイルとして管理する仕組みがある。
単純にPHPでSFCを出力して、
[Vuejs](https://jp.vuejs.org/index.html)側で動的に取り込めばいいじゃん(lazy loadというらしい)と思ったが、
そう簡単にはいかないようだ。
JSの人たちの、バッチでどうにかしときゃええやろ精神はどうにかならんもんかな。




さて、PHP5.2にPHPUnitを入れる、
なんて需要のない記事を書いていることからわかるように、
仕事で古いシステムを扱うことが多い。

それにReactやAngularのような最近のJavaScriptフレームワークを組み込もうにも

 * Nodejsサーバが立てられない
 * JavaScriptのソース管理やデプロイが確立していない
 * PHP V8Jsが使えない
 
なんてざら。
こんな状態で組み込みなんてできるのかどうか。
その辺を模索してみる。
思想的には以下のような感じ。

* PHPはSFC単位で出力
* JSをSFCを動的に呼び出し
* 事前バッチはなるべくなくす(必須ではない)



----
------------------- ↓ 本題はここから ↓-------------------


## HTML Imports
Google提案で、HTMLを分割管理できる機能。
WebComponents四人衆で一番期待していた。
Mozillaがすっかり老害化したおかげで風前の灯火となった。
> Firefox での Web Components のサポート状況
> https://developer.mozilla.org/ja/docs/Web/Web_Components/Status_in_Firefox

(ES Modulesで同じことできるとかぬかしおって。できねーっての)
とはいえ、polymerを駆使すればちゃんと動作する。

<details><summary>HTML Importsサンプルコード</summary><div>

```html:index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>VueJS Component Test</title>
  <script src="https://unpkg.com/vue"></script>
  <script defer src="https://unpkg.com/@webcomponents/webcomponentsjs@1/webcomponents-loader.js"></script>
</head>

<body>
  <link rel="import" href="./src/example.vue">
  <div id="wrapper">
    <my-component></my-component>
  </div>
</body>
<script>
  window.addEventListener('WebComponentsReady', function (e) {
    var app = new Vue({
      el: '#wrapper',
    });
  });
</script>

</html>
```
```html:src/example.vue
<template>
  <div @click="changeName()">
    Hello, {{name}}.
  </div>
</template>

<script>
var doc = document.currentScript.ownerDocument;
var template = doc.querySelector("template");

Vue.component("my-component", {
  template,
  data() {
    return {
      name: "world"
    };
  },
  methods: {
    changeName() {
      this.name = "foobar";
    }
  }
});
</script>

<style lang="less" scoped>
div {
  font-weight: bold;
  color: red;
}
</style>
```

</div></details>

サンプルソースはここ。
https://github.com/dohzoh/vue-example-htmlimport/tree/master/html-import


## [RequireJS](https://requirejs.org)
動的にJSをロードするということで真っ先に思いつく[RequireJS]。
最古参のシステムで[JQuery](https://jquery.com)や[UnderScore](https://underscorejs.org)の頃は本当にお世話になった。
[RequireJS](https://requirejs.org)の[Vuejs]プラグインに[require-vuejs](https://github.com/edgardleal/require-vuejs)があるのでそれを使う。

サンプルだが、HTMLImportsの時とは全体的に異なるので注意。

<details><summary>requirejsサンプルコード</summary><div>

```html:index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>VueJS Component Test</title>
  <script src="https://unpkg.com/requirejs/require.js"></script>
</head>

<body>
  <div id="wrapper">
    <my-component></my-component>
  </div>
</body>
<script>
  require.config({
    paths: {
      'Vue': 'https://unpkg.com/vue/dist/vue',
      'vue': 'https://unpkg.com/require-vuejs/dist/require-vuejs',
    },
  });
  require(['Vue', 'vue!src/example.vue'],(Vue, App) => {
    var app = new Vue({
      el: '#wrapper',
      components:{
        myComponent: App,
      },
    });
  });
</script>

</html>
```

```html:src/example.vue
<template>
  <div @click="changeName()">
    Hello, {{name}}.
  </div>

  <style>
    div {
      font-weight: bold;
      color: red;
    }
  </style>  
</template>

<script>
define(['Vue'], (Vue) =>{
  return Vue.extend({
    template: template,
    data() {
      return {
        name: "world"
      };
    },
    methods: {
      changeName() {
        this.name = "foobar";
      }
    }
  });
});
</script>
```
</div></details>

サンプルソースはここ。
https://github.com/dohzoh/vue-example-htmlimport/tree/master/requirejs/

## [SystemJS](https://github.com/systemjs/systemjs)
Angularでも使われている[RequireJS](https://requirejs.org)互換のモジュールローダー。
公式の[systemjs-plugin-vue](https://github.com/vuejs/systemjs-plugin-vue)はコマンドライン用なので今回は使わず、
マスクライブラリである[getlibs](http://getlibs.com)を使う。
(この名前はどうなんだろうなぁ。)

<details><summary>SystemJS サンプルコード</summary><div>

```html:index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>VueJS Component Test</title>
  <script src="https://unpkg.com/getlibs"></script>
</head>

<body>
  <div id="wrapper">
    <my-component></my-component>
  </div>
</body>
<script>
  SystemJS.config({
    map: {
      'Vue': 'https://unpkg.com/vue/',
    }
  });

  Promise.all([
    System.import("Vue"),
    System.import("./src/example.vue"),
  ]).then(([Vue, App]) => { 
    var app = new Vue({
      el: '#wrapper',
      components: {
        myComponent: App,
      },
    });
  });
</script>
</html>
```
```html:src/example.vue
<template>
  <div @click="changeName()">
    Hello, {{name}}.
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "world"
    };
  },
  methods: {
    changeName() {
      this.name = "foobar";
    }
  }
};
</script>

<style lang="less" scoped>
div {
  font-weight: bold;
  color: red;
}
</style>
```
</div></details>

サンプルソースはここ。
https://github.com/dohzoh/vue-example-htmlimport/tree/master/systemjs

## 独自実装系
\<template>, \<script>, \<style>を独自に分解して各モジュールをロードする。

### [http-vue-loader](https://github.com/FranckFreiburger/http-vue-loader)

以下の記事をご覧ください。

> Nodeがなくてもvueファイルを使いたい
> https://qiita.com/horikeso/items/d2f00c39e8571f3ff310


### その他、独自実装系

ほかにもいくつかあるが列挙だけしておく。

* https://github.com/MingruiZhang/vue-in-browser
* https://github.com/RonnieSan/vue-browser-sfc
* https://github.com/imdreamrunner/browser-vue-loader


## ES Modules
scriptタグのtype属性にmoduleと記述し、
import記法を有効にしたもの。
上記HTML Importsが消える原因。

https://vuejsdevelopers.com/2017/09/24/vue-js-single-file-javascript-components/
https://jakearchibald.com/2017/es-modules-in-browsers/
https://developers.google.com/web/updates/2017/11/dynamic-import

これに関しては事前にコンバートが必要。
[rollupjs](https://rollupjs.org/guide/en)がおすすめ。

ES Modulesのサンプルはhtml部分だけ載せておく。
リンク先にJSもあるので、
そちらを参照のこと。

<details><summary>ES Modulesサンプルコード</summary><div>

```html:index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>VueJS Component Test</title>
  <script src="https://unpkg.com/vue"></script>
</head>

<body>
  <div id="wrapper">
    <my-component></my-component>
  </div>
</body>
<script type="module">
    import App from "./src/example.js";

    var app = new Vue({
      el: '#wrapper',
      components: {
        myComponent: App,
      },
    });
</script>
</html>
```
</div></details>

サンプルソースはここ。
https://github.com/dohzoh/vue-example-htmlimport/tree/master/es-modules/

## 考察
あえて、ESModulesを使う理由はない。
予め置換しておく必要がある上に、
IE対応が微妙なので論外だ。(Mozilla〇ね)
ならば、Webpack系列でファイル1枚にするのと大して変わらん。

独自実装系はやんちゃにFunction関数(要するにeval)を使っているので、
個人用途に限られるだろう。

HTML ImportsはVueとの相性は抜群だった。
ただ、残念ながら今後ブラウザでサポートされることはない。
polymerを一生抱えるシステムを創るとなるとリスクは高いか。
インポートの度にlinkタグを生成するとなると取り回しもちょっと悪い。

となると、[RequireJS](https://requirejs.org)か[SystemJS](https://github.com/systemjs/systemjs)という寂しい選択肢になったが、
こちらはお好みでという感じかなと思う。
ただ、vueファイルがAMD記法(define)である[RequireJS](https://requirejs.org)より、
ES6記法(export default)が使える[SystemJS](https://github.com/systemjs/systemjs)の方が、
将来的なシステム取り回しにも対応できるだろう。


----
------------------- ↓ 後書はここから ↓-------------------

JavaScript界隈の人たちは同じところをぐるぐる回るのがお好きなようで、
最近の話題がSSR(サーバーサイドレンダリング)だそうだ。
サーバーサイドはRestAPIだけ作っとけばいいとさんざん言ってきた連中が、
SSRとか言ってんだから失笑ものだわな。

ここまでやって、[HyperApp](https://hyperapp.js.org)なるものがあることを知った。
また１から調査かな。