12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【ハンズオン】NuxtでTypeScriptとComposition APIを使おう!

Last updated at Posted at 2021-04-23

TypeScript とは

TypeScript はマイクロソフトによって開発され、メンテナンスされているフリーでオープンソースのプログラミング言語です。

TypeScript は JavaScript に対して、省略も可能な静的型付けとクラスベースオブジェクト指向が加わりました。

詳しくは公式サイトをどうぞ。

導入

create-nuxt-app で TypeScript を選択

ターミナル
% npx create-nuxt-app nuxt-typescript

create-nuxt-app v3.6.0
✨  Generating Nuxt.js project in nuxt-typescript
? Project name: nuxt-typescript
? Programming language: TypeScript
? Package manager: Npm
? UI framework: None
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? What is your GitHub username? XXXXX
? Version control system: Git

モジュールを追加

ターミナル
$ npm install --save-dev @nuxt/types

ファイルの作成

ターミナル
$ touch shims-vue.d.ts
shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

tsconfig.jsonに追記

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "ESNext",
    "moduleResolution": "Node",
    "lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["./*"]
    },
    "types": ["@nuxt/types", "@types/node"]
  },
  "files": ["shims-vue.d.ts"],
  "include": [
    "components/**/*.ts",
    "components/**/*.vue",
    "layouts/**/*.ts",
    "layouts/**/*.vue",
    "pages/**/*.ts",
    "pages/**/*.vue"
  ],
  "exclude": ["node_modules", ".nuxt", "dist"]
}

これで完了です。

Composition API とは

v2.x 系の Options API での課題は以下の通りです。
コードがコンポーネントのthisに依存するため、
コンポーネントのオプション(datacomputedmethodswatch)にコードが分割され、
可読性が著しく落ちることでした。

皆さんも公式サイトの画像のように、このように分断されてしまうことが多いのではないでしょうか?
image.png

この問題を解決するために、生まれたのが Composition API です。

Vue/composition-apiの導入

モジュールを追加

ターミナル
% npm install --save @vue/composition-api

プラグインの追加

plugins/composition-api.ts
import Vue from 'vue';
import VueCompositionApi from '@vue/composition-api';

Vue.use(VueCompositionApi);

nuxt.config.js に追加

nuxt.config.js
export default {
  plugins: ['@/plugins/composition-api'],
};

Nuxt/composition-apiの導入

モジュールを追加

ターミナル
% npm install @nuxtjs/composition-api --save

nuxt.config.js に追加

nuxt.config.js
buildModules: [
    '@nuxtjs/composition-api/module'
  ]

nuxt.config.js に追加

nuxt.config.js
generate: {
    interval: 2000,
  }

これで完了です。

新要素

【TypeScript の型推論のために】defineComponent

v2.x のときは、Vue.extendが必要でした。
Composition API では、defineComponent に変更されました。

pages/index.vue
<script lang="ts">
import { defineComponent } from '@vue/composition-api';

export default defineComponent({});
</script>

【v2 のオプションをひとまとめに!】setup コンポーネント(引数: props, context)

pages/index.vue
<script lang="ts">
import { defineComponent, computed } from '@vue/composition-api';

export default defineComponent({
  props: {
    times: {
      type: Number,
      default: 2,
    },
  },
  setup(props, context) {
    // 2を取得
    const times = computed(() => {
      return props.times;
    });

    // Attributes (Non-reactive object)
    console.log(context.attrs);

    // Slots (Non-reactive object)
    console.log(context.slots);

    // Emit Events (Method)
    console.log(context.emit);
  },
});
</script>

【data はリアクティブに】ref, reactive

pages/index.vue
<template>
  <div>
    {{ count }}
    {{ task }}
    {{ status }}
    {{ tasks }}
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api';

type TaskType = {
  task: string;
  status: string;
  tasks: string[];
};

export default defineComponent({
  setup() {
    // ref
    const count = ref<number>(0);
    console.log(count); // { value: 0 }
    console.log(count.value) // 0

    count.value ++;
    console.log(count.value) // 1

    // reactive
    const state = reactive<TaskType>({
      task: '',
      status: '',
      tasks: []
    })

    return {
      count,
      // このようにスプレッド構文で取得するとスマート(ただこの場合、toRefsがないとリアクティブじゃなくなる)
      ...toRefs(state);
    }
  },
});
</script>

