はじめに
npm create vuetify@latest
を使わずにviteを利用してvue3 vuetifyの環境を作成し、StoryBookを追加します。
Vueプロジェクトの作成
1 vue projectの作成
$ npm create vite@latest
- Vue → TypeScript
VueとVuetifyの統合
1 vuetifyのinstall
『Vuetify公式』 既存プロジェクトへの導入
$ npm install vuetify
2 vite-plugin-vuetifyのinstall
『Vuetify公式』 TreeShaking(不要コードの削除)
『npm』vite-plugin-vuetify
$ npm i vite-plugin-vuetify
3 fileを変更
3-1 Vueアプリケーションを作成するファイルに以下を追加 (main.ts) (createAppするところ)
import { createApp } from 'vue'
// Vuetify
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
// Components
import App from './App.vue'
const vuetify = createVuetify()
createApp(App).use(vuetify).mount('#app')
3-2 vite.config.ts
import vue from '@vitejs/plugin-vue'
import vuetify from 'vite-plugin-vuetify'
export default {
plugins: [
vue(),
vuetify(), // vue()の後ろに設定すること
],
}
error解決
main.tsのimport 'vuetify/styles'
で『モジュール 'vuetify/styles' またはそれに対応する型宣言が見つかりません。』というerrorが発生する方がいるかもしれません。
副作用の発生するかもしれないimportを警告する機能がONになっているのが原因です
解決策1: インラインでnoUncheckedSideEffectImportsのルールを無効化する
// @ts-expect-error -- side effects import
import 'vuetify/styles'
解決策2: tsconfig.app.json
で"noUncheckedSideEffectImports": true
のルールを削除する
{
...
// 以下を消す
"noUncheckedSideEffectImports": true
...
}
4 お好みで
Iconを使う場合はinstall
Sass変数を利用してVuetifyのデフォルトスタイルをカスタマイズ
5 App.vueで動作確認
styleの当たったchipが表示されれば成功です
<v-chip>Chip Component</v-chip>
StoryBook
1 rootディレクトリで以下を実行 (自動で必要な初期設定をやってくれる)
『StoryBook公式』 StoryBook with Vite
$ npx storybook@latest init
2 vuetify componentを使えるように設定する
『StoryBook公式』 VuetifyとStorybookを統合する
2-1 StoryBookにVuetifyを登録
今回の作成方法だとregisterPlugins関数を使用していないので、公式ページの例と若干違います
// .storybook/preview.ts
// 追加
import { setup } from "@storybook/vue3";
import "vuetify/styles";
import { createVuetify } from "vuetify";
...
// 追加
setup((app) => {
const vuetify = createVuetify();
app.use(vuetify);
});
2-2 ストーリーラッパーコンポーネントの作成
storybookでvuetifyを扱うには<v-app></v-app>
でラップする必要があります。
<!-- .storybook/StoryWrapper.vue -->
<template>
<v-app>
<v-main>
<slot name="story"></slot>
</v-main>
</v-app>
</template>
// 公式では記載されていませんが、 setup lang="ts"を記述しないと後にimportした際にtsのerrorが表示されました
// 公式はtsではなくjsを前提に解説されています
<script setup lang="ts"></script>
2-3 .withVuetifyThemeデコレータを作成する
// .storybook/withVeutifyTheme.decorator.ts
import { h } from 'vue';
// ここでtsのerrorが出る場合はStoryWrapper.vueでsetupやlang="ts"を記述してみてください
import StoryWrapper from './StoryWrapper.vue';
export const withVuetifyTheme = (storyFn, context) => {
const story = storyFn();
return () => {
return h(
StoryWrapper,
{}, // Props for StoryWrapper
{
// Puts your story into StoryWrapper's "story" slot with your story args
story: () => h(story, { ...context.args }),
}
);
};
};
2-4 作成したでコレータをpreview.jsのStorybookに渡す
// 追加
import { withVuetifyTheme } from "./withVuetifyTheme.decorator";
// 追加
export const decorators = [withVuetifyTheme];
最終的なprevie.ts
初期設定で生成されたコードも含めると以下のようになります。
import type { Preview } from "@storybook/vue3";
// 以下importは全部追加したコード
import { setup } from "@storybook/vue3";
import "vuetify/styles";
import { createVuetify } from "vuetify";
import { withVuetifyTheme } from "./withVuetifyTheme.decorator";
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
// 追加したコード
setup((app) => {
const vuetify = createVuetify();
app.use(vuetify);
});
export default preview;
// 追加したコード
export const decorators = [withVuetifyTheme];
動作確認
1 適当にお好きなコンポーネントを作成
// src/components/BaseChip.vue
<script setup lang="ts">
withDefaults(
defineProps<{
color?: string;
closable?: true;
onClick?: () => void;
onClickClose?: () => void;
}>(),
{
color: "primary",
onClick: undefined,
onClickClose: undefined,
}
);
</script>
<template>
<v-chip
:color="color"
@onClick="onClick"
@click:close="onClickClose"
:closable="closable"
>
<slot></slot>
</v-chip>
</template>
2 簡易的ですが表示確認用のStoryを作成
// src/components/BaseChip.stories.ts
import type { Meta, StoryObj } from "@storybook/vue3";
import BaseChip from "./BaseChip.vue";
const meta: Meta<typeof BaseChip> = {
title: "BaseChip",
component: BaseChip,
argTypes: {
color: {
options: ["primary", "secondary", "success", "error", "warning"],
control: { type: "select" },
},
},
};
export default meta;
type Story = StoryObj<typeof BaseChip>;
export const Primary: Story = {
args: {
default: "IT事業促進部",
color: "primary",
},
};
おわりに
1 モチベーション
tsconfig.app.json
の初期設定はviteでの始め方によって設定がまちまちです。
当記事投稿時点ではnpm crreate vite@latest → vue → typescripで作成するとnoUncheckedSideEffectImports": true
が含まれており上記errorが発生します。
実際にはPrettier, EsLint, Vue-Routerを使いたいケースがほとんどだと思うので、Vue → Customize with create-vueで作成するとtsconfig.app.jsonにnoUncheckedSideEffectImports": true
が含まれませんので、当エラーは発生しません。 加えてvuetify公式で紹介されているnpm create vuetify@latest
を使った場合も発生しませんので、そちらの利用を検討するのも良いでしょう。 npm create vuetify@latest
を利用する場合、当記事で紹介した設定以外にも様々設定が加えられ、最適化されているようです。
当記事は学習目的でvue → typescripでプロジェクトを開始した際に上記でハマったのが投稿のモチベーションだったりします。
2 小話
Vue x StoryBookは環境構築が難しいと聞いたことがあるのですが、だいぶやりやすくなっているみたいです。先人達に感謝です。
参考
Vuetify
『Vuetify公式』プロジェクトへの導入
https://vuetifyjs.com/ja/getting-started/installation/#section-30a430f330b930c830fc30eb
『Vuetify公式』 不要コードの削除(Tree Shaking)
https://vuetifyjs.com/ja/features/treeshaking/
『NPM』 vite-plugin-vuetify
https://www.npmjs.com/package/vite-plugin-vuetify
StoryBook
『StoryBook公式』 VuetifyとStorybookを統合する
https://storybook.js.org/recipes/vuetify
『StoryBook公式』 StoryBook with Vite
https://storybook.js.org/docs/get-started/frameworks/vue3-vite?renderer=vue
『StoryBook公式』 StoryBook Component Vue
https://storybook.js.org/docs/get-started/frameworks/vue3-vite?renderer=vue