眠いしお腹すいたし(´・ω・`)

C#関連を主に書きます。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ

ごあいさつ

年末ですね。皆さんはいかがお過ごしでしょうか。

今年もクリスマスとかいう私には縁の無いイベントを無事何事もなく家で寝て過ごし、来年に向けて断捨離を進めなければいけないのに全く進まず、色々とやってみたい事だけが溜まっていって一つも消化できていない、そんな年末を過ごしています\(^o^)/

はじめに

最近。WebフロントエンドとAzure Mobile AppとAzure Functionsの組み合わせでついぶくというサービスを作っていて今はWebフロントエンドの勉強などをしています。

4年くらい前で止まっていたWebフロントエンドの知識を、今時な?Webフロントエンドな知識に更新することが一つの目標です。

暫くはサービス開発で得た知識を記事にしていきたいと思います。

今回はVisualStudio2015でASP.NET WebAPIプロジェクトにてVue.js + Bootstrapな開発環境の構築を行ってみたいと思います。

インストール関連

まずはnode.jsをインストールします。VS2015にてnode.jsをインストールすることも可能なのですがバージョンが古くて(´・ω・`)なので最新版を入れてしまいます。

Node.js

コマンドプロンプトにてnode -vを実行してインストールを行ったnodeバージョンが出ればOKです。

次にVisual Studioを開き[ツール]-[オプション]-[プロジェクトおよびソリューション]-[外部Webツール]にて

f:id:tamafuyou:20161229023834p:plain

PATHの順位を一番に持っていきます。

さらに[ツール]-[拡張機能と更新プログラム]にて NPM Task Runnerをインストールします。

f:id:tamafuyou:20161229024137p:plain

さらにWeb Compilerをインストールします。

f:id:tamafuyou:20161229042832p:plain

プロジェクト作成

まずはWeb APIプロジェクトを作成します。

f:id:tamafuyou:20161229025229p:plain

次のBundle処理の削除と余分なnugetパッケージの削除を行います。

BundleConfigクラスのRegusterBundle関数内をコメントアウトしてしまってください。

f:id:tamafuyou:20161229030839p:plain

nugetパッケージマネージャにて以下のパッケージを削除します。

  • bootstrap
  • jquery
  • Modernizr
  • Respond

次にViews/Shared/_Layout.cshtmlにてBundleしたファイルの読み込みコードを削除してしまいます。

この状態にてデバッグを開始すると

f:id:tamafuyou:20161229032058p:plain

スタイルがない状態になります。

Bootstrapの組み込み

今回はBootstrapをそのまま使うのではなく

honokak.osaka

を適用して、さらにscssにてスタイルの管理を行えるようにしてしまいます。

方法はこちらの記事を参考にしました。

katsuyuzu.hatenablog.jp

まずはpackge.jsonをプロジェクトに追加します。

中身はこんな感じ

{
  "version": "0.0.0",
  "name": "vuesample",
  "private": true,
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-bower-task": "~0.4.0"
  }
}

package.jsonはnpmというnode.js版nuget的なパッケージ管理ツールの設定を記述するためのファイルです。

作成しましたらタスクランナーエクスプローラよりInstallを実行します。

スクランナーエクスプローラ

f:id:tamafuyou:20161229034853p:plain

ここから表示できます。

f:id:tamafuyou:20161229034625p:plain

次にbower.jsonファイルをプロジェクトに追加します。

bowerはWebフロントエンドに特化したパッケージマネージャーです。

bower.jsonはその設定ファイルになります。

中身はこんな感じ

{
  "name": "VueSample",
  "private": true,
  "dependencies": {
    "jquery": "^3.1.1",
    "bootstrap-sass": "3.3.7",
    "Honoka": "^3.3.7"
  },
  "exportsOverride": {
    "jquery": {
      "js": "dist/*.*"
    },
    "bootstrap-sass": {
      "bootstrap.scss": "assets/stylesheets/bootstrap",
      "bootstrap.fonts": "assets/fonts/bootstrap/*.*",
      "js": "assets/javascripts/bootstrap.*"
    },
    "Honoka": {
      "honoka.scss": "scss",
      "honoka.css": "dist/css",
      "honoka.font": "dist/fonts",
      "honoka.js":  "dist/js"
    }
  }
}

exportsOverrideという設定が重要です。 これにより各パッケージのファイルを、展開用にグループ分けしています。

