Vue.js

Vueで後から読み込んだHTMLをコンポーネントにしたい

More than 3 years have passed since last update.

こんにちは。また新卒エンジニアの@nekobatoです。

Vueと闇の話します。

Vueが闇ってことじゃなくて、Vueで闇を作る話です。


テンプレートではなく、HTMLを読み込んでVueに繋ぎたい

用意するHTMLにv-**みたいなdirectiveは用意できるが、<script type="x-template">では囲えない。

なぜならそのHTML自体は他で使っていて、見た目に影響を出したくないから。

SPAにしろよという話でもあるのですが、今すぐ既にあるSPAに他所のHTMLを適用させたい、という場合に手っ取り早い方法を考えた所、こういうことになりました。

前提条件


  • JSファイルは一つ

  • コンテンツはXHRでHTMLを受け取る

  • さもSPAのように振る舞わせたい

  • 読み込んだHTMLはVueで操作するのでbindingが効いている必要がある


できないこと

Vueのcomponent内へ単純に後からinnerHTMLやappendChildなどで

<p>{{greatText}}</p>

みたいなものを突っ込んでも、当然ながら変換されません。

Vueのcomponentが生成されるタイミングでバインディングは作られるので、後からHTMLを追加してもbindingは効きません。

参考:http://jp.vuejs.org/guide/instance.html#ライフサイクルダイアグラム

「Vueにbiningをし直せる機能が入って無いかな?」と思って探しましたが、無かったので力技を使いました。

ちなみにvm.$mountというものがありますが、これは待機させておいたインスタンスを非同期で生成するための関数なので、そのまま「もう一度」には使えません。


Vue componentの再生成


用意

最初にこんな構造を作ります


  • root Vue


    • component1



単純ですね。そしたらcomponent側の設定はこうなります

component1Object = {

template: '<p>{{hoge}}</p>',
replace: false

Vue.component('component1', Vue.extend(component1Object));

そしてhtml側はこうなります

<script id="hoge_template" type="x-template">

<!-- 初期表示のなにか -->
</script>

<div id="#vue_root">
<div id="component1"></div>
</div>


HTMLを持ってくる

XHRでもあらかじめ用意されたものでもよいので、バインディングしたいHTMLをObjectに突っ込みます

component1.template = '<p>{{greatText}}</p>'

そしてcomponent1を消し去ります

this.$children[0].$destroy()

そしてまた生成します

Recomponent1 = Vue.extend(component1);

new Recomponent1().$mount('#component1');

そうすると、新しいtemplateで新たなcomponent1が生成されるわけですね。

簡単です。

こうしてまた一つ闇が生まれたのです。


$destroyが力技に便利

$destroyで完全にVueインスタンスを消しされるのと、中身が単なるObjectであるVueならではの力技でした。

これ以上にスマートな方法があったら教えてほしいです。

次は@masasuzuさんです。よろしくお願いします。