eslint-plugin-vueからVue.jsを学ぶ
はじめに
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 つをピックアップし、それぞれのルールの背景やルールから学べる点を考察します。
- vue/no-v-html
- vue/no-async-in-computed-properties
- vue/no-lifecycle-after-await
vue/no-v-html:v-html
の使用禁止(または制限)
1. ルールの内容
公式より抜粋:
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>
vue/no-async-in-computed-properties:算出プロパティ内での非同期処理禁止
2.ルールの内容
公式より抜粋(今回は算出プロパティに着目)
Computed properties and functions should be synchronous.
(和訳)算出プロパティや関数は同期的であるべきです。
ルールの背景
computed()
はあくまで純粋なゲッター関数を意図しており、非同期処理を入れると更新タイミングが予測困難になり、依存関係の追跡も困難になります。
公式ドキュメントでも、算出プロパティのゲッター関数の副作用を伴う処理を含めることへの注意喚起があります。
getter 関数は副作用のないものでなければならない
算出プロパティにおける getter 関数は計算のみを行い、副作用がないようにすることが重要です。例えば、算出プロパティの getter の内部で他のステートを変更したり、非同期リクエストを実行したり、DOM を変更したりしないようにしましょう!
ルールからの学び
- 非同期処理は
onMounted
やwatch
、methods
内で行い、その結果を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>
vue/no-lifecycle-after-await:await
後のライフサイクルフック登録禁止
3. ルールの内容
公式より抜粋:
This rule reports the lifecycle hooks after await expression.
(和訳)このルールは、await
式の後にライフサイクルフックがある場合に報告します。
ルールの背景
Vue コンポーネントインスタンスには描画されるまでにライフサイクルという概念が存在します。
各ライフサイクルのタイミングは公式のライフサイクルダイアグラムをご覧ください。
ライフサイクルダイアグラム
ライフサイクルでの各処理は 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 でのコーディングに関するさまざまな発見があるはずです👊
Discussion