JavaScript
jQuery
レポート
vue.js

jQuery しか使ったことのないデザイナーに Vue を教えた(報告書)

事の起こりはこんな会話でした。

D 「タブ表示みたいなデザインにして組み込みをしたのですが、js が上手く動かないんです・・・」
M 「どれどれ・・・――― 全部 jQuery? え? React とか Vue 使ったら?」
D 「え? えー・・・」

そろそろ View において jQuery が素敵じゃないことに気づいて欲しくて、Vue を教えてみました。
よくあるタブ機能を Vue 2 で実装します。

動作環境をつくる

私は JSFiddle を勧めました。

  • 1画面で、HTML、CSS、js を変更できる。
  • 共有ができる。
  • 途中経過を見ることができる。
  • ソースのフェッチができる。
  • 埋め込み要素が作成できる。

以上から、js モックを作るときによく使用させていただいております。
似たようなサービスに CodePen があります。
もちろん、通常のエディターとブラウザでも構いません。
自身にあったものを使用してください。

モジュールを取得する

cdnjs から Vue の CDN を取得します。
JSFiddle では CDN を使わなくても Vue が使えるのですが敢えてバージョンを指定したくて使用しました。

HTML と CSS を用意する

私はフロントエンドエンジニア(仮)とはいえ js メインでやっているので HTML、CSS は苦手です。
失礼ながら、デザイナーさんが数分で書いてくださった HTML と CSS をべた張りさせていただきます。

HTML
<div class="tab" id="tab">
  <nav>
    <ul>
      <li>タブA</li>
      <li>タブB</li>
      <li>タブC</li>
    </ul>
  </nav>
  <div class="tabBody">
    <section>
      <h2>タブA内容</h2>
      <p>AテキストAテキストAテキストAテキストAテキスト</p>
    </section>
    <section>
      <h2>タブB内容</h2>
      <p>文章入ります内容はBです</p>
    </section>
    <section>
      <h2>タブC内容</h2>
      <p>タブCを押したとき表示されます。</p>
    </section>
  </div>
</div>
SCSS
.tab{
  padding: 20px 15px;
  background: #eee;
  color: #333;
  nav{
    ul{
      list-style-type: none;
      overflow: hidden;
      border-bottom: 1px solid #ccc;
      li{
        display: inline-block;
        float: left;
        width: 30%;
        padding: 5px 0 3px;
        margin-right: 8px;
        text-align: center;
        background: #ccc;
      }
    }
  }
  .tabBody{
    section{
      padding: 5px 15px 20px;
      h2{
        font-weight: normal;
      }
    }
  }
}

Vue を書く

Vue
new Vue({
  // el は操作したいエレメントの ID を指定します。
  el:'#tab',
  // data は Vue インスタンス(methods やテンプレート等)で使用するプロパティです。
  data:{
    // これは methods 内では this.current、テンプレート内では current として使用できます。
    current:'tabA'
  },
  // methods は Vue インスタンスで使用できるメソッドです。
  methods:{
    // これは methods 内では this.show()、テンプレート内では show() として使用できます。
    show:function(name){
      this.current = name;
    },
    isCurrent:function(name){
      return this.current == name;
    }
  }
})

HTML をテンプレートに直す

HTML.vue
<div class="tab" id="tab">
  <nav>
    <ul>
      <!-- 
        イベントの結びつきには @ を使います。
        クリックされると先程の Vue インスタンスの show が呼び出されます。
      -->
      <li @click="show('tabA')">タブA</li>
      <li @click="show('tabB')">タブB</li>
      <li @click="show('tabC')">タブC</li>
    </ul>
  </nav>
  <div class="tabBody">
    <!--
      切り替えた時にフェードインフェードアウトさせたかったので transition タグを使いました。
      transition タグは Vue 独自のものです。
      Vue 2 で追加されました。

      簡単に説明をしておきます。
      transition タグの mode="out-in" は切り替えするときにアニメーションを実行する順番です。
      section タグにある key は、トランジションしたい要素のタグ名が同一の場合に識別させるために指定します。
      (「transition で同一名のタグを使うときは key 要素が必要」という Vue の仕様です。)

      詳しい説明は Vue の公式ドキュメントをご参考ください。
    -->
    <transition name="fade" mode="out-in">
      <!--
        v-if はそのまま if です。「もし~だったら要素を挿入する」というものです。
        似た機能に v-show がありますが transition が動きません。
        transition を掛けたい要素には v-if でなければならないようです。
        ※補足: v-if と v-show の違い。
          v-show は display:none の切り替えです。
          v-if は要素ごと挿入/削除を行います。

        ココでは isCurrent が呼び出されて プロパティ current が引数通りかどうかを確認してくれます。
      -->
      <section v-if="isCurrent('tabA')" key="tabA">
        <h2>タブA内容</h2>
        <p>AテキストAテキストAテキストAテキストAテキスト</p>
      </section>
      <section v-if="isCurrent('tabB')" key="tabB">
        <h2>タブB内容</h2>
        <p>文章入ります内容はBです</p>
      </section>
      <section v-if="isCurrent('tabC')" key="tabC">
        <h2>タブC内容</h2>
        <p>タブCを押したとき表示されます。</p>
      </section>
    </transition>
  </div>
