nuxt.svg

【Nuxt 2】小ネタ集

Nuxt2

はじめに

この記事では、これまでの記事で記載できていない Vue.js/NuxtJs の機能や設定について記述していきます。 小ネタ集というよりは、情報提供できていないものを消化しているだけです。

$nextTick()

$nextTick()は、DOM 更新後に実行したい処理を記述します。

例えば、v-ifで表示した要素内の入力欄に、フォーカスを設定したいとします。

<template>
  <div>
    <div v-if="show">
      <input ref="input1" />
    </div>
    <button @click="onClick">表示</button>
  </div>
</template>

<script>
export default {
  data: () => ({
    show: false,
  }),
  methods: {
    onClick() {
      this.show = true
      this.$refs.input1.focus()
    },
  },
}
</script>

この実装では、表示したタイミングでエラーとなります。 this.show = trueにより、要素を表示するために DOM が更新されます。 しかし、その結果を待たずしてthis.$refs.input1.focus()が実行されてしまうため、そんな要素はないよというエラーになります。

このような場合に$nextTick()を使用します。

onClick() {
  this.show = true
  this.$nextTick(() => {
    this.$refs.input1.focus()
  })
}

これにより DOM が更新された後に処理が実行される、つまり対象の要素がある状態で処理が実行されるため、フォーカスが正しく設定されます。

model

通常コンポーネントでv-modelを実装するには、propsvalueを定義することで紐付け、inputイベントによって値を更新します。

<template>
  <input type="text" v-model="inputValue" />
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: undefined,
    },
  },
  computed: {
    inputValue: {
      get() {
        return this.value
      },
      set(value) {
        this.$emit('input', value)
      },
    },
  },
}
</script>

これをvalue以外のプロパティに紐付けたり、input以外のイベントで値を更新したい場合にmodelを指定します。

<template>
  <input type="text" v-model="inputValue" />
</template>

<script>
export default {
  model: {
    prop: 'text',
    event: 'change',
  },
  props: {
    text: {
      type: String,
      default: undefined,
    },
  },
  computed: {
    inputValue: {
      get() {
        return this.text
      },
      set(value) {
        this.$emit('change', value)
      },
    },
  },
}
</script>

inheritAttrs

以下のようなコンポーネントがあったとします。

SimpleLink.vue
<template>
  <a><slot /></a>
</template>

これを以下のように使用するとどうなるでしょうか。

<simple-link href="https://google.com">Google</simple-link>

答えは、以下のように展開され、リンクが機能します。

<a href="https://google.com">Google</a>

このようにpropsで指定していない属性は、コンポーネントのルート要素に反映されてしまいます。 これを防ぐためには、inheritAttrsfalseに設定します。

SimpleLink.vue
<template>
  <a><slot /></a>
</template>

<script>
export default {
  inheritAttrs: false
}
</script>

これにより、styleclass以外のpropsに設定されていない属性は無視されるようになります。

component

コンポーネントを動的に変更したい場合に<component>を使用します。

<template>
  <div>
    <select v-model="selected">
      <option v-for="c in components" :key="c" :value="c">{{ c }}</option>
    </selected>
    <component :is="selected" />
  </div>
</template>

<script>
export default {
  data: () => ({
    selected: 'MyComponent1',
    components: [
      'MyComponent1',
      'MyComponent2',
      'MyComponent3'
    ]
  })
}
</script>

<component>:isにコンポーネント名をバインドすることで表示が動的に切り替わります。 動きとしてはv-ifと同じく、DOM 操作により表示・非表示が切り替わります。

プロパティのバインドも可能ですが、すべてのコンポーネントに適用されるため、個別に設定したい場合は素直にv-ifなどを使う方が無難です。

keep-alive

以下のような単純なコンポーネントを作成します。

SimpleForm.vue
<template>
  <input type="text">
</template>

これを以下のようにv-ifで非表示にすると、コンポーネントは初期化されます。 つまり、非表示にすれば入力した内容は消えてしまいます。

<template>
  <div>
    <simple-form v-if="show" />
    <button @click="show = !show">click</button>
  </div>
</template>

<script>
export default {
  data: () => ({
    show: true,
  }),
}
</script>

これを<keep-alive>で囲むことで、DOM からは消えますが、コンポーネントの状態をキャッシュすることができます。 つまり、非表示にしても入力した内容は失われません。また表示時にmountedなどのライフサイクルは実行されません。

<keep-alive>
  <simple-form v-if="show" />
</keep-alive>

これだけであればv-showと大きな変わりはありません。 <keep-alive>は、上記の<component>と組み合わせて使います。

<keep-alive>
  <component :is="selected">
</keep-alive>

<keep-alive>は、キャッシュを制御するための以下の 3 つの属性を持ちます。

max

maxは、キャッシュする最大数を設定します。 例えば以下で、MyComponent1MyComponent2MyComponent3と切り替えるとMyComponent1の情報は削除されます。

<keep-alive>
  <component :is="selected" max="2">
</keep-alive>

include

includeは、キャッシュするコンポーネントを設定します。 ここに設定されていないコンポーネントはキャッシュされません。

<keep-alive>
  <component :is="selected" :include="['MyComponent1', 'MyComponent2']">
</keep-alive>

exclude

excludeは、キャッシュしないコンポーネントを設定します。 ここに設定されていないコンポーネントはキャッシュされます。

<keep-alive>
  <component :is="selected" :exclude="'MyComponent3'">
</keep-alive>