リアクティブとはなんぞや

何かの値が変更された時、依存する値も変わって欲しいというのがリアクティブです。

index.ts
// リアクティブじゃない
let x = 1;
let y = x + 4; // 5 → 5
x = 3;

// リアクティブ
let x = 1;
let y = x + 4; // 5 → 7
x = 3;

このリアクティブを実現するために、refreactiveがあります。

使い分けに関して

ref reactive
プリミティブ型 オブジェクト型

ちなみに、プリミティブとは 5 つの型を指します。
・number
・string
・boolean
・null
・undefined

methods もただの関数になりました

v2.X での書き方はこのような感じです。

pages/index.vue
<script>
export default {
  data() {
    return {
      tasks: [],
      task: '',
      status: '作業中',
    };
  },
  methods: {
    addTask() {
      this.tasks.push({
        name: this.task,
        status: this.status,
      });
    },
  },
};
</script>

これが Composition API ではこのように変わりました。

pages/index.vue
<template>
  <div>
    <ul>
      <li v-for="(task, index) in tasks" :key="index">
        <span v-if="task.status === true"> {{ index }} : {{ task.name }} / 作業中 </span>
        <span v-else> {{ index }} : {{ task.name }} / 完了 </span>
      </li>
    </ul>
    <input type="text" v-model="task" />
    <button @click="addTask">+</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';

type Tasks = {
  name: string
  status: boolean
};

export default defineComponent({
  setup() {
    const task = ref<string>('');
    const status = ref<boolean>(true);
    const tasks = ref<Tasks[]>([]);

    const addTask = () => {
      tasks.value.push({
        name: task.value,
        status: status.value,
      });

      task.value = '';
    };

    return {
      task,
      status,
      tasks,
      addTask,
    };
  },
});
</script>

computed は、@vue/composition-api から持ってくる

pages/index.vue
<template>
  <div>
    <h2>Task: {{ doTasks }}</h2>
    <ul>
      <li v-for="(task, index) in tasks" :key="index">
        <span v-if="task.status"> {{ index }} : {{ task.name }} / 作業中 </span>
        <span v-else> {{ index }} : {{ task.name }} / 完了 </span>
      </li>
    </ul>
    <input type="text" v-model="task" />
    <button @click="addTask">+</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed } from '@vue/composition-api';

type Tasks = {
  name: string;
  status: boolean;
};

export default defineComponent({
  setup() {
    const task = ref<string>('');
    const status = ref<boolean>(true);
    const tasks = ref<Tasks[]>([]);

    const addTask = () => {
      tasks.value.push({
        name: task.value,
        status: status.value,
      });

      task.value = '';
    };

    const doTasks = computed(() => {
      return tasks.value.filter(t => t.status).length;
    });

    return {
      task,
      status,
      tasks,
      addTask,
      doTasks,
    };
  },
});
</script>

【ハンズオン】実際にTodoリストを作ってみよう!

新規追加

pages/index.vue
<template>
  <div>
    <h1>ToDoリスト</h1>
    <br />
    <input type="radio" id="all" name="type" checked />
    <label for="all">すべて</label>
    <input type="radio" id="work" name="type" />
    <label for="work">作業中</label>
    <input type="radio" id="complete" name="type" />
    <label for="complete">完了</label>
    <br />

    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>コメント</th>
          <th>状態</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(task, index) in tasks" :key="index">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button>作業中</button></td>
          <td v-else><button>完了</button></td>
          <td><button>削除</button></td>
        </tr>
      </tbody>
    </table>
    <br />
    <h2>新規タスクの追加</h2>
    <input type="text" v-model="task" />
    <button @click="addTask()">追加</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';

type Tasks = {
  name: string;
  status: boolean;
};

export default defineComponent({
  setup() {
    // state
    const task = ref<string>('');
    const status = ref<boolean>(true); // true->作業中, false->完了
    const tasks = ref<Tasks[]>([]);

    // methods
    const addTask = (): void => {
      tasks.value.push({
        name: task.value,
        status: status.value,
      });
      task.value = '';
    };

    return {
      // state
      task,
      status,
      tasks,
      // methods
      addTask,
    };
  },
});
</script>

