🦝

【Vue】Composable Function(あるいはHook)を使ってラクをする

4 min read

Composition APIで使えるようになったComposable Functionはえらい便利で、ロジックは大体これで書いたらいいんじゃないかなと思っています。
OpenLayersという地図ライブラリでの、Composable Functionの使用例を紹介してみます。

用例① 地図にレイヤを追加する

OpenLayersではJavaScript内で指定したレイヤの設定内容に応じて、canvas等に地図を描画してくれます。
JavaScriptで書くとこんな感じになります。

const map = new Map({/* 地図全体の設定いろいろ */})
const layer = new Layer({/* レイヤの設定いろいろ */})

map.addLayer(layer) //地図にレイヤを追加
map.removeLayer(layer) //地図からレイヤを削除

いちいちaddLayerとかを呼び出して制御するのはだいぶめんどくさいので、Vue内でのコンポーネントのライフサイクルと、レイヤの追加削除を同期させられれば直観的になりそうです。
そのためのスクリプトは、Composable Functionで書いていくと、いろんな場面で使えて便利になります。


export function useOlLayer<T extends BaseLayer>(map: Map, layer: T) {
  onMounted(() => {
    map.addLayer(layer)
  })
  onUnmounted(() => {
    map.removeLayer(layer)
  })
}

こんな感じで、ライフサイクルごとにやってもらいたい処理をComposable Functionの中で書くことができます。

使用するときはこんな感じです。

<script lang="ts">
import { Tile } from "ol/layer";
import { XYZ } from "ol/source";
import { useOlMap } from "~/composables/useOlMap";
import { useOlLayer } from "~/composables/useOlLayer";

export default defineComponent({
  setup() {
    const { map } = useOlMap() //Mapを取得する

    const tileLayer = new Tile({
      source: new XYZ({
        url: "https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png",
        attributions: "国土地理院",
        minZoom: 1,
        maxZoom: 18
      })
    }) //レイヤを定義している

    useOlLayer(map, tileLayer) //ここでComposable呼び出し

    return () => { } //renderless
  },
});
</script>

よくやる処理をまとめられるようになるので、とにかくまぁ便利です。

用例② イベントの購読と解除

OpenLayersというか地図系のライブラリ全般に言えることですがだいたいイベント駆動です。込み入ったことをやろうとすると結構イベント地獄になるのですが、イベントの登録もComposable Functionにまとめてしまえばお手軽です。

interface hasEvent { on: (...args: any) => any, un: (...args: any) => any }

type OlEventLKey<T extends hasEvent>
  = Parameters<T["on"]>[0][0]

type OlEventCallback<T extends hasEvent>
  = Parameters<T["on"]>[1]


export function useOlEventListener<T extends hasEvent>(
  target: T, key: OlEventLKey<T>, callback: OlEventCallback<T>
) {
  onMounted(() => {
    target.on(key, callback)
  })
  onUnmounted(() => {
    target.un(key, callback)
  })
}

OpenLayersではイベントを購読するのはon、解除するのはunでまとめられています。useOlEventListenerを使うと、OpenLayers関係のイベントの購読・解除がささっとできます。コンポーネントが破棄されればイベントがちゃんと解除されるので、解除漏れがなくなって便利です。

使用するときはこんな感じです。

<script lang="ts">
import { Tile } from "ol/layer";
import { XYZ } from "ol/source";
import { useOlMap } from "~/composables/useOlMap";
import { useOlLayer } from "~/composables/useOlLayer";

export default defineComponent({
  setup() {
    const { map } = useOlMap() //Mapを取得する

    const tileLayer = new Tile({
      source: new XYZ({
        url: "https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png",
        attributions: "国土地理院",
        minZoom: 1,
        maxZoom: 18
      })
    }) //レイヤを定義している

    useOlLayer(map, tileLayer) //ここでComposable呼び出し

    useOlEventListener(tileLayer, "postrender", () => {
      console.log("レンダリングした") //レイヤがレンダリングされたあとに呼び出される
    })

    useOlEventListener(map, "click", () => {
      console.log("クリックした") //地図上をクリックした後に呼び出される
    })

    return () => { } //renderless
  },
});
</script>

レイヤを表示させながら、イベントハンドラで何か処理させる、というような少し複雑なコンポーネントであっても、シンプルに実装できます。

jQueryなどでイベントの購読や解除をやろうとしたときに、しっかり管理するぞ!となると考えることが多くて結構大変ですが、VueやReactを使うとライフサイクルの概念に乗っかっていけます。フレームワークに乗っかっていくことで、「コンポーネントができたらイベントを購読するし、コンポーネントが消えたら、イベントの購読はやめる」というシンプルでイメージしやすい仕様に統一出来て、良いんじゃないかなぁと思います。
Options APIでは結構トリッキーなこと(mixin)をやらないと、Vueに乗っかっていくことができなかったのですが、Composition APIのおかげでラクになりました。

なお、もっといい実装例はVueUseのソースをみるといいと思います。

Discussion

ログインするとコメントできます