</div>

トランジションを付けたいので SCSS に下記を追記します。

SCSS
.fade-enter-active, .fade-leave-active {
  transition: all .8s ease;
  opacity: 1;
}
.fade-enter,
.fade-leave-active,
.fade-leave{
  opacity: 0;
}

以上で、タブ機能の完成です。

注意した点

説明をしながらソースコードを書いて貰った時、一つずつ覚えてもらいたかったので、v-if の部分は下記のように一旦ですが書いてもらいました。

HTML.vue
<section v-if="current == 'tabA'" key="tabA">

こちらがわかりやすいという人もいると思います。
しかし、後でメソッドに直しました。

HTML.vue
<section v-if="isCurrent('tabA')" key="tabA">

これは同じことの繰り返しだから、ではなく current == 'tabA' は直感的ではなく機械的だからです。
プログラミング言語とは機械が分かる言語という意味です。
つまり、プログラミングとは機械と会話することです。
普通、プログラミングはいつも難解で何を機械と会話しているのかわからないでしょう。
外国語を聞いた気分になるはずです。
しかし、isCurrent('tabA') と書くことで、少なからず英語が分かる方なら理解できるものになります。

Is current 'tabA'? --- current == 'tabA'
Yes, it is. --- true
No, it is not. --- false

ソースコードとはこれの繰り返しです。
だから、@click で実行されるメソッド名も clickTab('tabA') などというものではなく show('tabA') というコンピューターへの具体的な指示を表すものであるのが良いと考えております。

質問されたこと

「なるべく役割毎にソースコードをわけるのが通常の設計(MVC のこと)ですよね? HTML に if が出てきてより複雑になっている気がするのですが。jQuery なら操作が全部固まってて・・・。」

デザイナーさん的に if や for が出てきた時点で View ではなく Controller という認識でした。
これについては、下記のよう答えました。

「View は変数の代入ができません。関数も作ることができません。
 変数の代入も関数も作成もできないなら、それは Controll でなければ Model でもありません。
 それは分岐や繰り返し表現のあるダイヤグラム(図面)です。
 ダイヤグラムは View ですよね?」

納得していただけました。

Vue ではできないこと

とはいえ、私は jQuery は素晴らしいモジュールだと思っております。
DOM 操作にはやはり jQuery の力を使わざるを得ません。
例えば、window 操作。

$(window).on('resize', () => { ... })

Vue には DOM 操作をする機能がないので(そもそもの概念に反するので)、上記のような window のイベントハンドラに対する動作は jQuery に軍配が上がっているとおもいます。
クロスブラウザ対応を考えて、addEventXX だとか attachEvent だとか書いていたらキリがありません。
高さや幅の取得も、まだまだ jQuery を使った方が楽です。
また、jQuery のプラグインの力はデザイナーさんの支えにもなっております。
これは中々壊すことができません。
モジュールの重さだとかもあるかもしれませんが、良いところは取り入れて、悪いところは取り除くのがよいと思っております。

振り返り

色々と苦労はあったものの、Vue の良さを理解していただけました。
ニュース一覧などにある「もっと見る」機能も作ってみたいと意欲的だったので彼女が飽きるまで勉強に付き合いたいとおもいます。

  • 教えられる側に作りたい物があったこと。
  • 教える側が1対1で教えられたこと。
  • 短時間で集中的に行ったこと。

今回はこれが功を奏したと思います。
少しずつ社内の js レベル向上のためにこれからも頑張りたいと思います。

以上