<style></style>

削除

pages/index.vue
<template>
  <div>
    <h1>ToDoリスト</h1>
    <br />
    <input type="radio" id="all" name="type" checked />
    <label for="all">すべて</label>
    <input type="radio" id="work" name="type" />
    <label for="work">作業中</label>
    <input type="radio" id="complete" name="type" />
    <label for="complete">完了</label>
    <br />

    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>コメント</th>
          <th>状態</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(task, index) in tasks" :key="index">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button>作業中</button></td>
          <td v-else><button>完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>
      </tbody>
    </table>
    <br />
    <h2>新規タスクの追加</h2>
    <input type="text" v-model="task" />
    <button @click="addTask()">追加</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';

type Tasks = {
  name: string;
  status: boolean;
};

export default defineComponent({
  setup() {
    // state
    const task = ref<string>('');
    const status = ref<boolean>(true); // true->作業中, false->完了
    const tasks = ref<Tasks[]>([]);

    // methods
    const addTask = (): void => {
      tasks.value.push({
        name: task.value,
        status: status.value,
      });
      task.value = '';
    };

    const deleteTask = (id: number): void => {
      tasks.value.splice(id, 1);
    };

    return {
      // state
      task,
      status,
      tasks,
      // methods
      addTask,
      deleteTask,
    };
  },
});
</script>

<style></style>

タスク状態変更

pages/index.vue
<template>
  <div>
    <h1>ToDoリスト</h1>
    <br />
    <input type="radio" id="all" name="type" checked />
    <label for="all">すべて</label>
    <input type="radio" id="work" name="type" />
    <label for="work">作業中</label>
    <input type="radio" id="complete" name="type" />
    <label for="complete">完了</label>
    <br />

    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>コメント</th>
          <th>状態</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(task, index) in tasks" :key="index">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>
      </tbody>
    </table>
    <br />
    <h2>新規タスクの追加</h2>
    <input type="text" v-model="task" />
    <button @click="addTask()">追加</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';

type Tasks = {
  name: string;
  status: boolean;
};

export default defineComponent({
  setup() {
    // state
    const task = ref<string>('');
    const status = ref<boolean>(true); // true->作業中, false->完了
    const tasks = ref<Tasks[]>([]);

    // methods
    const addTask = (): void => {
      tasks.value.push({
        name: task.value,
        status: status.value,
      });
      task.value = '';
    };

    const deleteTask = (id: number): void => {
      tasks.value.splice(id, 1);
    };

    const updateTask = (id: number): void => {
      tasks.value[id].status = !tasks.value[id].status;
    };

    return {
      // state
      task,
      status,
      tasks,
      // methods
      addTask,
      deleteTask,
      updateTask,
    };
  },
});
</script>

<style></style>

タスク表示切り替え

pages/index.vue
<template>
  <div>
    <h1>ToDoリスト</h1>
    <br />
    <input type="radio" id="all" name="type" value="すべて" v-model="radio" />
    <label for="all">すべて</label>
    <input type="radio" id="work" name="type" value="作業中" v-model="radio" />
    <label for="work">作業中</label>
    <input type="radio" id="complete" name="type" value="完了" v-model="radio" />
    <label for="complete">完了</label>
    <br />

    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>コメント</th>
          <th>状態</th>
        </tr>
      </thead>
      <tbody v-for="(task, index) in tasks" :key="index">
        <tr v-if="radio === 'すべて'">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>

        <tr v-else-if="radio === '作業中' && task.status === true">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>

        <tr v-else-if="radio === '完了' && task.status === false">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>
      </tbody>
    </table>
    <br />
    <h2>新規タスクの追加</h2>
    <input type="text" v-model="task" />
    <button @click="addTask()">追加</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';

type Tasks = {
  name: string;
  status: boolean;
};

export default defineComponent({
  setup() {
    // state
    const task = ref<string>('');
    const status = ref<boolean>(true); // true->作業中, false->完了
    const tasks = ref<Tasks[]>([]);
    const radio = ref<string>('すべて');

    // methods
    const addTask = (): void => {
      tasks.value.push({
        name: task.value,
        status: status.value,
      });
      task.value = '';
    };

    const deleteTask = (id: number): void => {
      tasks.value.splice(id, 1);
    };

    const updateTask = (id: number): void => {
      tasks.value[id].status = !tasks.value[id].status;
    };

    return {
      // state
      task,
      status,
      tasks,
      radio,
      // methods
      addTask,
      deleteTask,
      updateTask,
    };
  },
});
</script>

<style></style>

コードの問題点

コンポーネント内に、「View状態管理ロジック」が混在していることです。

上記のToDoであれば、100行も満たないコードなので問題ないですが、
大規模なコードであれば可読性が下がったりメンテナンスしにくいなどの問題が発生します。
(ちなみに、上記のようなToDoの場合、v2.xの方が書きやすいですw)

それでは、コンポーネントから、「View」と「状態管理+ロジック」に切り分けましょう!

コンポーネントから「状態管理+ロジック」を切り分ける

新たなフォルダを作り、そこに状態管理とロジックを定義しましょう。

ターミナル
mkdir composables
mkdir composables/store
touch composables/store/taskLogic.ts

これでフォルダとファイルの作成が完了しました。
それでは、こちらのファイル内に移していきましょう!

composables/store/taskLogic.ts
import { ref } from '@vue/composition-api';

type Tasks = {
  name: string;
  status: boolean;
};

export default function taskLogic() {
  // state
  const task = ref<string>('');
  const status = ref<boolean>(true); // true->作業中, false->完了
  const tasks = ref<Tasks[]>([]);
  const radio = ref<string>('すべて');

  // methods
  const addTask = (): void => {
    tasks.value.push({
      name: task.value,
      status: status.value,
    });
    task.value = '';
  };

  const deleteTask = (id: number): void => {
    tasks.value.splice(id, 1);
  };

  const updateTask = (id: number): void => {
    tasks.value[id].status = !tasks.value[id].status;
  };

  return {
    // state
    task,
    status,
    tasks,
    radio,
    // methods
    addTask,
    deleteTask,
    updateTask,
  };
}

コンポーネント側はimportして必要なものをreturnで返すだけです。

pages/index.vue
<template>
  <div>
    <h1>ToDoリスト</h1>
    <br />
    <input type="radio" id="all" name="type" value="すべて" v-model="radio" />
    <label for="all">すべて</label>
    <input type="radio" id="work" name="type" value="作業中" v-model="radio" />
    <label for="work">作業中</label>
    <input type="radio" id="complete" name="type" value="完了" v-model="radio" />
    <label for="complete">完了</label>
    <br />

    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>コメント</th>
          <th>状態</th>
        </tr>
      </thead>
      <tbody v-for="(task, index) in tasks" :key="index">
        <tr v-if="radio === 'すべて'">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>

        <tr v-else-if="radio === '作業中' && task.status === true">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>

        <tr v-else-if="radio === '完了' && task.status === false">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>
      </tbody>
    </table>
    <br />
    <h2>新規タスクの追加</h2>
    <input type="text" v-model="task" />
    <button @click="addTask()">追加</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';
// logic
import taskLogic from '@/composables/store/taskLogic';

export default defineComponent({
  setup() {
    const { task, status, tasks, radio, addTask, deleteTask, updateTask } = taskLogic();

    return {
      // state
      task,
      status,
      tasks,
      radio,
      // methods
      addTask,
      deleteTask,
      updateTask,
    };
  },
});
</script>

<style></style>

Provide/Injectでコンポーネント内で「状態管理+ロジック」を共有

①composablesにフォルダとファイルを作成

ターミナル
$ mkdir composables/key
$ touch composables/key/taskLogicKey.ts

②composables/key/taskLogicKey.tsにキーを作成する

composables/key/taskLogicKey.ts
import { InjectionKey } from '@vue/composition-api';
import { taskLogicStore } from '../store/taskLogic';

const taskLogicKey: InjectionKey<taskLogicStore> = Symbol('TaskLogicStore');

export default taskLogicKey;

③composables/store/taskLogic.tsで型情報を追加する

composables/store/taskLogic.ts
import { ref } from '@vue/composition-api';

type Tasks = {
  name: string;
  status: boolean;
};