次にgrundfile.jsというJavaScriptファイルをプロジェクトに追加します。

中身はこんな感じ

/// <binding />
module.exports = function (grunt) {
    grunt.initConfig({
        bower: {
            install: {
                options: {
                    targetDir: "",
                    cleanTargetDir: false,
                    layout: function (type, component, source) {
                        if (type === "js") {
                            return "Scripts";
                        }
                        else if (type === "honoka.scss") {
                            return "Content";
                        }
                        else if (type === "bootstrap.scss") {
                            return "Content/honoka/bootstrap";
                        } 
                        else if (type === "bootstrap.fonts") {
                            return "fonts";
                        }
                        else {
                            return "__untyped__";
                        }
                    }
                }
            }
        }
    });

    grunt.registerTask("default", ["bower:install"]);
    grunt.loadNpmTasks("grunt-bower-task");

};

先ほどbower.jsonのexportsOverrideにて設定したグループごとにレイアウトを設定しています。

これによりreturnで返答しているフォルダにファイルが展開されます。

スクランナーエクスプローラよりgruntfile.jsのbower:installを実行してください。

もしoperation not permitted, mkdir というエラーが出ていたら手作業でフォルダを作成して再度実行してください。

f:id:tamafuyou:20161229041611p:plain

これらのファイルをプロジェクトに取り込みたいのでcsprojファイルを少し修正します。

csprojファイルをエディタで開いて以下を追加します。

    <Content Include="Content\**\*.scss" />
    <Content Include="Scripts\**\*.js" />
    <Content Include="Scripts\**\*.map" />
    <Content Include="fonts\**\*.*" />

これによりプロジェクトツリーのContentフォルダ下にbootstrap.scssファイルが追加されていますので右クリックしてコンパイルをしてみてください。

f:id:tamafuyou:20161229043737p:plain

cssファイルが出来上がっていると思います。

ではViews/Shared/_Layout.cshtmlファイルに作成したcssファイルを指定してデバッグを実行します。

f:id:tamafuyou:20161229044232p:plain

これでようやくスタイルが適用できた状態です。

ここからVue.jsの組み込みと簡単なコーディングを行っていきます。

Vue.jsの組み込み

まずは先ほど作成したpackage.jsonに追記を以下のように行ってください。

  "version": "0.0.0",
  "name": "vuesample",
  "private": true,
  "dependencies": {
    "vue": "^2.1.7",
    "vue-route": "^1.5.1"
  },

  "devDependencies": {
    "webpack": "^1.14.0",
    "webpack-dev-middleware": "^1.9.0",
    "webpack-hot-middleware": "^2.13.2",
    "webpack-merge": "^2.0.0",
    "babel-core": "6.21.0",
    "babel-loader": "6.2.10",
    "babel-preset-es2015": "^6.13.2",
    "grunt": "~0.4.5",
    "grunt-bower-task": "~0.4.0"
  },
  "scripts": {
    "webpack": "webpack -d"
  }
}

webpackというツールとbabelというツールのインストール、Vue.jsのインストールを行う設定を追記しました。

webpackは色々なライブラリを参照追加して作成したコードをコンパイルしてブラウザで実行できるスクリプトにしてくれるツールです。

それだけでなくスタイルとか画像とか色々と凄い事をしてくれるのですが詳しくは調べてみてください。

babelは新しい書き方をしているJavaScriptを古い形(ブラウザが理解できる形)にコンパイルしてくれるツールです。

追記後タスクランナーエクスプローラからpackageのインストールを行ってください。

次にプロジェクトにwebpack.config.jsというファイルを追加します。

中身は以下のようにしてください、

var path = require('path');

module.exports = {
    resolve: {
        root: path.resolve('./'),
        alias: {
            'vue$': 'vue/dist/vue.common.js'
        }
    },
    entry: {
        home: 'VueModels/home/Index.js'
    },
    output: {
        path: "Scripts/app",
        filename: "[name].bundle.js"
    },
    themeLoader: {
        themes: ['./node_modules/vuestrap/theme/bootstrap.scss'], // docs theme, default bootstrap
    },
    module: {
        loaders: [{
            test: /\.js$/,
            loader: 'babel'
        }, {
            test: /\.html$/,
            loader: 'html'
        },  {
            test: /\.json$/,
            loader: 'json'
        }]
    }
};

