Vue.jsを使う上で気をつけておきたいこと - inokawablog

Vue.jsを使う上で気をつけておきたいこと

vue.jsのスタイルガイドはとても充実しています。
vue.jsに慣れてきたら、一度は読んでおきたいところですが、とても長いですね。

今回はスタイルガイドを踏まえた上で注意しておきたいポイントをまとめておきます。

作成時と更新時に同じ処理をする場合

以下のように、作成時に何かしらのデータを取得して、更新時にも同じ処理を走らせる場合。例えば、検索フォームで最初は一覧を表示して、入力された文字列によってフィルターをかける場合などがあります。

searchTextが更新されたら、hogehoge()が発火します。

created () {
    this.hogehoge()
}
watch: {
    searchText() {
        this.hogehoge()
    }
}

それがこうなります。

⬇︎⬇︎⬇︎⬇︎⬇︎⬇︎⬇︎⬇︎⬇︎⬇︎⬇︎⬇︎

watch: {
    searchText: {
        handler: 'hogehoge',
        immediate: true
    }
}

そしておそらく検索フォームなどはコストの高い処理になるのでlodash関数の_.debounceを使って、負荷を軽減させてあげると良いと思います。

watch: {
    searchText: {
        handler: 'hogehoge',
        immediate: true
    }
},
methods: {
    hogehoge: _.debounce(function(searchText) {
        //APIでデータを持ってきたり、フィルター処理など
    }
}

immediate: trueオプションを付与することで、作成時にもhandlerに指定した関数が実行されるようになります。ちなみにimmediateの発火タイミングはbeforeCreatecreatedの間です。

こちらに調べてくれた方がいました!有難や🙏

親から子へデータを渡す

長くなってしまったので別の記事へ。【サンプル付】Vue.jsでComponentを作成する! 正しくpropsを使おう!

親コンポーネントインスタンスへのアクセス

vue.jsで$parentを使用するのは、処理を煩雑にする上、子から親のデータを変更するのはデータの変更の処理を追いづらくなるので、あまりいいものではありません。そこで$parentの代替となるのが、provide/injectです。
こちらは2.2.0 から新しく実装されました。

こちらに素晴らしいサンプルがあります。以下はサンプルのコード

<!DOCTYPE html>
<html>
  <head>
    <title>Dependency Injection Google Maps Demo</title>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAHbknPTCvUSgWwU0jJ68m4h6b7vpyP6hM"></script>
    <script src="https://unpkg.com/vue"></script>
    <style>
      .map {
        width: 100%;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <google-map>
        <google-map-marker v-bind:places="vueConfCities"></google-map-marker>
      </google-map>
    </div>

    <script>
      Vue.component("google-map", {
        provide: function() {
          return {
            getMap: this.getMap
          };
        },
        data: function() {
          return {
            map: null
          };
        },
        mounted: function() {
          this.map = new google.maps.Map(this.$el, {
            center: { lat: 0, lng: 0 },
            zoom: 1
          });
        },
        methods: {
          getMap: function(found) {
            var vm = this;
            function checkForMap() {
              if (vm.map) {
                found(vm.map);
              } else {
                setTimeout(checkForMap, 50);
              }
            }
            checkForMap();
          }
        },
        template: '<div class="map"><slot></slot></div>'
      });

      Vue.component("google-map-marker", {
        inject: ["getMap"],
        props: ["places"],
        created: function() {
          var vm = this;
          vm.getMap(function(map) {
            vm.places.forEach(function(place) {
              new google.maps.Marker({
                position: place.position,
                map: map
              });
            });
          });
        },
        render(h) {
          return null;
        }
      });

      new Vue({
        el: "#app",
        data: {
          vueConfCities: [
            {
              name: "Wrocław",
              position: {
                lat: 51.107885,
                lng: 17.038538
              }
            },
            {
              name: "New Orleans",
              position: {
                lat: 29.951066,
                lng: -90.071532
              }
            }
          ]
        }
      });
    </script>
  </body>
</html>

このサンプルでは、親コンポーネントのgoogle-mapgetMapをprovide(提供)して、子コンポーネントのgoogle-map-markerで、getMapをinject(依存注入)して、取り込んでいます。これにより<google-map>インスタンス全体にアクセスせず、どの子孫コンポーネントからでもgetMapだけにアクセスできます。素晴らしい!!

無駄な変更や削除がなくなりコンポーネントの安全性も高まりますし、

ただ一つ注意点があり、すでにお分かりのようにコンポーネント同士が密結合になり、リファクタリングとアプリケーションのスケールを難しくします。
もし特定のプロパティをシェアしたいのなら、Vuexなどを使用する方が良いのかもしれません。

コンポーネントの命名規則

スタイルガイドで強く推奨されている部分で、以下のセクションを一通り読んでおきましょう。

優先度B のルール: 強く推奨 (読みやすさの向上)

上のセクションの基底コンポーネントの名前に、以下のような文言があります。

これらのコンポーネントはとても頻繁に使われるので、あらゆる場所で import するよりも単純にグローバルにしてしまいたいと思うかもしれません。プレフィックスによって、それを Webpack でできるようになります。

コンポーネントに正しい命名をすることで、以下のようにグローバルにimportすることができるメリットを得られます。さらにコンポーネントを探す手間や、可読性も上がるので必ず上記のセクションを読んでおきましょう。

var requireComponent = require.context("./src", true, /Base[A-Z]\w+\.(vue|js)$/)
requireComponent.keys().forEach(function (fileName) {
  var baseComponentConfig = requireComponent(fileName)
  baseComponentConfig = baseComponentConfig.default || baseComponentConfig
  var baseComponentName = baseComponentConfig.name || (
    fileName
      .replace(/^.+\//, '')
      .replace(/\.\w+$/, '')
  )
  Vue.component(baseComponentName, baseComponentConfig)
})

参考

まとめ

これはいいなと思ったものがあり次第更新していきます!