Nuxt.jsでも

Function API

(現CompositionAPI )を使う

NuxtMeetup#9@メルペイ

自己紹介

かいしゃ 株式会社ROXX 

なまえ  ushironoko

しごと  フロントエンド

すき   アイマス

@dreamroot46

みなさんFunction APIやってますか?

composition-api

要約

・Vue 3.0で追加される関数ベースでコンポーネントが書けるAPI

・コンポーネントを返したり返さなかったりする

・mixinとかHOCの代わりになれる

・コンポーネントの外にいたりいなかったりする

- 名前の衝突がない

- 不要なインスタンス作らない

・ちゃんと型が効く

いつ使えるの?

・Vue 3からコアに入る

Vue 2系の場合プラグインで使える

・NuxtのAPIはまだ未対応

Nuxtでの使い方(ついでにTS対応)

・create-nuxt-app(Lint + Prettier)

・yarn add @nuxt/typescript-runtime

@vue/composition-api

・yarn add -D @nuxt/typescript-build

@typescript-eslint/eslint-plugin @typescript-eslint/parser

諸々入れる※v2.9.0以降

Nuxtでの使い方(ついでにTS対応)

諸々設定

・tsconfig.jsonに設定追加

・nuxt.config.jsをts化 & nuxt-ts devなどに書き換え、その他諸々追加

・.eslint.jsに設定追加

だいたいNuxt TypeScriptのドキュメントにあります

Nuxt TypeScript

Nuxtでの使い方(ついでにTS対応)

・plugins/composition-api.js

import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'

Vue.use(VueCompositionApi)

・nuxt.config.ts

//省略

plugins: ['~/plugins/composition-api'],

// 省略
<template>
  <div>
    <div>{{ `x: ${x}` }}</div>
    <div>{{ `y: ${y}` }}</div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import { ref, onMounted, onUnmounted } from '@vue/composition-api'

export default Vue.extend({
  setup() {
    const x = ref(0)
    const y = ref(0)
    const update = (e: MouseEvent) => {
      x.value = e.pageX
      y.value = e.pageY
    }
    onMounted(() => {
      window.addEventListener('mousemove', update)
    })
    onUnmounted(() => {
      window.removeEventListener('mousemove', update)
    })
    return { x, y }
  }
})
</script>

いつものマウスのやつ(useMouse的な)

コンポーネントに依存した処理じゃないし外に出したい

お気持ち

・コンポーネントよりも小さい単位(関数)で分割したい

・多分こんな感じになってアレ

import mouse from '~/utils/mouse'

// 省略
 methods: {
   mouse(val) {
     return mouse(val)
   }
 }

composition function

compositions/useMouse.ts

import { ref, onMounted, onUnmounted } from '@vue/composition-api'

export default () => {
  const x = ref(0)
  const y = ref(0)

  const update = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    window.addEventListener('mousemove', update)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })

  return { x, y }
}

・リアクティブな値をコンポーネントの外で作れる

pages/index.vue

<template>
  <div>
    <h1>Nuxt + vue-function-api</h1>
    <div>{{ `x: ${x}` }}</div>
    <div>{{ `y: ${y}` }}</div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import useMouse from '~/compositions/useMouse'

export default Vue.extend({
  setup() {
    const { x, y } = useMouse() // x: Wrapper<number> y: Wrapper<number> 
    return { x, y }
  }
})
</script>

・ばっちり型も効く(Wrapper<T>型になる)

よさそう

Composition API Document

Composition API 所感

・処理をコンポーネントから抽出できる

・直感的に型が効いてうれしい

・普通の関数以上コンポーネント未満ができる

- リアクティブな関数を自作できる

・thisとおさらばできる

Nuxt要素薄まってきたのでNuxtの話します

NuxtとComposition API

・asyncDataがない

・fetchがない

・contextに色々入ってない

・slots

・attrs

・emit

VueのContextはある

・parent

・root

・refs

Nuxt独自のものは未対応

Nuxtでの使い方

・Object APIとキメラにする

<script>
import Vue from 'vue'
import useMouse from '~/compositions/useMouse'

export default Vue.extend({
  props: {
    id: Number
  },
  setup() {
    return {
      ...useMouse()
    }
  },
  asyncData() {
    ...
  },
  data() {
    ...
  }
})
</script>

キメラにしたときの動作

・setup → Object APIの順に実行される

・setupで返した値はthisで参照可能

・asyncDataやfetchがある場合setupのに実行される

  setup() {
    return {
      count: 1000
    }
  },
  mounted() {
    console.log(this.count) // 1000 ミツケター!
  }

キメラにしたときの動作

・Object APIと競合するプロパティは上書きされる

・setupの中でしかComposition APIは動かない

・setupにasyncDataの値が渡せない

  setup(props, ctx) {
    // IIKO ワドコドコ…
  },
  asyncData() {
    return {
      IIKO: 'IIKO'
    }
  }

ただし子にpropsとして渡せば可能

<template>
  <div>
    <v-counter :initial-count="count"></v-counter>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import VCounter from '~/components/VCounter.vue'

export default Vue.extend({
  components: {
    VCounter
  },
  asyncData() {
    return { count: 100 }
  },
  data() {
    return {
      count: 0
    }
  }
})
</script>

・pages/index.vue

<template>
  <div>
    {{ count }}
    <button @click="increment">+</button>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import useCount from '~/compositions/useCount'

export default Vue.extend({
  props: {
    initialCount: {
      type: Number,
      default: 0
    }
  },
  setup(props) {
    return {
      ...useCount(props.initialCount)
    }
  }
})
</script>

子のsetupでcomposition functionに渡す

・components/VCounter.vue

よさそう

注意点

  asyncData() {
    console.log('asyncData')
    return {
      count1: 1,
      count2: 2
    }
  },
  setup() {
    console.log('setup')
    return {
      count1: 1000,
      count2: 2000,
      count3: 3000
    }
  },
  mounted() {
    console.log(this.count1)
    console.log(this.count2)
    console.log(this.count3)
  }

setupはasyncDataに負ける

注意点

setupはasyncDataに負ける

注意点

setupの型が死んでる(v2.9.0〜)

まとめ

 ・Nuxtでもpluginsで登録すれば使える

 ・コンポーネントを返さない純粋な関数としても有用

 ・NuxtのAPIはないが、一部は設計次第で対応可能

 ・あくまでRFC、ご利用は自己責任で

参考

 ・追記:@vue/composition-apiに代わり内容も若干変更されたのでスライド追従しました

Thank You!

Made with Slides.com