Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
9
Help us understand the problem. What is going on with this article?
@imunew

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

More than 3 years have passed since last update.

はじめに

既にある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を使っていこうと思います。
間違っているところ等ありましたら、コメントにてご指摘いただけますと助かります。

ではでは。

9
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
9
Help us understand the problem. What is going on with this article?