Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

【eslint-plugin-vue】Vue.js 3.xサポートを始めました

eslint-plugin-vueはVue.jsバージョン3.x(いわゆるvue-next)のサポートを始めました。
eslint-plugin-vueバージョン7.0.0以降で利用できます。

インストール or アップデート

npmなどを使用してインストール or アップデートしてください。

npm install -D eslint-plugin-vue@latest

使い方

( 詳細はこちらを参照してください。 https://eslint.vuejs.org/user-guide/

ESLint設定

ESLint設定ファイル.eslintrc.*extendsに下記の設定を追加します。
( 詳細はこちらを参照してください。 https://eslint.vuejs.org/user-guide/#usage

.eslintrc.jsの場合:

.eslintrc.js
module.exports = {
  extends: [
    // ...
    // ↓追加
    'plugin:vue/vue3-recommended',
    // または、
    'plugin:vue/vue3-strongly-recommended',
    // または
    'plugin:vue/vue3-essential',
  ],
  // ...
}

すでに利用していて、extendsにVue.js 2.x用の設定を入れている場合は、Vue.js 3.x用の設定に置き換えてください。

.eslintrc.js
module.exports = {
  extends: [
    // ...
-    'plugin:vue/recommended',
+    'plugin:vue/vue3-recommended', // <- 'vue3-'ってつけてください。
  ],
  // ...
}

Vue.js 3.x用に追加されたルールセット

(上でも書きましたが)Vue.js 3.x用に下記のルールセットが利用できるようになりました。

下のルールセットの方が有効になるルールが多いです。

Vue.js 3.x用に追加された検証ルール

詳細はリリースノートを参照してください。

vue/no-deprecated-filter

このルールはフィルターの使用を禁止します。

Vue.js 2.xまで使用できたフィルターですが、Vue.js 3.xでは廃止されます。(詳細はRFC0015を参照してください。)
このルールでVue.js 2.xの癖でうっかりフィルター書いてしまった場合や、Vue.js 3.xに移行する際に書き換えないといけない箇所として、検出できます。

この制限として、逆に<template>内でビット演算のOR|もレポートされてしまいますし、そもそもうまく解析できない場合があります。

vue/no-deprecated-v-bind-sync

このルールはv-bind:foo.sync.sync修飾子の使用を禁止します。

そもそも.sync修飾子が何なのか(何だったのか)はこちらの公式ドキュメントを参照してください。

この構文は、Vue.js 3.xでv-model:fooのような書き方に置き換えられます。(詳細はRFC0005を参照してください。)
なので、古い書き方は利用できなくなります。このルールは自動修正をサポートしているので、Vue.js 3.xへの移行で利用できると思います。

vue/no-deprecated-v-on-number-modifiers

このルールはv-onkeydownなどで使用できていた、数字の修飾子によるキー指定を禁止します。

Vue.js 2.xまで使用できたキーコードを利用したキー修飾子ですが、Vue.js 3.xでは廃止されます。(詳細はRFC0014を参照してください。)
Vue.js 3.xに移行する際に書き換えないといけない箇所として検出できます。
ただし、9などの一桁の数字は、Vue.js 2.xではTabコードのリスナーを目的として利用していたかもしれませんが、Vue.js 3.xでは数字キーの9のリスナーかもしれないのでこのルールで検出することはできません。

<!-- ✓ GOOD -->
<input v-on:keydown.enter="submit">

<!-- ✗ BAD -->
<input v-on:keydown.13="submit">

vue/no-deprecated-data-object-declaration

このルールはdataオプションをオブジェクトで定義している箇所を検出します。

Vue.js 2.xでも通常のコンポーネントではdataオプションはfunctionで定義することが推奨されていましたが、Vue.js 3.xでは全てのdataオプションでfunctionでの定義しか受け入れなくなります。(Vue.js 2.xで利用できていたObjectによる定義は廃止されます)
(詳細はRFC0019を参照してください。)

/* ✗ BAD */
createApp({
  data: {
    foo: null
  }
}).mount('#app')

/* ✓ GOOD */
createApp({
  data () {
    return {
      foo: null
    }
  }
}).mount('#app')
<script>
/* ✗ BAD */
export default {
  data: {
    foo: null
  }
}

/* ✓ GOOD */
export default {
  data () {
    return {
      foo: null
    }
  }
}
</script>

vue/no-deprecated-events-api

このルールは$on$off$onceメソッドを使用している箇所を検出します。

これらのメソッドはVue.js 3.xで廃止されます。(詳細はRFC0020を参照してください。)

vue/no-deprecated-inline-template

このルールはインラインテンプレートの使用を禁止します。

そもそも僕はこのインラインテンプレート機能知らなかったですし、誰かSFCで使ってる人いるの?という感想しかないですが、Vue.js 3.xで廃止されます。(詳細はRFC0016を参照してください。)

vue/no-deprecated-functional-template

このルールは<template functional>の使用を禁止します。

functional テンプレートはVue.js 3.xで廃止されます。(詳細はRFC0007を参照してください。)

vue/no-deprecated-html-element-is

このルールはHTML要素へのis属性の使用を禁止します。

is属性は普通、<component>タグで使うと思うのですが、HTMLタグでも使用できていました。
しかしVue.js 3.xでは<component>タグでのみ使用でき、HTMLタグでは使用できなくなります。(詳細はRFC0027を参照してください。)

vue/no-deprecated-vue-config-keycodes

このルールはVue.config.keyCodesを使用している箇所を検出します。

vue/no-deprecated-v-on-number-modifiersルールの説明でも書いたように、Vue.js 3.xではキーコードを利用したキー修飾子は廃止されます。
これに伴って、キーコードを文字列修飾子として使うためのコンフィグがありましたが、こちらもVue.js 3.xで廃止されます。(詳細はRFC0014を参照してください。)

vue/no-deprecated-dollar-listeners-api

このルールは$listenersを使用している箇所を検出します。

$listenersv-onで登録された全てのイベントにアクセスできるAPIですが、Vue.js 3.xで廃止されます。(詳細はRFC0031を参照してください。)

vue/no-deprecated-v-on-native-modifier

このルールはv-on.native修飾子を使用している箇所を検出します。

v-on.nativeはコンポーネントのルート要素にネイティブイベントを登録できるAPIです。

ですが、Vue.js 3.xで廃止されます。(詳細はRFC0031を参照してください。)

vue/no-deprecated-dollar-scopedslots-api

このルールは$scopedSlotsを使用している箇所を検出します。

$scopedSlotsはVue.js 3.xで廃止され、同等機能が$slotsに実装されます。(詳細はRFC0006を参照してください。)

vue/no-deprecated-destroyed-lifecycle

このルールはdestroyed,beforeDestroyのライフサイクルフックを検出します。

Vue.js 3.xではunmounted,beforeUnmountを使用しましょう。

vue/no-deprecated-props-default-this

このルールはpropsdefault関数内でthisを使用している箇所を検出します。

propsオプションは、デフォルト値を生成する関数を定義できます。このデフォルト値を生成する関数内では、thisを使用して、自身のインスタンスにアクセスできていましたが、Vue.js 3.xではthisが使用できなくなりました。

次のコードはこのルールで報告されます。

<script>
export default {
  props: {
    foo: String,
    bar: {
      type: String,
      default () {
        // ✗ BAD
        return this.foo;
      }
    }
  }
}
</script>

このコードは次のように変更する必要があります。

<script>
export default {
  props: {
    foo: String,
    bar: {
      type: String,
      default (props) {
        // ✓ GOOD
        return props.foo;
      }
    }
  }
}
</script>

詳細は 移行ガイド - Props Default Function this Accessを参照してください。

vue/no-lifecycle-after-await

RFC0013のComposition APIのonMountedなどのライフサイクルフックは非同期でコールしても登録されない制限があるそうです。
そのため明らかに非同期であるとわかるawaitの後のライフサイクルフックをエラーとして検出します。

async setup() {
  /* ✓ GOOD */
  onMounted(() => { /* ... */ })

  await doSomething()

  /* ✗ BAD */
  onMounted(() => { /* ... */ })
}

vue/no-watch-after-await

RFC0013のComposition APIのwatchwatchEffectは非同期でコールして登録するとインスタンスと一緒に破棄されなかったりと問題がある利用方法があります。
そのため明らかに非同期であるとわかるawaitの後のwatchwatchEffectで、かつ、自力で破棄を管理しない利用をエラーとして検出します。

async setup() {
  /* ✓ GOOD */
  watchEffect(() => { /* ... */ })

  await doSomething()

  /* ✗ BAD */
  watchEffect(() => { /* ... */ })

  /* ✓ GOOD */
  // 自力で破棄までするならOK
  const stopHandle = watchEffect(() => { /* ... */ })
}

// ...

  // 破棄
  stopHandle()

vue/no-ref-as-operand

RFC0013のComposition APIのrefでラップされた値は.valueを経由して値を扱います。TypeScriptを使っていれば概ねエラー検出されるような気もしますが、このルールでもいくつかの間違ったパターンをエラーとして検出します。

const count = ref(0)
const ok = ref(true)

/* ✓ GOOD */
count.value++
count.value + 1
1 + count.value
var msg = ok.value ? 'yes' : 'no'

/* ✗ BAD */
count++
count + 1
1 + count
var msg = ok ? 'yes' : 'no'

vue/no-setup-props-destructure

RFC0013のComposition APIのpropssetupの引数で与えられます。しかし、この値の取り出し方を間違えるとリアクティブに動作しなくなってしまうため、そのパターンをいくつかエラーとして検出します。

常に、propsからメンバーを取り出すように記述すると問題ありません。

/* ✓ GOOD */
setup(props) {
  watch(() => {
    console.log(props.count)
  })

  return () => {
    return h('div', props.count)
  }
}

次のように引数を分割代入にしたりするとリアクティブに動作しません。このルールではこのパターンをエラーとして検出します。

/* ✗ BAD */
setup({ count }) {
  watch(() => {
    console.log(count) // countの変更は検知されません。
  })

  return () => {
    return h('div', count) // countが変更されても更新されません。
  }
}

また次のようにトップレベルで値を取り出して利用する場合もリアクティブに動作しません。このルールではこのパターンもエラーとして検出します。

setup(props) {
  /* ✗ BAD */
  const { count } = props

  watch(() => {
    console.log(count) // countの変更は検知されません。
  })

  return () => {
    return h('div', count) // countが変更されても更新されません。
  }
}

vue/require-toggle-inside-transition

このルールは<transition>要素の子要素が(Vue.js 3.x観点で)明らかv-if等の表示のコントロールをしていない箇所を検出します。

Vue.js 2.xでも利用できそうなルールですが、Vue.js 2.xではVue.js作者の意図していなかった操作による<transition>を動作させる方法があるようです。
Vue.js 3.xではその意図していなかった方法は利用できなくなります。(詳細はRFC0017を参照してください。)

vue/require-explicit-emits

Vue.js 3.xで追加されるemitsオプション関連のルールです。emitsオプションの詳細はRFC0030を確認してください。

このルールは、コンポーネントが、$emitや、setupcontext.emitを利用して、イベントをトリガーしているのに、emitsオプションに定義されていないイベント名を報告します。

報告されたエラーはESLintのSuggestions APIを使用して修正候補を提供します。
このルールを使用して、VSCodeで修正候補をポチポチ押して修正していくことで、Vue.js 2.xからの移行作業の助けになるかもしれません。

vue/return-in-emits-validator

Vue.js 3.xで追加されるemitsオプション関連のルールです。
emitsfunctionを定義してイベントの引数を検証することができます。

このルールでは、イベント引数の検証functionが結果の返り値を返しているかどうかを確認して誤った利用を報告します。

vue/require-slots-as-functions

このルールは$slotsのプロパティを(明らかに)functionとして使用してない箇所を検出します。

$slots.fooはVue.js 2.xではvnode配列を保持していましたが、Vue.js 3.xではvnode配列を返すfunctionとなります。つまりVue.js 2.xの$scopedSlots相当の機能がVue.js 3.xでは$slotsに実装されます。(詳細はRFC0006を参照してください。)

このルールを使うと、間違ってVue.js 2.xのノリでvnode配列を返すと思い込んで実装してしまったであろう箇所をいくつか検出することができます。

vue/valid-v-is

このルールはv-isの間違った使用をしている箇所を検出します。

Vue.js 2.xからis属性という特別な属性がありますが、ネイティブのHTML要素にこれは使用できなくなりました。(詳細はRFC0027を参照してください。)
よほどのことがない限り使いことはないと思いますが、Vue.js 3.xではv-isを使用します。

このルールはv-isの間違った使用を検出します。(valueが無い!とか)

vue/no-v-for-template-key-on-child

このルールは

<template v-for="x in list">
  <div :key="x.key"/>
</template>

というように、<template v-for>keyを子要素に配置している箇所を報告します。これは、

<template v-for="x in list" :key="x.key">
  <div />
</template>

としてください。

この書き方は、Vue.js 2.xでは不正な記述でしたが、Vue.js 3.xでは推奨されます。(とういうか前者の書き方ではVue.js 3.xコンパイラがエラーを報告します。(vue-next 3.0.0-rc.6時点))

Vue.js 3.xのFragment関連の変更によって、<template v-for>key<template>要素に配置できるようになました。このルールは古い書き方を報告して、新しい書き方を促します。

Vue.js 2.xでは<template>の子要素が複数ある場合、:keyを使用して最適化したい場合、

<template v-for="x in list">
  <div :key="x.key+'_1'"/>
  <div :key="x.key+'_2'"/>
  <div :key="x.key+'_3'"/>
  <div :key="x.key+'_4'"/>
</template>

のように、全ての要素に:keyを書かなければなりませんでしたが、Vue.js 3.xでは

<template v-for="x in list" :key="x.key">
  <div />
  <div />
  <div />
  <div />
</template>

で、十分です。(<transition>目的でkeyを使用している場合はその限りではないかもしれません。)

とてもスッキリしますね!

vue/experimental-script-setup-vars

このルールはエラーを報告しません。
<script setup>で定義した、propsなどの変数(引数)を使用した箇所で、変数が未定義であると警告されるのを防ぐルールです。
具体的にはESLintコアルールno-undefによって、props変数を使用した箇所で警告になるのを防ぎます。

勘違いされる前にお伝えしておくと、eslint-plugin-vueはまだ<script setup>をサポートしていません。#1248で作業を開始しようというお気持ちを見せていますが、全然進めていませんテヘペロ(・ω<)
サポートする作業を手伝ってくれる方は、是非、必要な作業をリストアップしたり、プルリクしてくださいmm

vue/experimental-script-setup-varsルールを使用しない場合、次のソースコードはno-undefルールによって警告されます。

<script setup="props, { emit }">
import { watchEffect } from 'vue'

watchEffect(() => console.log(props.msg)) // "'props' is not defined." のエラー
emit('foo') // "'emit' is not defined." のエラー
</script>

これは、propsemitの変数がJavaScript上で定義されていないため、未定義の変数が使用されているとして警告されます。
しかしこれらの変数は<script setup="props, { emit }">setup部分に定義されており、Vue.jsの構文としては有効な変数です。

vue/experimental-script-setup-varsルールは<script setup="props, { emit }">を解析し、Hackじみたことをして、ESLintに定義済みの変数であることを伝えます。

おそらく正攻法で対応するにはパーサーを修正(作成)し、@typescript-eslint/parserのバージョン4と同じように新しいScopeManagerを提供するようにすべきだと思いますが、今後大きく変わるかもしれない実験的機能の対応のために、正直そこまでやっていられないので、vue/experimental-script-setup-varsルールで暫定的に対応しました。なのでこちらのルールも実験的機能として位置付けています。

Vue.js 3.x用に変更された検証ルール

vue/valid-template-root

Vue.js 2.xではテンプレートのトップレベルの要素は一つまででしたが、Vue.js 3.xではこの制限が無くなり、複数要素やテキストノードをルートに設定することができるようです。

このルールは今まで、「トップレベルの要素は一つ」というのをチェックしていましたが、このロジックはvue/no-multiple-template-rootという別のルールに切り出されvue/valid-template-rootではチェックしなくなりました。

vue/valid-v-model

RFC0005RFC0011v-modelAPIの動作が変更され、引数とカスタム修飾子が受け入れられるようになります。
vue/valid-v-modelではVue.js 2.xまで受け入れられなかった引数とカスタム修飾子をエラーとして報告していましたが、Vue.js 3.xでエラーにならないパターンは別のルールに切り出され、vue/valid-v-modelでは検出しないように変更されました。

vue/no-template-key

<template v-for"..." :key="...">のような記述は、Vue.js 2.xでは不正でしたが、
vue/no-v-for-template-key-on-childの説明で書いたように、Vue.js 3.xでは<template v-for>key<template>要素に配置できるようになりました。

これに対応するために、このルールは、v-forを持つ<template>に配置されたkeyを報告しなくなりました。

ただし、Vue.js 2.xでは不正なので、別のルールでv-forを持つ<template>に配置されたkeyをチェックします。

vue/valid-v-for, vue/require-v-for-key

上でも書いたように、Vue.js 3.xでは<template v-for>key<template>要素に配置できるようになりました。

これに対応するために、これらのルールは、<template>keyが配置された場合にエラーを報告しなくなりました。

Vue.js 3.x用に変更されたその他

あとは細かいことなのでリリースノートを参照してください。

最後に

Vue.js 3.x使っていてこんなルールも必要だ!というアイディアがある方
ぜひissueの投稿をお願いします!

あと、どなたか手伝ってくれる方がいればプルリクもお願いします!

なんなら僕のプルリクにレビューコメント書いてくれるだけでも嬉しいです!本当にお願いします!

ota-meshi
会計パッケージ開発10年、独立系SIer5年経験。6歳と1歳の娘と格闘の毎日のJSON色つけ係です。Java8+、ES6+、Vue、ESLint、stylelint、postcss、python、webpack。会社のOSS CheetahGridのメンテナ。Vue.jsのメンバー。stylelintのメンバー。自分の備忘録としても使うので小さい情報も書いていきます。
future
ITを武器とした課題解決型のコンサルティングサービスを提供します
http://future-architect.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away