せっかく作ったので供養。
書き方
前提
Vue.js 3.4 以上
コード
<script setup lang="ts" generic="T"> import { ref } from 'vue' // 子コンポーネント読み込み import ChildComponent from 'ChildComponent' // options defineOptions({ inheritAttrs: false }) // -------------------------------- // v-model は defineModel を使う // required: true を指定する const visible = defineModel({ required: true }) // 必須でない場合は基本的に default 値を設定する const name = defineModel({ default: '' }) // defineModel が複数存在する場合は、メインとなる一つ以外に名前をつける const email = defineModel('email', { default: '' }) // -------------------------------- // props は型引数で定義(type-based declaration) // 定義を分ける必要はない const props = defineProps<{ foo: T bar?: string[] }>() // デフォルト値を指定する場合の書き方 // 必須ではない props がある場合は必須、ないなら指定してはいけない const props = withDefaults(defineProps<Props>(), { baz: () => ['one', 'two'] }) // -------------------------------- // emit も props と同様に型引数で定義 // 引数は名前付きタプルで指定する const emit = defineEmits<{ change: [id: number] update: [value: string] }>() // -------------------------------- // slot const slots = defineSlots<{ default(props: { message: string }): any }>() // -------------------------------- // 動的なスタイル // クラスの付け替えではなく、CSS から変数を参照する const color = computed(() => value.value === 'red' ? 'red' : 'black') // -------------------------------- const value = ref('') const method = () => {} </script> <template> <ChildComponent> <section class="parent_section"> <slot :msg="value" /> </section> </ChildComponent> </template> <style lang="scss" scoped> .parent_section { border: solid 1px #000; color: v-bind(color); } </style>
他
- top level await は使用しない
- suspense がまだ実験的機能
- script setup と通常の script は併用禁止
defineOptions
を使う
- 基本的にスタイルガイドに合わせる
検討したこと
- ブロックの順番
- 明示的に「この順番がいい」ということは書いていない
- Vue.js 公式では基本的に script, template, style の順番で記述されており、こちらがデファクトスタンダードと思われる
- デファクトスタンダードに合わせない積極的な理由はないと思うので、それに合わせるのが良さそう
- React も Svelte も script 先行だし、それに合わせたほうが認知負荷が下がる=Vue の経験が浅くても読みやすくなる
- CSS の
v-bind()
- BEM でいう Modifier が撲滅できるし、JS の仕組みに沿った管理ができる(Composable 化とか)ので、積極的に使っていくのが良さそう