🐡

eslint-plugin-vueからVue.jsを学ぶ

2024/12/11に公開

はじめに

eslint-plugin-vue には Vue.js のコーディングルールが数多く詰まっています。
本記事では、その一部ルールを取り上げ、ルールの背景となる「なぜ」を紐解きながら、Vue.js について学習していきましょう。

そもそもeslint-plugin-vueとは

ご存じの方も多いかもしれませんが、eslint-plugin-vue は Vue.js 公式の ESLint プラグインです。
公式ドキュメントによると、.vue ファイルの <template><script> 部分、さらに .js ファイル内の Vue.js コードを ESLint でチェックできるようにしてくれます。

This plugin allows us to check the <template> and <script> of .vue files with ESLint, as well as Vue.js code in .js files.

(和訳)このプラグインを使用すると、ESLint で .vue ファイルの <template><script>、および .js ファイル内の Vue.js コードをチェックできます。

取り上げるルール

eslint-plugin-vue には多くのルールがあります(2024/11/23 時点で 137 個)。
本記事では以下の 3 つをピックアップし、それぞれのルールの背景やルールから学べる点を考察します。

  1. vue/no-v-html
  2. vue/no-async-in-computed-properties
  3. vue/no-lifecycle-after-await

1. vue/no-v-htmlv-html の使用禁止(または制限)

eslint-plugin-vue

ルールの内容

公式より抜粋:

This rule reports all uses of v-html directive
(和訳)このルールは、v-html ディレクティブのすべての使用を報告します。

ルールの背景

v-html は任意の HTML を挿入できるため、XSS 攻撃の温床になり得ます。
v-html について Vue.js 公式ドキュメントでは、以下のように注意喚起しています。

セキュリティーに関する注意
ウェブサイト上で任意の HTML を動的にレンダリングすることは、XSS 攻撃につながりやすいため、非常に危険です。信頼できるコンテンツにのみ v-html を使用し、ユーザーが提供するコンテンツには 絶対に 使用しないでください。

Vue.js は {{ }} 内のデータを自動的にエスケープしますが、v-html を使うとこれをバイパスしてしまいます。

ルールからの学び

  • 外部から取り込むデータは常にサニタイズするなど、セキュアなコーディングを心がける。
  • 生 HTML を挿入したい場合は、DOMPurify などでサニタイズしてから描画するなど、十分な対策を取る。

改善例

<template>
  <div>{{ sanitizedContent }}</div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import DOMPurify from 'dompurify';

const props = defineProps({ rawHtmlContent: String });
const sanitizedContent = computed(() => DOMPurify.sanitize(props.rawHtmlContent));
</script>

2. vue/no-async-in-computed-properties:算出プロパティ内での非同期処理禁止

vue/no-async-in-computed-properties

ルールの内容

公式より抜粋(今回は算出プロパティに着目)

Computed properties and functions should be synchronous.
(和訳)算出プロパティや関数は同期的であるべきです。

ルールの背景

computed() はあくまで純粋なゲッター関数を意図しており、非同期処理を入れると更新タイミングが予測困難になり、依存関係の追跡も困難になります。
公式ドキュメントでも、算出プロパティのゲッター関数の副作用を伴う処理を含めることへの注意喚起があります。

getter 関数は副作用のないものでなければならない​
算出プロパティにおける getter 関数は計算のみを行い、副作用がないようにすることが重要です。例えば、算出プロパティの getter の内部で他のステートを変更したり、非同期リクエストを実行したり、DOM を変更したりしないようにしましょう!

ルールからの学び

  • 非同期処理は onMountedwatchmethods 内で行い、その結果を ref に格納してから算出プロパティで参照する。
  • 算出プロパティはあくまでも純粋な計算ロジックに徹し、副作用や非同期処理を避ける。

改善例

<template>
  <div>{{ data }}</div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';

const data = ref(null);
onMounted(async () => {
  const response = await fetch('https://api.example.com/data');
  data.value = await response.json();
});
</script>

3. vue/no-lifecycle-after-awaitawait 後のライフサイクルフック登録禁止

vue/no-lifecycle-after-await

ルールの内容

公式より抜粋:

This rule reports the lifecycle hooks after await expression.
(和訳)このルールは、await 式の後にライフサイクルフックがある場合に報告します。

ルールの背景

Vue コンポーネントインスタンスには描画されるまでにライフサイクルという概念が存在します。
各ライフサイクルのタイミングは公式のライフサイクルダイアグラムをご覧ください。

ライフサイクルダイアグラム

lifecycle

ライフサイクルでの各処理は setup() 内で onMounted のようなライフサイクルフックとして記述できます。
ライフサイクルフックはコールバック関数で記述され、Vue コンポーネントインスタンスに登録されます。
await 式で処理タイミングがズレるとライフサイクルフック登録が行えず、うまく機能しなくなってしまいます。

ルールからの学び

  • ライフサイクルフック(onMounted など)は setup 関数内で同期的に登録する。
  • 非同期処理が必要な場合は、ライフサイクルフック内で await を用いて実行する。

改善例

<script setup lang="ts">
import { onMounted } from 'vue';

async function initializeApp() {
  // 非同期処理
}

onMounted(async () => {
  await initializeApp();
  // 続きの初期化処理
});
</script>

まとめ

eslint-plugin-vue のルールを通じて、セキュリティ上の注意点やステート管理方法、ライフサイクルに関する正しい扱い方など、Vue.js における「なぜ」を学ぶことができました。
他にも多くのルールが存在するため、時間があるときに eslint-plugin-vue を覗いてみましょう!Vue.js でのコーディングに関するさまざまな発見があるはずです👊

GitHubで編集を提案
Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion