Vue2からVue3への変更例

## はじめに

前回以下を作成した。

https://qiita.com/kenji123/items/aa83cb19d6bc951192e1

フロントをVue2記法のOptionAPIで記載していたが、Vue3の目玉であるCompositon APIを使用して書き換える。
Vue2をさわったことがあるが、Vue3はイマイチという層を対象としている。

## Vue2からVue3にあたって分かればよいこと
Vue3で、Vue2使用者でもつまづくというのを聞くが、一旦以下を押さえておけば大丈夫なような気がする。
Vueの公式ページであるが、以下を読んで始めれば問題ないのではないかと思われる。

https://ja.vuejs.org/guide/introduction.html

要はVue2はOptions API、Vue3はCompositon APIで主に記載するということである。

以下をまず押さえれればよい。
・data()　→　ref()で扱う
・methods:{}　→　functionメソッドやアロー関数を使用する。
・setup()関数が出てくる。

以上を抑えれば問題ないのではと思う。
特にrefとかは知らないと説明だけ見てもかなり抵抗感があった。

## Composition APIで記載
実際に例を出して書いてみる。前回Vue2のOptions APIで記載してみたので今回はVue3のComposition APIで書いてみる。

変更となるのは、scriptタグ内である。

### 親コンポーネント(MainPage.vue)
・変更前（OptionAPI)
```MainPage.vue
<script>
export default {
    data() {
        return {
            noteList: [],
        }
    },
    methods: {
        onClickButtonAdd: function () {
            console.log('aaaa')
            this.noteList.push({
                id: new Date().getTime().toString(16),
                name: `新規ノート`,
                mouseover: false,
                editing: false,
            })
        },
        onDeleteNote: function (deleteNote) {
            const index = this.noteList.indexOf(deleteNote);
            this.noteList.splice(index, 1);
        },
        onEditNoteStart: function (editNote) {
            for (let note of this.noteList) {
                note.editing = (note.id === editNote.id);
            }
        },
        onEditNoteEnd: function () {
            for (let note of this.noteList) {
                note.editing = false;
            }
        },
    },

    components: {
        NoteItem,
    },
}
</script>
```
↓
・変更後(Compsition API)

```MainPage.vue

<script>
import NoteItem from './parts/NoteItem.vue'
import { ref, reactive } from 'vue'

export default {
    components: {
        NoteItem,
    },
    setup() {
        const noteList = ref([])

        const onClickButtonAdd = () => {
            noteList.value.push({
                id: new Date().getTime().toString(16),
                name: `新規ノート`,
                mouseover: false,
                editing: false,
            })
        }

        const onDeleteNote = (deleteNote) => {
            const index = noteList.value.indexOf(deleteNote)
            noteList.value.splice(index, 1)
        }

        const onEditNoteStart = (editNote) => {
            for (let note of noteList.value) {
                note.editing = (note.id === editNote.id)
            }
        }

        const onEditNoteEnd = () => {
            for (let note of noteList.value) {
                note.editing = false
            }
        }

        return {
            noteList,
            onClickButtonAdd,
            onDeleteNote,
            onEditNoteStart,
            onEditNoteEnd,
        }
    }

}
</script>

```

### 子コンポーネント(NoteItem.vue)

・変更前（OptionAPI)
```NoteItem.vue
<script>
export default {
    name: 'NoteItem',
    props: [
        'note',
    ],
    methods: {
        onMouseOver: function () {
            this.note.mouseover = true;
        },
        onMouseLeave: function () {
            this.note.mouseover = false;
        },
        onClickDelete: function (note) {
            this.$emit('delete', note);
        },
        onClickEdit: function (note) {
            this.$emit('editStart', note);
        },
        onEditEnd: function () {
            this.$emit('editEnd');
        },
    },
}

```

