よく分かってないでvue-cliで作ったVue.js製SPAをアップデートした

GitHub Edit Page
この記事は公開から1年以上が経過しています。内容が一部古い箇所があります。

前回 => https://qiita.com/yamanoku/items/41df5c05c5c89714ea3c

特にきっかけはないのですが、表題のまんまのことやりましたので記録です。

以前の環境について

で作ってました。vue-routerがまず v2.0.0 でもないというのが驚きですが、いかんせん Vue.js どころか諸々のスキルが低すぎたのもあったので、言われるがままなすがままにこの環境のままでつくりました。

使用したvue-cliテンプレートはwebpack-simpleでした。

一応動きます。 https://github.com/yamanoku/vue_portfolio_templete/tree/ver1.0.0

強制アプデして直そうとした

まず以前の内容からブランチを切ってnpm-check-updatesをかけpackage.json周りを強制アップデートしてから、諸々インストールして、dev を走らせましたが、早速webpack からぶっ壊れる

webpack

  resolveLoader: {
    root: path.join(__dirname, 'node_modules'),
  },
  resolveLoader: {
    modules: [path.join(__dirname, 'src'), 'node_modules'],
  },
  module: {
    loaders: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.json$/,
        loader: 'json-loader'
      },
      {
        test: /\.html$/,
        loader: 'vue-html-loader'
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: "file-loader?name=[name].[ext]"
      }
    ]
  },
  module: {
    loaders: [
      {
        test: /\.vue$/,
        loader: 'vue'
      },
      {
        test: /\.js$/,
        loader: 'babel',
        exclude: /node_modules/
      },
      {
        test: /\.json$/,
        loader: 'json'
      },
      {
        test: /\.html$/,
        loader: 'vue-html'
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: "file?name=[name].[ext]"
      }
    ]
  },

1つ1つ調べながら対応していたのもあってなかなかbuilddevが通らず苦労しました。

webpack のほうを修正して npm-scripts 周りが動作するようになったので動かし始めたのですが、お次はVue1.x => Vue2.0.0へのアプデ修正周りです。

Vue.js

vue-migration-helper https://github.com/vuejs/vue-migration-helper

世の中にはマイグレーションヘルパーツールなる優れたものがあるのでこちらを使用しました(変更した部分に関しては後述)。 ターミナルで吐かれたエラーを1つずつ治し、ようやく 0 になってコンソールもエラーなくなったのですが、肝心の vue-router 部分が動かず、ここで立ち往生しました。

1つ1つ見直していけば問題判明できそうというのもありましたが、個人のプロダクトで特に機能も複雑化していないものだったので、1 つずつ手直しするよりもvue-cli で1から作り直してやろうという気持ちが勝ったのでやり直しました。

ちなみにですが立ち往生した作業途中もブランチ切って上げてます。どんなのか気になる人は参考までに見てみてください。 https://github.com/yamanoku/vue_portfolio_templete/tree/fail/updates

vue-cli から作り直し

グローバルにあるvue-cliをアップデートして、前回はwebpack-simpleテンプレートだったので今回はwebpackを選択しました。余談ですがいつの間にかpwaなるものも追加されていたのですね。

さっそくassets内に必要ファイルを格納しました。以下変更部分

App.vuemain.jsは既存のままですが、router 部分の記述はsrc/router/index.jsに移動しました。

Vue.js 変更箇所

v-forで回した時に:key指定

before

<template v-for="list in lists">
  <div
    class="detail"
    v-if="$route.path === '/work/' + list.id + '/detail'"
    :style="{ backgroundImage: 'url('+ list.image +')' }"
  ></div
></template>

after

<template v-for="list in lists">
  <div
    class="detail"
    v-if="$route.path === '/work/' + list.id + '/detail'"
    :style="{ backgroundImage: 'url('+ list.image +')' }"
    :key="list.id"
  ></div
></template>

2.2.0 以降で、コンポーネントで v-for を使用するとき、key は必須です

https://jp.vuejs.org/v2/guide/list.html#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%A8-v-for

v-forで作成したエイリアス値の属性値での設定

before

<a href="{{list.url}}" target="_blank">{{list.url}}</a>

after

<a :href="list.url" target="_blank">{{list.url}}</a>

{{ xxx }}指定が不要になる。

this.$eval 廃止

以下記事参考にしました。ありがとうございます。

https://qiita.com/tmiame/items/34823b22cd3829321120#eval

以前のは json の画像 URL 部分も一緒に引っかかるのをどうにかしたかったんですがこれで解決しました。

vue-router 設定時

router.start

before

router.start(App, '#app');

after

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>',
});

Vue インスタンスにrouterプロパティを渡す。

https://jp.vuejs.org/v2/guide/migration-vue-router.html#router-start-%E7%BD%AE%E3%81%8D%E6%8F%9B%E3%81%88

router.map

before

router.map({
  '/home': {
    component: Main,
  },
  '/work/:number/detail': {
    component: Detail,
  },
  '/profile': {
    component: Profile,
  },
  '*': {
    component: NotFound,
  },
});
router.redirect({
  '/': '/home',
});

