この備忘録の立ち上げでは、
npm init vite@latest
npm install
npm install -D tailwindcss postcss autoprefixer
にて必要なものを install。vue で tailwindcss を適用するのはこのサイトのままやれば問題なし。
//main.ts
import { createApp } from "vue";
import App from "./App.vue";
import "./index.css";
createApp(App).mount("#app");
シンプル。
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0) //count.valueでデータを使えるようになる。html内では{{count}}と記載すれば良い
</script>
//以下の書き方がしっくりくる。上記のジェネリクスで記載している書き方は不自然に感じた
const props = defineProps({
msg: {type: String,require: true}
});
見たことのないメソッドが存在した。
前はいか
props: {
msg: {
type: string,
require: true,
defaut: "",
},
},
data() {
return {
count:0
}
}
//nuxt ts && decorator
@Prop({ type: string, required: true , default: ""}) readonly msg!: string
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
import HelloWorld from "./components/HelloWorld.vue";
</script>
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
import するだけで components を使えるようになっている。
vue2 で記載されていたライフサイクルの書き方は、setup の関数を用いての書き方になった。具体的には以下の通り。
import { defineComponent } from "vue";
export default defineComponent({
emits: ["click"],
setup(props, context) {
// Attributes (Non-reactive object)
console.log(context.attrs);
// Slots (Non-reactive object)
console.log(context.slots);
// Emit Events (Method)
console.log(context.emit);
}
});
context オブジェクトには、emit がある。emit を呼び出したい時には、definecomponent 内で emit で使われる関数名を指定してから、使うことになる。また、vue3.2 系から、export default で setup 関数を使わない書き方になっているが、ここでは省略する。
store でよく使われるのは、readonly である。readonly の実態は変数の改変を防ぐためのものである。具体的にはいかが制限される。
//プロパティの代入は制限され、エラーとなる
let obj: { readonly x: number } = { x: 1 };
obj.x = 2;
//一方で、プロパティの代入は問題ない
let obj: { readonly x: number } = { x: 1 };
obj = { x: 2 };
//実用的に
export interface TodoStore {
state: DeepReadonly<TodoState>
addTodo: (todo: Params) => void
}
const state = reactive<TodoState>({
todos: []
})
const todoStore: TodoStore = {
state: readonly(state),
addTodo
}
という感じで、state の中身の todos のプロパティを改変しないように使われる。state.todos = aaaみたいなことはできなくなる。また、readonly のインタフェース的な役割??となっているのが DeepReadonly だと思う。
Vue3 系や CompositionAPI からは data()が廃止される代わりに監視したい値を ref や reactive を使用することで値の変更を監視することができるようになりました。参考
//refの使い方
import { ref } from 'vue'
const conter = ref<number>(0)
counter.value++
//reactiveの使い方
import { reactive } from 'vue'
interface FormState {
email: string
password: string
}
const formState = reactive<FormState>({
email: '',
password: ''
})
//stateではreactiveを使う
const state = reactive<TodoState>({
todos: []
})
ref と reactive の使い分けは複数であるかどうか。ref は counter のように単一の変数を監視したい時に使う。一方で、reactive はオブジェクトのような変数を監視したい時に使う。
まず vuex を使うことは推奨されていないように思える。ここでは、その代わりとなる provie/inject を勉強したので記載する。

簡単にいうと親コンポーネントで provide を宣言した、store オブジェクトを子コンポーネントで使えるようにするものである。
//src/store/todo.ts
export default todoStore
export const todoKey:InjectionKey<TodoStore> = Symbol('todo')
//src/App.vue
<script lang="ts">
import { defineComponent, provide } from 'vue'
import TodoStore, { todoKey } from '../store/todo/index'
export default defineComponent({
name: 'App',
setup () {
//イメージとしては、keyを設定してそのkeyを元にstoreの情報を取り出すイメージである
provide(todoKey, TodoStore)
}
})
</script>
//src/TodoList.vue
import { inject } from '@vue/runtime-core'
import { todoKey } from '../../store/todo/index'
export default defineComponent({
setup () {
const todoStore = inject(todoKey)
if (!todoStore) {
throw new Error('todoStore is not provided')
}
}})
provide(todoKey, TodoStore)にて Key で指定した Value を呼び出せる。その子コンポーネントに相当する TodoList にて key をもとにそのコンポーネントに inject してやることで、store で宣言した、オブジェクトを使うことができるようになっている。 なので、これまで親コンポーネントで取り出していた store の値を props に渡す、という作業をとる必要がなくなった。良いところはこれまでは props の値を渡すようなことが必要となっていたが、inject に対して mock すればよく todoStore の受け取るものを記載しやすくなった。
emits: ['clickDelete']のように明示的に emits を指定する必要があるようになった。emit は子コンポーネントが親コンポーネントに対して値を渡すような時に記載する方法である。
<script lang="ts">
import { defineComponent, PropType } from '@vue/runtime-core'
import { Todo } from '../../types/types'
export default defineComponent({
props: {
todo: {
type: Object as PropType<Todo>,//propのオブジェクトはPropTypeで宣言しておく必要がある
required: true
}
},
emits: ['clickDelete', 'clickTitle'],//emitに使われる関数を記載しておく必要がある。
setup (props, { emit }) {
const clickDate = () => {
emit('clickDelete', props.todo.id)
}
const clickTitle = () => {
emit('clickTitle', props.todo.id)
}
return {
clickDate,
clickTitle
}
}
})
</script>
//親コンポーネント
<TodoItem v-for="todo in todoStore.state.todos"
:key="todo.id"
:todo="todo"
@click-delete="clickDelete"
@click-title="clickTitle"
/>
という感じに記載する必要がある。
非同期処理を書く際は Suspense で囲まないといけない。これが気持ち悪い、と個人的には思っているのだが、suspense で囲みデータが呼び出されるのをまつ
<div v-if="error">
{{ error.message }}
</div>
<Suspense v-else>
<template #default>
<AsyncTodos />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
上記のように、AsyncTodos で非同期処理を書くことを念頭に考えると、Suspense で囲むようにしないといけない。多分。
最後にこれが最大に参考になった。参考