LoginSignup
7
9

More than 5 years have passed since last update.

ビューの一部分を`jQuery.ajax`と`Vue.js`で一定間隔で更新する

Last updated at Posted at 2017-02-06

はじめに

既にあるWebアプリケーションのビューをほとんどいじることなく、Vue.jsを導入して、一部の要素を一定間隔で更新することができましたので、ここに紹介します。

はじめた動機とVue.jsの印象

  • 既にあるWebアプリケーションのビューの一部の要素を一定間隔で更新したい
  • AngularReactなどのjavascriptフレームワークをまともに使わずにここまできた
  • jqueryで何が悪いの、とまでは言わないが、データをセットするためにDOMを扱いたくない
  • とはいえ実現したいのは、データバインディングだけで、SPAじゃない
  • Vue.js(もしくは他のフレームワーク)を導入するからといって、ビューをすべて作り直すようなことはしたくない
  • LaravelVue.jsをコアに取り込んだらしい
  • Vue.js良さそう from 『私たちはなぜReactではなくVue.jsを選んだのか | プログラミング | POSTD

Vueインスタンスはアプリケーション?

公式ガイドの「はじめに」をみていると、Vueインスタンスはアプリケーションとして扱うのが流儀みたいにみえます。

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

アプリケーションって具体的に何って話ですが、このコードを見たときの僕の印象は下記のとおりです。

  • ビューを構成する単なる一要素ではない?
  • 子要素を包括する存在?
  • 子要素はVue.componentで扱うべき?

VueインスタンスはViewModel?

「はじめに」の次のページに「Vue インスタンス」という章があり、そちらでは、VueインスタンスはViewModelとして扱われています。

var vm = new Vue({
  // オプション
})
Vue のデザインは、MVVM パターンと厳密には関連が無いものの、部分的には影響を受けています。
慣例として、 vm (ViewModel の略) を Vue インスタンスの変数名としてよく使います。

シングルページアプリケーションみたいに、全体を管理したい場合は、アプリケーションとして扱って、配下にコンポーネントを配置したほうが効率的で、部品的に使う場合は、ViewModelとして扱うのが良さそうです。

Vueインスタンスを動的に生成、更新するクラスをES6で実装

サーバーサイドで使われる言語と同様、フロントエンドでもES6のclass構文を使って、クラスを実装していきます。
最終的には、下記のクラスを実装し、webpack+babelでES5に変換してビュー側で読み込みます。

class SomeViewModels {

    constructor(endpoint, selector) {
        this.endpoint = endpoint;
        this.selector = selector;
    }

    bind() {
        let $elements = $(this.selector);
        let self = this;

        $.each($elements, function(index, value){
            let $element = $(value);

            new Vue({
                el: '#' + $element.attr('id'),
                data: {
                    id: $element.data('id'),
                    a: '',
                    b: '',
                    c: ''
                },
                created: function() {
                    self._getData(this);
                }
            });
        });
    }

    _getData(vue) {
        let self = this;
        $.ajax({
            url: this.endpoint + '?id=' + vue.id,
            dataType: 'json'
        }).then(function(data){
            self._bindData(vue, data);
        }).always(function(){
            setTimeout(function(){
                self._getData(vue);
            }, 60 * 1000);
        });
    }

    _bindData(vue, data) {
        if (!data) {
            return null;
        }
        $.each(data, function(index, value){
            vue.a = value.a;
            vue.b = value.b;
            vue.c = value.c;
        });
    }
}

$(document).ready(function(){
    let endpoint = $('#api-endpoint').data('api-endpoint');
    let selector = '.some-target';
    let vms = new SomeViewModels(endpoint, selector);
    vms.bind();
});

尚、テンプレート側は、下記のように、v-text属性を追加してあげるだけで、データバインドされます。
テンプレート構文( {{ }} )だとエスケープしたりしないといけないので、v-text属性を使う方が便利です。

<div v-text="a"></div>
<div v-text="b"></div>
<div v-text="c"></div>

その他(環境まわりなど)

webpack+babelを使った環境構築するまでの記事はたくさんあるので、ここでは細かく説明しませんが、ポイントになりそうなところは整理しておきます。

Vue.js 本体はCDNのものをそのまま利用

Vue.js自体をwebpackでビルドする方法もありそうなんですが、特に必要性がないので、CDNに置いてあるものをそのまま使います。
httpとhttps両方に対応しているので、スキーマレスで書いても問題ありません。

<script src="//unpkg.com/vue@2.1.10/dist/vue.min.js"></script>

package.json

{
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "babel-core": "^6.22.1",
    "babel-loader": "^6.2.10",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-preset-es2015": "^6.22.0",
    "babel-runtime": "^6.22.0",
    "cross-env": "^3.1.4",
    "css-loader": "^0.26.1",
    "stylus": "^0.54.5",
    "stylus-loader": "^2.4.0",
    "webpack": "^2.2.1"
  },
  "dependencies": {
    "es6-promise": "^4.0.5"
  }
}

webpack.config.js

webpackの設定ファイルは下記のとおりです。

const webpack = require('webpack');

module.exports = {
    context: __dirname + "/src",
    devtool: 'source-map',
    entry: {
        app: ["./scripts/app.js"]
    },
    output: {
        filename: "scripts/[name].min.js",
        libraryTarget: 'var',
        path: __dirname + "/dist"
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true
        })
    ],
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015'],
                }
            }
        ]
    }
};

細かい話ですが、webpack.optimize.UglifyJsPluginを使う場合は、引数にsourceMap: trueを指定するとともに、devtool: 'source-map'を指定しないと、mapファイルが生成されなかったです。

おわりに

結果的に、既にあるWebアプリケーションのテンプレートに、v-text属性をつけ加えるだけで、対象の要素を一定間隔で更新することができました。
他のフレームワークはともかく、これからは、積極的にVue.jsを使っていこうと思います。
間違っているところ等ありましたら、コメントにてご指摘いただけますと助かります。

ではでは。

7
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9