・変更後(Compsition API)
```NoteItem.vue
<script>
import { ref } from 'vue'

export default {
    name: 'NoteItem',
    props: [
        'note',
    ],
    setup(props, context) {
        const onMouseOver = () => {
            props.note.mouseover = true
        }

        const onMouseLeave = () => {
            props.note.mouseover = false
        }

        const onClickDelete = (note) => {
            context.emit('delete', note)
        }

        const onClickEdit = (note) => {
            context.emit('editStart', note)
        }

        const onEditEnd = () => {
            context.emit('editEnd')
        }

        return {
            onMouseOver,
            onMouseLeave,
            onClickDelete,
            onClickEdit,
            onEditEnd,
        }
    }
}
</script>

```

### 説明
Composition APIでは、setup()関数を使ってコンポーネントの状態とメソッドを定義する。setup()関数は2つの引数を取る。一つ目はpropsで、親コンポーネントから渡されるプロパティを参照する。二つ目はcontextで、いくつかの有用なプロパティ（emit、slots、attrs）を提供する。

refとreactiveはリアクティブなデータを作成するためのヘルパー関数。refはリアクティブなデータを作成し、valueプロパティを通じてデータにアクセスできる。
reactiveはリアクティブなオブジェクトを作成し、直接プロパティにアクセスできます。ただし、今回のケースでは主にrefを使用した。

それぞれのメソッドはアロー関数を使用して定義している。これによりthisのスコープがsetup関数に固定されるらしい。

最後に、setup()関数はオブジェクトを返し、その中に含まれる全てのプロパティとメソッドはテンプレートで使用できる。

なお、上記の例ではonMouseOver、onMouseLeaveは直接props.noteを変更しているが、通常はpropsを直接変更することは避ける。その代わりに親コンポーネントへのイベント通知やVuexなどの状態管理ライブラリを使用することを推奨される。


## 終わりに
Vue3のComposition APIは、コンポーネントのロジックをより明確に管理することができるらしいが、確かにわかりやすい。
ただ、今回は簡易にしようという試みで記載したが、実際のところはそうもいかない、その理由をざっくりその他にメモで記載しておく。


## その他
Vue 3で導入されたComposition APIは、Vue.jsの開発体験を大きく変えました。特に、リアクティブシステムの扱い方がOptions APIと比較して大きく異なります。`toRefs`のような新しいAPIの導入は、より細かいリアクティブデータの制御を可能にする一方で、Vue 2やOptions APIに慣れている開発者にとっては学習曲線を伴う変更です。Vue 3のこの部分がややこしく感じられる理由には、以下のような点があります。

### 新しいリアクティブシステムの概念
Vue 3では、`ref`, `reactive`, `toRefs`などの新しいリアクティブシステム関連のAPIが導入されました。これらは、データのリアクティブ性をより細かく、柔軟に扱うための強力なツールですが、それぞれの使い方や適切な使用シーンを理解する必要があります。

### 構成オプションとの統合
Composition APIは、コンポーネントのロジックを機能別に分割し、再利用しやすくすることを目的としています。しかし、これには複数のリアクティブなプロパティや関数を`setup`関数内で管理することが含まれ、Options APIでの開発に慣れている場合、この新しいパターンに適応するのが難しいかもしれません。

### より高度なJavaScriptの知識が求められる
Composition APIを効果的に使用するには、JavaScriptのより高度な概念（分割代入、リアクティブプロパティの参照、コンピューテッドプロパティとウォッチャの手動作成など）に慣れている必要があります。これにより、Vue 3の学習曲線は初学者やVue 2からの移行者にとってやや急なものになる可能性があります。

### 移行の複雑さ
既存のVue 2アプリケーションをVue 3に移行する過程では、リアクティブデータの扱い方の違いに加えて、他のいくつかの変更点（Composition APIの導入、スロットの構文変更、非同期コンポーネントの扱い方など）にも対応する必要があります。

これらの変更には明確な利点があり、大規模なアプリケーションや複雑なコンポーネント間の相互作用を扱う際に、より良い構造とメンテナンス性を提供します。しかし、これらの新しい概念とAPIに慣れるには、時間と実践が必要です。Vue 3のドキュメントやコミュニティからのサポート、チュートリアルを通じて、徐々に新しいパターンを身につけていくことが重要です。