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

> - 2020/09/30 [eslint-plugin-vue] 7.0.0 リリースに伴う追記・編集

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

- [vuejs/eslint-plugin-vue - Release v7.0.0]

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

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

```bash
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`の場合：

```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用の設定に置き換えてください。

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

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

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


- [plugin:vue/vue3-essential](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention-for-vue-js-3-x)
- [plugin:vue/vue3-strongly-recommended](https://eslint.vuejs.org/rules/#priority-b-strongly-recommended-improving-readability-for-vue-js-3-x)
- [plugin:vue/vue3-recommended](https://eslint.vuejs.org/rules/#priority-c-recommended-minimizing-arbitrary-choices-and-cognitive-overhead-for-vue-js-3-x)

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

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

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

- [vuejs/eslint-plugin-vue - Release v7.0.0]

## [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ガイド - カスタムイベント - `.sync` 修飾子]

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

## [vue/no-deprecated-v-on-number-modifiers]

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

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

```vue
<!-- ✓ 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]を参照してください。）

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

/* ✓ GOOD */
createApp({
  data () {
    return {
      foo: null
    }
  }
}).mount('#app')
```

```vue
<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`を使用している箇所を検出します。

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

## [vue/no-deprecated-v-on-native-modifier]

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

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

- [コンポーネントにネイティブイベントをバインディング](https://jp.vuejs.org/v2/guide/components-custom-events.html#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AB%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E3%83%90%E3%82%A4%E3%83%B3%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0)

ですが、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]

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

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

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

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

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

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

詳細は [移行ガイド - Props Default Function `this` Access](https://v3.vuejs.org/guide/migration/props-default-this.html)を参照してください。

## [vue/no-lifecycle-after-await]

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

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

  await doSomething()

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

## [vue/no-watch-after-await]

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

```js
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を使っていれば概ねエラー検出されるような気もしますが、このルールでもいくつかの間違ったパターンをエラーとして検出します。

```js
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の`props`は`setup`の引数で与えられます。しかし、この値の取り出し方を間違えるとリアクティブに動作しなくなってしまうため、そのパターンをいくつかエラーとして検出します。

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

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

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

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

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

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

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

```js
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`や、`setup`の`context.emit`を利用して、イベントをトリガーしているのに、[emits]オプションに定義されていないイベント名を報告します。

報告されたエラーは[ESLintのSuggestions API](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)を使用して修正候補を提供します。
このルールを使用して、VSCodeで修正候補をポチポチ押して修正していくことで、Vue.js 2.xからの移行作業の助けになるかもしれません。

## [vue/return-in-emits-validator]

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

このルールでは、イベント引数の検証`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]

このルールは

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

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

```vue
<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`を使用して最適化したい場合、