export default function taskLogic() {
  // state
  const task = ref<string>('');
  const status = ref<boolean>(true); // true->作業中, false->完了
  const tasks = ref<Tasks[]>([]);
  const radio = ref<string>('すべて');

  // methods
  const addTask = (): void => {
    tasks.value.push({
      name: task.value,
      status: status.value,
    });
    task.value = '';
  };

  const deleteTask = (id: number): void => {
    tasks.value.splice(id, 1);
  };

  const updateTask = (id: number): void => {
    tasks.value[id].status = !tasks.value[id].status;
  };

  return {
    // state
    task,
    status,
    tasks,
    radio,
    // methods
    addTask,
    deleteTask,
    updateTask,
  };
}

// 型情報を追加する
export type taskLogicStore = ReturnType<typeof taskLogic>;

④親コンポーネントでProvide(=子コンポーネントに運ぶ)

pages/index.vue
<template>
  <div>
    <h1>ToDoリスト</h1>
    <br />
    <Radio />
    <br />
    <Table />
    <br />
    <h2>新規タスクの追加</h2>
    <Task />
  </div>
</template>

<script lang="ts">
import { defineComponent, provide } from '@vue/composition-api';
// logic
import taskLogicStore from '@/composables/store/taskLogic';
// key
import taskLogicKey from '@/composables/key/taskLogicKey';
// components
import Radio from '@/components/Radio.vue';
import Table from '@/components/Table.vue';
import Task from '@/components/Task.vue';

export default defineComponent({
  components: { Radio, Table, Task },
  setup() {
    provide(taskLogicKey, taskLogicStore());
  },
});
</script>

<style></style>

⑤子コンポーネントでInject(=親コンポーネントから受け取る)

components/Radio.vue
<template>
  <div>
    <input type="radio" id="all" name="type" value="すべて" v-model="radio" />
    <label for="all">すべて</label>
    <input type="radio" id="work" name="type" value="作業中" v-model="radio" />
    <label for="work">作業中</label>
    <input type="radio" id="complete" name="type" value="完了" v-model="radio" />
    <label for="complete">完了</label>
  </div>
</template>

<script lang="ts">
import { defineComponent, inject } from '@vue/composition-api';
// logic
import taskLogicStore from '@/composables/store/taskLogic';
// key
import taskLogicKey from '@/composables/key/taskLogicKey';

export default defineComponent({
  setup() {
    const { radio } = inject(taskLogicKey) as taskLogicStore;

    return {
      // state
      radio,
    };
  },
});
</script>

<style></style>
components/Table.vue
<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>コメント</th>
          <th>状態</th>
        </tr>
      </thead>
      <tbody v-for="(task, index) in tasks" :key="index">
        <tr v-if="radio === 'すべて'">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>

        <tr v-else-if="radio === '作業中' && task.status === true">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>

        <tr v-else-if="radio === '完了' && task.status === false">
          <td>{{ index }}</td>
          <td>{{ task.name }}</td>
          <td v-if="task.status"><button @click="updateTask(index)">作業中</button></td>
          <td v-else><button @click="updateTask(index)">完了</button></td>
          <td><button @click="deleteTask(index)">削除</button></td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script lang="ts">
import { defineComponent, inject } from '@vue/composition-api';
// logic
import taskLogicStore from '@/composables/store/taskLogic';
// key
import taskLogicKey from '@/composables/key/taskLogicKey';

export default defineComponent({
  setup() {
    const { task, status, tasks, radio, deleteTask, updateTask } = inject(
      taskLogicKey
    ) as taskLogicStore;

    return {
      // state
      task,
      status,
      tasks,
      radio,
      // methods
      deleteTask,
      updateTask,
    };
  },
});
</script>

<style></style>
components/Task.vue
<template>
  <div>
    <input type="text" v-model="task" />
    <button @click="addTask()">追加</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, inject } from '@vue/composition-api';
// logic
import taskLogicStore from '@/composables/store/taskLogic';
// key
import taskLogicKey from '@/composables/key/taskLogicKey';

export default defineComponent({
  setup() {
    const { task, addTask } = inject(taskLogicKey) as taskLogicStore;

    return {
      // state
      task,
      // methods
      addTask,
    };
  },
});
</script>

<style></style>

これで完了です!

12
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?