Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Vue.js 状態管理の選択肢 - そのVuex本当に必要ですか - / Vue.js Sta...
Search
ryo
March 17, 2021
Technology
4.8k
7
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Vue.js 状態管理の選択肢 - そのVuex本当に必要ですか - / Vue.js State Management Options
iCARE Dev Meetup #19 2021/03/17
ryo
March 17, 2021
More Decks by ryo
See All by ryo
Bridging Social Graphs
kawamataryo
0
280
Raycast AI APIを使ってちょっと便利なAI拡張機能を作ってみた
kawamataryo
1
570
退屈なことはDevinにやらせよう〜〜Devin APIを使ったVisual Regression Testの自動追加〜
kawamataryo
5
2.3k
SaaS公式MCPサーバーをリリースして得た学び
kawamataryo
7
2.2k
Raycast AI APIを使ってちょっと便利な拡張機能を作ってみた / created-a-handy-extension-using-the-raycast-ai-api
kawamataryo
1
870
ts-morphのパフォーマンス改善Tips
kawamataryo
0
130
webpack to Rspack
kawamataryo
0
120
GitHub Actions と Datadog でコードベースの定点観測
kawamataryo
7
2.1k
個人開発駆動学習 / personal development driven learning
kawamataryo
1
310
Other Decks in Technology
See All in Technology
AI時代の EM への処方箋
staka121
PRO
0
150
実装だけじゃない! CCA-F取得エンジニアが教えるClaude Code開発プロセス活用術
diggymo
2
720
Compose 新機能総まとめ / What's New in Jetpack Compose
yanzm
0
250
Claude Code公式skillで 自分の仕事を少しずつ手放そう!(Claude Code開発ノウハウ大公開スペシャル by クラスメソッド)
kaym
1
340
End-to-Endで考える信頼性 —LINEアプリにおけるクライアント開発×SRE連携の実践
maruloop
4
4.2k
[2026-07-15] AI Ready なはずだったアーキテクチャと、見えてきた課題・次に目指す状態
wxyzzz
8
3.6k
Keeping applications secure by evolving OAuth 2.0 and OpenID Connect
ahus1
PRO
1
160
Genie Ontologyは銀の弾丸かを考える / Is Genie Ontology a Silver Bullet?
nttcom
0
300
SRE Next 2026 何でも屋からの脱却
bto
0
720
人を動かすのは時間ではなく、納得感 〜新任EMが入社3ヶ月、組織を2回変えた話〜
kakehashi
PRO
3
220
シンガポールで登壇してきます
yama3133
0
130
穢れた技術選定について
watany
10
840
Featured
See All Featured
Game over? The fight for quality and originality in the time of robots
wayneb77
1
220
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
340
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
880
Visualization
eitanlees
152
17k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
460
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
180
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.4k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Rails Girls Zürich Keynote
gr2m
96
14k
Transcript
Vue.js ঢ়ଶཧͷબࢶ ʙ ͦͷVuexຊʹඞཁͰ͔͢? ʙ @KawamataRyo 2021/03/17 iCARE Dev Meetup
#19
ࣗݾհ
@KawamataRyo LAPRAS גࣜձࣾ TypeScript, Vue, Firebase, Ruby ݩফ࢜🔥🚒
ࠓ͢͜ͱ
Vuex + α ͷঢ়ଶཧख๏ͷհ ͦΕͧΕͷ Pros/Cons ͷ·ͱΊ
ͳͥঢ়ଶཧΛςʔϚʹʁ🤔
Vuex ݏΘΕ͗͢͡Όͳ͍͔..?
Vuex 😭 🐵 🐶 😼 🦁 🐯 🐻 🐹 🦊
ͦΕͬͯຊʹVuexͷͳͷͩΖ͏͔ʁ
VuexΛ దʹར༻Ͱ͖ͯͳ͍ or దͳॴͰར༻Ͱ͖ͯͳ͍ ߹͋Δͷ͔ʁ
VuexΛ దʹར༻Ͱ͖ͯͳ͍ or దͳॴͰར༻Ͱ͖ͯͳ͍ ߹͋Δͷ͔ʁ
Vuex Ҏ֎ͷঢ়ଶཧΛΔ͜ͱͰ ঢ়ଶཧͷબࢶΛ͍͛ͨ
ঢ়ଶཧͷجຊ
ͦͦঢ়ଶཧͱʁ
ΞϓϦ͕࣋ͭ ঢ়ଶʢσʔλʣΛదʹѻ ͍ΞϓϦΛਖ਼͘͠ಈ࡞ͤ͞ Δํ๏
ͳͥঢ়ଶཧϥΠϒϥϦ͕ඞཁʁ
ίϯϙʔωϯτ͕૿͑Δͱ Props/EmitͰͷঢ়ଶͷ ͕ෳࡶʹ..
ೝূใͳͲෳίϯϙʔωϯτ Ͱڞ௨ͷσʔλɾϩδοΫΛ࣋ͪ ͍ͨ..
ෳͷίϯϙʔωϯτ͔Β ΞΫηεͰ͖Δάϩʔόϧͳ ঢ়ଶ͕ඞཁ => ঢ়ଶཧϥΠϒϥϦ
ঢ়ଶཧͷબࢶ
Vuex Pinia Vue Apollo Composition API Store
Vuex Pinia Vue Apollo Composition API Store
- Vueެࣜͷঢ়ଶཧϥΠϒϥϦ - FluxϥΠΫ - ୯Ұ Store vuejs / vuex
(v4)
Store import { InjectionKey } from "vue"; import { createStore,
Store } from "vuex"; export interface State { count: number; } export const key: InjectionKey<Store<State>> = Symbol(); export const store = createStore<State>({ state: { count: 0 }, mutations: { increment(state) { state.count++; }, decrement(state) { state.count--; } }, actions: { increment({ commit }) { commit("increment"); }, decrement({ commit }) { commit("decrement"); } } });
Store import { InjectionKey } from "vue"; import { createStore,
Store } from "vuex"; export interface State { count: number; } export const key: InjectionKey<Store<State>> = Symbol(); export const store = createStore<State>({ state: { count: 0 }, mutations: { increment(state) { state.count++; }, decrement(state) { state.count--; } }, actions: { increment({ commit }) { commit("increment"); }, decrement({ commit }) { commit("decrement"); } } }); createStoreͰstoreΛఆٛ mutations, actionsͰstateΛૢ࡞
Store import { InjectionKey } from "vue"; import { createStore,
Store } from "vuex"; export interface State { count: number; } export const key: InjectionKey<Store<State>> = Symbol(); export const store = createStore<State>({ state: { count: 0 }, mutations: { increment(state) { state.count++; }, decrement(state) { state.count--; } }, actions: { increment({ commit }) { commit("increment"); }, decrement({ commit }) { commit("decrement"); } } }); provide/inject༻ͷΩʔ ʢTSͷܕఆٛऔಘͷͨΊʣ
Install import { createApp } from "vue"; import App from
"./App.vue"; import { store, key } from "./stores/vuex/store"; const app = createApp(App); // vuex app.use(store, key); app.mount("#app");
Install import { createApp } from "vue"; import App from
"./App.vue"; import { store, key } from "./stores/vuex/store"; const app = createApp(App); // vuex app.use(store, key); app.mount("#app"); storeͱkeyΛొ
Components (read) <script lang="ts"> import { defineComponent, computed } from
"vue"; import { useStore } from "vuex"; import { key } from "@/stores/vuex/store"; export default defineComponent({ name: "Counter", setup() { const store = useStore(key); const count = computed(() => store.state.count); return { count }; } }); </script>
Components (read) <script lang="ts"> import { defineComponent, computed } from
"vue"; import { useStore } from "vuex"; import { key } from "@/stores/vuex/store"; export default defineComponent({ name: "Counter", setup() { const store = useStore(key); const count = computed(() => store.state.count); return { count }; } }); </script> keyΛͯ͠storeΛऔಘ countnumberܕͱͯ͠ਪ
<script lang="ts"> import { defineComponent } from "vue"; import {
useStore } from "vuex"; import { key } from "@/stores/vuex/store"; export default defineComponent({ name: "IncrementButton", setup() { const store = useStore(key); const increment = () => store.dispatch("increment"); return { increment }; } }); </script> Components (write)
<script lang="ts"> import { defineComponent } from "vue"; import {
useStore } from "vuex"; import { key } from "@/stores/vuex/store"; export default defineComponent({ name: "IncrementButton", setup() { const store = useStore(key); const increment = () => store.dispatch("increment"); return { increment }; } }); </script> Components (write) dispatchͰactionsΛ࣮ޮ ܕิޮ͔ͳ͍
- ެࣜϥΠϒϥϦ - υΩϡϝϯτɾࢀߟใͷ๛͞ - DevToolsͷ࿈ܞ - ߏԽ͞ΕͨύλʔϯʢFluxʣ - SSRରԠ
Pros 😁
- TypeScriptͷܕ͚ͷରԠ - هड़ྔͷଟ͞ Cons 😵
Vuex Pinia Vue Apollo Composition API Store
- Composition APIઐ༻ - ܰྔʢ1kbະຬʣ - ෳStore posva / pinia
Store import { defineStore } from "pinia"; export const useCounterStore
= defineStore({ id: "counter", state: () => ({ count: 0 }), actions: { increment() { this.count++; }, decrement() { this.count--; } } });
Store import { defineStore } from "pinia"; export const useCounterStore
= defineStore({ id: "counter", state: () => ({ count: 0 }), actions: { increment() { this.count++; }, decrement() { this.count--; } } }); mutationsͳ͘actionsͰ stateΛૢ࡞ storeݻ༗ͷ idΛ࣋ͭ
Install import { createApp } from "vue"; import App from
"./App.vue"; import { createPinia } from "pinia"; const app = createApp(App); // pinia app.use(createPinia()); app.mount("#app");
Install import { createApp } from "vue"; import App from
"./App.vue"; import { createPinia } from "pinia"; const app = createApp(App); // pinia app.use(createPinia()); app.mount("#app"); piniaͷrootετΞΛొ
Components (read) <script lang="ts"> import { defineComponent, computed } from
"vue"; import { useCounterStore } from "@/stores/pinia/store"; export default defineComponent({ name: "Counter", setup() { const store = useCounterStore(); const count = computed(() => store.count); return { count }; } }); </script>
Components (read) <script lang="ts"> import { defineComponent, computed } from
"vue"; import { useCounterStore } from "@/stores/pinia/store"; export default defineComponent({ name: "Counter", setup() { const store = useCounterStore(); const count = computed(() => store.count); return { count }; } }); </script> numberܕͱͯ͠ਪ exportͨ͠StoreΛར༻
<script lang="ts"> import { defineComponent } from "vue"; import {
useCounterStore } from "@/stores/pinia/store"; export default defineComponent({ name: "IncrementButton", setup() { const store = useCounterStore(); const increment = () => store.increment(); return { increment }; } }); </script> Components (write)
<script lang="ts"> import { defineComponent } from "vue"; import {
useCounterStore } from "@/stores/pinia/store"; export default defineComponent({ name: "IncrementButton", setup() { const store = useCounterStore(); const increment = () => store.increment(); return { increment }; } }); </script> Components (write) actionsͷ ܕิ͕ޮ͘
Demo https://vue-state-management-samples.vercel.app/pinia
- TypeScript શରԠ - DevToolsͷ࿈ܞʢݱঢ়ࢀরͷΈʣ - γϯϓϧͳίʔυ - SSRରԠ Pros
😁
- ใྔͷগͳ͞ - ਖ਼ࣜϦϦʔεલ - ։ൃͷܧଓੑ Cons 😵
Vuex Pinia Composition API Store vue-apollo
- Composition APIͰ ಠ࣮ࣗͨ͠Store Composition Store
Store import { reactive, provide, InjectionKey, toRefs } from "vue";
import { DeepReadonly } from "utility-types"; export const createStore = () => { const state = reactive({ count: 0 }); const actions = { increment: () => { state.count++; }, decrement: () => { state.count--; } }; return { state: toRefs(state), actions }; }; type Store = ReturnType<DeepReadonly<typeof createStore>>; export const STORE_KEY: InjectionKey<Store> = Symbol("Store"); export const useStore = () => { return inject(STORE_KEY) as Store; };
Store import { reactive, provide, InjectionKey, toRefs } from "vue";
import { DeepReadonly } from "utility-types"; export const createStore = () => { const state = reactive({ count: 0 }); const actions = { increment: () => { state.count++; }, decrement: () => { state.count--; } }; return { state: toRefs(state), actions }; }; type Store = ReturnType<DeepReadonly<typeof createStore>>; export const STORE_KEY: InjectionKey<Store> = Symbol("Store"); export const useStore = () => { return inject(STORE_KEY) as Store; }; reactiveͰstateΛఆٛ ؔͰstateΛૢ࡞
Store import { reactive, provide, InjectionKey, toRefs } from "vue";
import { DeepReadonly } from "utility-types"; export const createStore = () => { const state = reactive({ count: 0 }); const actions = { increment: () => { state.count++; }, decrement: () => { state.count--; } }; return { state: toRefs(state), actions }; }; type Store = DeepReadonly<ReturnType<typeof createStore>>; export const STORE_KEY: InjectionKey<Store> = Symbol("Store"); export const useStore = () => { return inject(STORE_KEY) as Store; }; provide/inject༻ͷ ؔɾΩʔ DeepReadolyͰ stateͷॻ͖͑Λېࢭ্ͨ͠Ͱ InjectionKeyͷܕʹઃఆ
Install import { createApp, provide } from "vue"; import App
from "./App.vue"; import { STORE_KEY, createStore } from "@/stores/originalStore/store"; const app = createApp(App); // Original Store app.provide(STORE_KEY, createStore()); app.mount("#app");
import { createApp, provide } from "vue"; import App from
"./App.vue"; import { STORE_KEY, createStore } from "@/stores/originalStore/store"; const app = createApp(App); // Original Store app.provide(STORE_KEY, createStore()); app.mount("#app"); Install Storeͷ࡞ͱprovideͰͷొ
Components (read) <script lang="ts"> import { defineComponent, computed } from
"vue"; import { useStore } from "@/stores/originalStore/store"; export default defineComponent({ name: "Counter", setup() { const { state } = useStore(); const count = computed(() => state.count.value); return { count }; } }); </script>
Components (read) <script lang="ts"> import { defineComponent, computed } from
"vue"; import { useStore } from "@/stores/originalStore/store"; export default defineComponent({ name: "Counter", setup() { const { state } = useStore(); const count = computed(() => state.count.value); return { count }; } }); </script> injectͰͷstoreͷऔಘ numberܕͱͯ͠ਪ
<script lang="ts"> import { defineComponent } from "vue"; import {
useStore } from "@/stores/originalStore/store"; export default defineComponent({ name: "IncrementButton", setup() { const { actions } = useStore(); const increment = () => actions.increment(); return { increment }; } }); </script> Components (write)
<script lang="ts"> import { defineComponent } from "vue"; import {
useStore } from "@/stores/originalStore/store"; export default defineComponent({ name: "IncrementButton", setup() { const { actions } = useStore(); const increment = () => actions.increment(); return { increment }; } }); </script> Components (write) ୯७ͳؔݺͼग़͠ ͳͷͰܕิ͕ޮ͘
Demo https://vue-state-management-samples.vercel.app/original-store
- TypeScript શରԠ - γϯϓϧͳίʔυ - ґଘͳ͠ Pros 😁
- ΦϨΦϨStoreͷ - DevTools ࿈ܞ - vue-routerɺSSRͷରԠ Cons 😵
Vuex Pinia Vue Apollo Composition API Store
- Apollo CacheΛStoreʹ - Query / MutationͰͷૢ࡞ vuejs / vue-apollo
GraphQLͷεΩʔϚఆٛ import gql from "graphql-tag"; export const typeDefs = gql`
extend type Store { count: Int! } extend type Mutation { increment: Int decrement: Int } extend type Query { CountQuery: Store } `;
Queryͷఆٛ import gql from "graphql-tag"; export const COUNT_QUERY = gql`
query CountQuery { store @client { count } } `;
Queryͷఆٛ import gql from "graphql-tag"; export const COUNT_QUERY = gql`
query CountQuery { store @client { count } } `; @clientͱσΟϨΫςΟϒΛ͚ͭΔ͜ͱͰɺ αʔόʔʹ͍߹ΘͤͣɺΩϟογϡͰॲཧ͢Δ
import gql from "graphql-tag"; export const INCREMENT_MUTATION = gql` mutation
incrementMutation { increment @client } `; export const DECREMENT_MUTATION = gql` mutation decrementMutation { decrement @client } `; Mutationͷఆٛ
Resolvers import { InMemoryCache } from "apollo-cache-inmemory"; import { COUNT_QUERY
} from "@/stores/apolloClient/queries"; export const resolvers = { Mutation: { increment: ( _: unknown, _arg: unknown, { cache }: { cache: InMemoryCache }) => { const data = cache.readQuery<any>({ query: COUNT_QUERY }); data.store.count++; cache.writeQuery({ query: COUNT_QUERY, data }); return data.store.count; }, decrement: ( _: unknown, _arg: unknown, { cache }: { cache: InMemoryCache }) => { const data = cache.readQuery<any>({ query: COUNT_QUERY }); data.store.count--; cache.writeQuery({ query: COUNT_QUERY, data }); return data.store.count; } } };
Resolvers import { InMemoryCache } from "apollo-cache-inmemory"; import { COUNT_QUERY
} from "@/stores/apolloClient/queries"; export const resolvers = { Mutation: { increment: ( _: unknown, _arg: unknown, { cache }: { cache: InMemoryCache }) => { const data = cache.readQuery<any>({ query: COUNT_QUERY }); data.store.count++; cache.writeQuery({ query: COUNT_QUERY, data }); return data.store.count; }, decrement: ( _: unknown, _arg: unknown, { cache }: { cache: InMemoryCache }) => { const data = cache.readQuery<any>({ query: COUNT_QUERY }); data.store.count--; cache.writeQuery({ query: COUNT_QUERY, data }); return data.store.count; } } }; queryͷcacheΛॻ͖͍͑ͯΔ
Apollo Clientͷ࡞ import ApolloClient from "apollo-boost"; import { typeDefs }
from "@/stores/apolloClient/typeDefs"; import { InMemoryCache } from "apollo-cache-inmemory"; import { resolvers } from "@/stores/apolloClient/resolvers"; const cache = new InMemoryCache(); export const apolloClient = new ApolloClient({ cache, typeDefs, resolvers }); // storeͷॳظԽ cache.writeData({ data: { store: { __typename: "Store", count: 0 } } });
Apollo Clientͷ࡞ import ApolloClient from "apollo-boost"; import { typeDefs }
from "@/stores/apolloClient/typeDefs"; import { InMemoryCache } from "apollo-cache-inmemory"; import { resolvers } from "@/stores/apolloClient/resolvers"; const cache = new InMemoryCache(); export const apolloClient = new ApolloClient({ cache, typeDefs, resolvers }); // storeͷॳظԽ cache.writeData({ data: { store: { __typename: "Store", count: 0 } } }); cacheͷॳظԽॲཧ
Install import { createApp } from "vue"; import App from
"./App.vue"; import { DefaultApolloClient } from "@vue/apollo-composable"; import { apolloClient } from "@/stores/apolloClient/apolloClient"; const app = createApp(App); // vue-apollo app.provide(DefaultApolloClient, apolloClient); app.mount("#app");
Install import { createApp } from "vue"; import App from
"./App.vue"; import { DefaultApolloClient } from "@vue/apollo-composable"; import { apolloClient } from "@/stores/apolloClient/apolloClient"; const app = createApp(App); // vue-apollo app.provide(DefaultApolloClient, apolloClient); app.mount("#app"); Apollo ClientΛprovide
Components (read) <script lang="ts"> import { defineComponent } from "vue";
import { useQuery, useResult } from "@vue/apollo-composable"; import { COUNT_QUERY } from "@/stores/apolloClient/queries"; export default defineComponent({ name: "Counter", setup() { const { result } = useQuery(COUNT_QUERY); const count = useResult(result, 0, data => data.store.count); return { count }; } }); </script>
Components (read) <script lang="ts"> import { defineComponent } from "vue";
import { useQuery, useResult } from "@vue/apollo-composable"; import { COUNT_QUERY } from "@/stores/apolloClient/queries"; export default defineComponent({ name: "Counter", setup() { const { result } = useQuery(COUNT_QUERY); const count = useResult(result, 0, data => data.store.count); return { count }; } }); </script> GraphQL QueryͰऔಘ
Components (write) <script lang="ts"> import { defineComponent } from "vue";
import { useMutation } from "@vue/apollo-composable"; import { INCREMENT_MUTATION } from "@/stores/apolloClient/mutations"; export default defineComponent({ name: "IncrementButton", setup() { const { mutate: increment } = useMutation(INCREMENT_MUTATION); return { increment }; } }); </script>
<script lang="ts"> import { defineComponent } from "vue"; import {
useMutation } from "@vue/apollo-composable"; import { INCREMENT_MUTATION } from "@/stores/apolloClient/mutations"; export default defineComponent({ name: "IncrementButton", setup() { const { mutate: increment } = useMutation(INCREMENT_MUTATION); return { increment }; } }); </script> Components (write) MutationͰॻ͖͑
Demo https://vue-state-management-samples.vercel.app/vue-apollo
- StoreΛ࣋ͨͳ͍ - DevToolsʢApolloʣରԠ Pros 😁
- Vue ApolloɺGraphQLͷґଘ - ঢ়ଶͷѲ͕ͮ͠Β͍ - ։ൃͷܧଓੑ - ίʔυྔͷ૿Ճ Cons
😵
·ͱΊ
7VFY 1JOJB $PNQPTJUJPO"1* 4UPSF 7VF"QPMMP 4UPSFͷ ୯Ұ ෳ ෳ ʢΩϟγϡʣ
%FW5PPMTରԠ ˓ ˚ º ˚ʢ"QPMMPʣ 5ZQF4DSJQUରԠ ˚ ˓ ˓ ˚ ଟػೳੑ ʢ443FUDʣ ˓ ˚ º º ίʔυͷهड़ྔ ˚ ˓ ˓ º
ࠓͷVueͷঢ়ଶཧVuex͚ͩͰͳ͍ɻ ΞϓϦέʔγϣϯͷنॏࢹ͢Δʹ ߹Θͤͯదͳঢ়ଶཧख๏Λબ ͢Δ͜ͱ͕େ
ࢀߟ • you might not need Vuex: https://speakerdeck.com/ntepluhina/you-might-not-need-vuex • Do
you really need Vuex: https://blog.logrocket.com/do-you-really-need-vuex • ΈΜͳͷVue.js: https://gihyo.jp/book/2021/978-4-297-11902-7 • Pinia vs Vuex: https://www.youtube.com/watch?v=ht_1NR7OFWc • intro to vuex: https://www.vuemastery.com/courses/mastering-vuex/intro-to-vuex/ • Pinia doc: https://pinia.esm.dev/ • Vuex4 doc: https://next.vuex.vuejs.org/
͓·͚ Vuex 5 RFCͷ·ͱΊ 2021/03/14 ࣌ https://github.com/kiaking/rfcs/blob/vuex-5/active-rfcs/0000-vuex-5.md
Storeͷఆٛ Storeͷར༻ Mutationsͷഇࢭ dispatchΛհͣ͞ actionsΛݺΔ ᶃ FluxϥΠΫύλʔϯΛഇࢭ
Optional Store Composition Store ΄΅Composition APIͦͷ·· ᶄ 2छྨͷStoreఆٛํ๏
Store 1 Store 2 ωετ͞ΕͨStoreʢModuleʣ Λഇࢭͯ͠ɺComposition Ͱ Storeͷ૬ޓར༻Λߦ͏ ᶅωετ͞ΕͨStoreΛഇࢭ
Store Component Optional Storeͷ߹ ܕΞϊςʔγϣϯΛ͚ͭΔ ʢComposition StoreͰෆཁʣ ༻࣌ ܕิ͕શʹޮ͘ ᶆ
શͳTypeScriptαϙʔτ