after

export default new Router({
  routes: [
    {
      path: '/',
      redirect: '/home',
    },
    {
      path: '/home',
      name: 'Main',
      component: Main,
    },
    {
      path: '/work/:number/detail',
      name: 'Works',
      component: Detail,
    },
    {
      path: '/profile',
      name: 'Profile',
      component: Profile,
    },
    {
      path: '*',
      name: 'NotFound',
      component: NotFound,
    },
  ],
});

redirect 部分も一緒に配列に追加。

https://jp.vuejs.org/v2/guide/migration-vue-router.html#router-map-%E7%BD%AE%E3%81%8D%E6%8F%9B%E3%81%88

hashbang 機能

不要となりましたのでこちらは削除。

vue-router コンポーネント使用時

before

<a class="detail_btn" v-link="{ path: '/home' }">Back Home</a>

after

<router-link class="detail_btn" to="/home">Back Home</router-link>

path 指定での記述ではなくなりました。

名前付きルート指定

before

<a v-link="'/work/' + list.id + '/detail'" class="filetype"
  >[{{ list.type }}]</a
>

after

<router-link
  :to="{ name: 'Works', params: { number: list.id }}"
  class="filetype"
  >[{{ list.type }}]</router-link
>

router.map設定時にnameを指定しておくと、ここでルートを特定できて非常に楽になりました。 上記設定でたとえば href="work/001/detail" を書き出してくれます。

https://router.vuejs.org/ja/essentials/named-routes.html

transition 指定

before

<article transition="fade"></article>

after

<transition name="fade"></transition>

どこかのタグに対してtransition属性として付与するのではなく、<transition>タグで独立。

いままで各コンポーネントごとでかけてましたが一括化するためにApp.vueにのみに変更・設定しておきました。

<template>
  <div id="app">
    <transition name="fade">
      <router-view />
    </transition>
  </div>
</template>

.v-transition 廃止

.v-enter-active.v-leave-activeに変更。

ほか修正したこととか

アプデに伴い、webpack や Vue.js と関係なく修正した部分について

flex-box => grid layout

before

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  width: 95%;
  margin: auto;
}
.container::before {
  display: block;
  content: '';
  order: 1;
  width: 22.75%;
}
.container::after {
  display: block;
  content: '';
  width: 22.75%;
}
.card {
  width: 22.75%;
  margin-bottom: 2.5%;
  background: #456a8e;
  border-radius: 3px;
  box-shadow: 0 1px 3px rgba(100, 100, 100, 0.25);
  transition: all 0.25s ease-in-out;
}

after

.container {
  width: 95%;
  margin: auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 20px;
  grid-row-gap: 20px;
  padding-bottom: 20px;
}
.card {
  background: #456a8e;
  border-radius: 3px;
  transition: all 0.25s ease-in-out;
}

IE11 は.cardwidth:100%になるが、コード量的には減ってすっきり。 ただ、grid-row-gapが%指定しても空きができなかった。grid-column-gapはできたけど両方に指定はできないか?

transition効果

Screenshot from Gyazo

下から上に切り替わるような効果にしていたが、もうちょっとスムーズになるように調整したのと、タブレット〜スマホサイズ時は左から右に切り替わるようにした。

Screenshot from Gyazo

.fade-enter-active {
  transition: all 0.75s ease;
}
.fade-leave-active {
  transition: all 0.75s cubic-bezier(1, 0.5, 0.8, 1);
}
@media screen and (min-width: 769px) {
  .fade-enter,
  .fade-leave-to {
    transform: translateY(100vh);
  }
}
@media screen and (max-width: 768px) {
  .fade-enter,
  .fade-leave-to {
    transform: translateX(-250vw);
  }
}

あとheight: 100%での SPA になってるのでページ下部までいって詳細ページに遷移する時にガクッとなる動きがあり違和感があったため、router のscrollBehaviorを使って詳細ページ時はページトップへ遷移(setTimeoutで transition 部分の時間と調整)して、それ以外はページ位置を保持しておくという設定もつけておきました。

  routes: [
    ...
    {
      path: '/work/:number/detail',
      name: 'Works',
      component: Detail,
      meta: { scrollToTop: true }
    },
    ...
  ],
  scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return new Promise(resolve => {
        if (to.matched.some(m => m.meta.scrollToTop)) {
          setTimeout(() => {
            resolve({ x: 0, y: 0 })
          }, 750)
        }
      })
    }
  }

まだ精度としては微妙なのでスタイル部分も合わせて見直す必要ありそうだけど、まぁいいかとなってます。

ToDo

雑感

関連リンク

Future of Vue.js (vue-cli について)

https://speakerdeck.com/player/7d437d38c31b46318998d120b2d9c929?slide=60

vue-router 日本語ドキュメント

https://router.vuejs.org/ja/

アーカイブ記事のため、内容に関する更新依頼は受け付けておりませんが、誤字や脱字などありましたらご連絡ください。

この記事に関する修正依頼
アーカイブ一覧へ戻る