次にプロジェクトに以下の画像のようにVueModelsフォルダ、その下にHomeフォルダを作成し、HomeフォルダにIndex.jsファイルを作成してください。

f:id:tamafuyou:20161229070907p:plain

中身はこのような感じにしてください。

var Vue = require('vue');

var model = {
    message: "ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ"
};

new Vue({
    data: model,
}).$mount('#app');

そうしたらタスクランナーエクスプローラからpackage.json/Custom/Webpackを実行してください。

もしWebPackというタスクがないのであればVisualStudioを再起動してみてください。

するとScriptsフォルダの下にapp/home.bundle.jsというファイルが作成されているかと思います。

これがIndex.jsをwebpackでコンパイルしてブラウザで使用できる形にしたファイルです。

Viewを修正して動かしてみましょう。

まずは_layout.cshtmlを修正します。

f:id:tamafuyou:20161229072840p:plain

  • classにContainerが設定されているdivタグにid=appを追加
  • Script.Renderでコントローラ名.bundle.jsファイルを追加

次にHome/Index.cshtmlを修正します。

f:id:tamafuyou:20161229073144p:plain

Jumbotronの中のPタグの中身を{{message}}と書き換えます。

これでデバッグ実行すると

f:id:tamafuyou:20161229073319p:plain

とVueクラスとhtmlがバインドされるようになります。

ちょっとプログラムしてみよう

では、ちょっとそれっぽくプログラムしてみましょう。

ですがその前に、開発しやすくするためのタスクを追加します。

package.jsonを開きscriptsに"watch": "webpack -d -w"を追加しタスクランナーエクスプローラで実行します。

これを実行するとindex.jsファイルを監視し変更があった場合に自動的にビルドを行ってくれるようになります。

まずはIndex.jsをこのように修正してみましょう。

var Vue = require('vue');

var model = {
    message: "ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ",
    speakers:[]
};

new Vue({
    data: model,
    methods: {
        LoadSpeakers: function() {
            fetch("http://demo4404797.mockable.io/speakers")
            .then(function (response) {
                return response.json();
            })
            .then(function (json) {
                model.speakers = json;
            });
        }
    }
}).$mount('#app');

modelクラスにspeakersというプロパティを追加します。speakersは配列として宣言しておきます。

次にvueクラスにmethodsを追加しLoadSpeakersメソッドを追加します。

LoadSpeakersメソッドではfetchを使ってWebAPIにアクセスしてjsonを取得してmodelクラスのspeakersにセットします。

http://demo4404797.mockable.io/speakersにアクセスすればjsonを確認することができます。

次にIndex.cshtmlを修正します。

<div class="jumbotron">
    <h1>ASP.NET</h1>
    <p class="lead">{{message}}</p>
    <p><button v-on:click="LoadSpeakers" class="btn btn-primary btn-lg">Get Speakers &raquo;</button></p>
</div>
<div class="row">
    <div class="media" v-for="speaker in speakers">
        <a class="media-left">
            <img style="width: 128px" class="media-object" v-bind:src="speaker.Avatar">
        </a>
        <div class="media-body">
            <h4 class="media-heading">{{speaker.Name}} ({{speaker.Title}})</h4>
            {{speaker.Description}}
        </div>
    </div>
</div>

jumbotron内にあったaタグをbuttonタグに書き換えてv-on:click="LoadSpeakers"を追加します。

これによりボタンがクリックされた場合には先ほどのLoadSpeakersメソッドが呼ばれます。

次にjumbotronに続くrowの中身をすべて消しmedia objectを追加します。

media objectはbootstrapのLayoutで画像付きの紹介文を書いたりするのに適しています。

mediaクラスを設定したdivタグにv-forアトリビュートを追加し speakersプロパティをループするように設定します。 これによりリストレンダリングを行うことができます。

子要素ではimgタグにv-bind:srcで画像アドレスをバインド、h4タグには名前や役割を表記、説明文にはDescriptionをバインドさせました。

では、デバッグ実行してみましょう。

ボタンを押してLoadSpeakersが実行されると

f:id:tamafuyou:20161229083339p:plain

このような感じになると思います。

おしまいに

私自身始めたばっかりで全然知識が追い付いていない状態ですが、特に環境構築に関しては苦労しています。

この先VSでもWebフロントエンド開発が行いやすいように色々と進化していってくれると嬉しいなぁと思います。

ではよいお年を( `ー´)ノ