```vue
<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では

```vue
<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](https://github.com/vuejs/eslint-plugin-vue/issues/1248)で作業を開始しようというお気持ちを見せていますが、全然進めていませんﾃﾍﾍﾟﾛ(・ω<)
> サポートする作業を手伝ってくれる方は、是非、必要な作業をリストアップしたり、プルリクしてくださいmm

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

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

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

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

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

おそらく正攻法で対応するにはパーサーを修正（作成）し、[`@typescript-eslint/parser`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser)のバージョン4と同じように新しい[ScopeManager](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/scope-manager)を提供するようにすべきだと思いますが、今後大きく変わるかもしれない実験的機能の対応のために、正直そこまでやっていられないので、[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]

[RFC0005]と[RFC0011]で`v-model`APIの動作が変更され、引数とカスタム修飾子が受け入れられるようになります。
[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用に変更されたその他

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

- [vuejs/eslint-plugin-vue - Release v7.0.0]

# 最後に

Vue.js 3.x使っていてこんなルールも必要だ！というアイディアがある方
ぜひ[issue](https://github.com/vuejs/eslint-plugin-vue/issues)の投稿をお願いします！

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

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

[vue-next]: https://github.com/vuejs/vue-next

[eslint-plugin-vue]: https://github.com/vuejs/eslint-plugin-vue
[vuejs/eslint-plugin-vue - Release v7.0.0]: https://github.com/vuejs/eslint-plugin-vue/releases/tag/v7.0.0

[フィルター]: https://jp.vuejs.org/v2/guide/filters.html
[Vue.jsガイド - カスタムイベント - `.sync` 修飾子]: https://jp.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A3%BE%E5%AD%90
[インラインテンプレート]: https://jp.vuejs.org/v2/guide/components-edge-cases.html#%E3%82%A4%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88
[functional テンプレート]: https://jp.vuejs.org/v2/guide/render-function.html#%E9%96%A2%E6%95%B0%E5%9E%8B%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88
[`is`属性]: https://jp.vuejs.org/v2/api/#is
[emits]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0030-emits-option.md

[`<script setup>`]: https://github.com/vuejs/rfcs/blob/sfc-improvements/active-rfcs/0000-sfc-script-setup.md
[no-undef]: https://eslint.org/docs/rules/no-undef

[vue/no-deprecated-filter]: https://eslint.vuejs.org/rules/no-deprecated-filter.html
[vue/no-deprecated-v-bind-sync]: https://eslint.vuejs.org/rules/no-deprecated-v-bind-sync.html
[vue/no-lifecycle-after-await]: https://eslint.vuejs.org/rules/no-lifecycle-after-await.html
[vue/no-ref-as-operand]: https://eslint.vuejs.org/rules/no-ref-as-operand.html
[vue/no-setup-props-destructure]: https://eslint.vuejs.org/rules/no-setup-props-destructure.html

[vue/no-watch-after-await]: https://eslint.vuejs.org/rules/no-watch-after-await.html
[vue/require-toggle-inside-transition]: https://eslint.vuejs.org/rules/require-toggle-inside-transition.html
[vue/no-deprecated-v-on-number-modifiers]: https://eslint.vuejs.org/rules/no-deprecated-v-on-number-modifiers.html
[vue/no-deprecated-data-object-declaration]: https://eslint.vuejs.org/rules/no-deprecated-data-object-declaration.html
[vue/no-deprecated-events-api]: https://eslint.vuejs.org/rules/no-deprecated-events-api.html
[vue/no-deprecated-inline-template]: https://eslint.vuejs.org/rules/no-deprecated-inline-template.html

[vue/no-deprecated-html-element-is]: https://eslint.vuejs.org/rules/no-deprecated-html-element-is.html
[vue/no-deprecated-vue-config-keycodes]: https://eslint.vuejs.org/rules/no-deprecated-vue-config-keycodes.html
[vue/no-deprecated-functional-template]: https://eslint.vuejs.org/rules/no-deprecated-functional-template.html

[vue/require-explicit-emits]: https://eslint.vuejs.org/rules/require-explicit-emits.html
[vue/return-in-emits-validator]: https://eslint.vuejs.org/rules/return-in-emits-validator.html
[vue/no-deprecated-v-on-native-modifier]: https://eslint.vuejs.org/rules/no-deprecated-v-on-native-modifier.html
[vue/no-deprecated-dollar-listeners-api]: https://eslint.vuejs.org/rules/no-deprecated-dollar-listeners-api.html

[vue/no-deprecated-dollar-scopedslots-api]: https://eslint.vuejs.org/rules/no-deprecated-dollar-scopedslots-api.html
[vue/require-slots-as-functions]: https://eslint.vuejs.org/rules/require-slots-as-functions.html

[vue/valid-template-root]: https://eslint.vuejs.org/rules/valid-template-root.html
[vue/no-multiple-template-root]: https://eslint.vuejs.org/rules/no-multiple-template-root.html
[vue/valid-v-model]: https://eslint.vuejs.org/rules/valid-v-model.html

[vue/no-deprecated-destroyed-lifecycle]: https://eslint.vuejs.org/rules/no-deprecated-destroyed-lifecycle.html

[vue/valid-v-is]: https://eslint.vuejs.org/rules/valid-v-is.html

[vue/no-v-for-template-key-on-child]: https://eslint.vuejs.org/rules/no-v-for-template-key-on-child.html
[vue/no-template-key]: https://eslint.vuejs.org/rules/no-template-key.html
[vue/valid-v-for]: https://eslint.vuejs.org/rules/valid-v-for.html
[vue/require-v-for-key]: https://eslint.vuejs.org/rules/require-v-for-key.html

[vue/no-deprecated-props-default-this]: https://eslint.vuejs.org/rules/no-deprecated-props-default-this.html
[vue/experimental-script-setup-vars]: https://eslint.vuejs.org/rules/experimental-script-setup-vars.html

[RFC0015]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0015-remove-filters.md
[RFC0005]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md
[RFC0013]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0013-composition-api.md
[RFC0011]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md

[RFC0016]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0016-remove-inline-templates.md
[RFC0017]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0017-transition-as-root.md
[RFC0020]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0020-events-api-change.md
[RFC0014]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0014-drop-keycode-support.md
[RFC0019]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0019-remove-data-object-declaration.md

[RFC0007]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0007-functional-async-api-change.md
[RFC0027]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0027-custom-elements-interop.md

[RFC0030]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0030-emits-option.md
[RFC0031]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md

[RFC0006]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0006-slots-unification.md