Nuxt.js(TypeScript) × Vuetify × Storybookを使用して開発環境を再構築してみる
以前、Nuxtの開発環境構築の記事を書きました。
その後、Nuxt.jsやStorybookのバージョンが上がり設定方法が変わったので、改めて開発環境を記事にしてみようと思いました。
動作環境 - maxOS 10.15.4 - VSCode(Veturをインストール済み) - yarnインストール済み
構成は以下の通りです。(変更はありません)
- Nuxt.js
- TypeScript
- Vuetify
- ESLint
- Prettier
- Jest
- Storybook
今回はnpx
でなくyarn
を使います。
プロジェクトを作成
yarn create nuxt-app <アプリケーション名>
対話形式で答えていきます。 このようにしました。
$ yarn create nuxt-app nuxt-typesciprt-starter yarn create v1.21.1 [1/4] 🔍 Resolving packages... [2/4] 🚚 Fetching packages... [3/4] 🔗 Linking dependencies... [4/4] 🔨 Building fresh packages... success Installed "create-nuxt-app@2.15.0" with binaries: - create-nuxt-app create-nuxt-app v2.15.0 ✨ Generating Nuxt.js project in . ? Project name nuxt-typesciprt-starter ? Project description My world-class Nuxt.js project ? Author name ikkyu-3 ? Choose programming language TypeScript ? Choose the package manager Yarn ? Choose UI framework Vuetify.js ? Choose custom server framework None (Recommended) ? Choose the runtime for TypeScript @nuxt/typescript-runtime ? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)ESLint, Prettier, Lint staged files, St yleLint ? Choose test framework Jest ? Choose rendering mode Universal (SSR) ? Choose development tools jsconfig.json (Recommended for VS Code)
プロジェクトが作成されたら一度起動してみます。
$ cd nuxt-typescript-starter $ yarn dev
http://localhost:3000/
にアクセスしてページが表示されるか確認します。
簡単ですね🎉
TypeScript導入
プロジェクト作成時に使用言語をTypeScriptにしましたが、開発に必要なライブラリや設定を行います。
vue-property-decoratorを使用します。
$ yarn add vue-property-decorator
TypeScriptでvueファイルを読み込めるように設定
プロジェクトのrootディレクトリにshims-vue.d.ts
を作成します。
declare module '*.vue' { import Vue from 'vue' export default Vue }
vueファイルを変更
components/Logo.vue
にscriptタグを追加します。
<script lang="ts"> import { Component, Vue } from 'vue-property-decorator' @Component export default class Logo extends Vue {} </script>
次にcomponents/VuetifyLogo.vue
にscriptタグを追加します。
<script lang="ts"> import { Component, Vue } from 'vue-property-decorator' @Component export default class VuetifyLogo extends Vue {} </script>
次にpages/index.vue
のscriptタグを変更します。
<script lang="ts"> import { Component, Vue } from 'vue-property-decorator' import Logo from '~/components/Logo.vue' import VuetifyLogo from '~/components/VuetifyLogo.vue' @Component({ components: { Logo, VuetifyLogo } }) export default class Index extends Vue {} </script>
package.jsonを変更
scriptsのlintとlint-stagedを変更します。
{ "scripts": { "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .", // tsファイルを対象に追加します ... }, ... "lint-staged": { "*.{ts,js,vue}": "yarn lint", // tsファイルを対象に追加します ... } }
JestにTypeScriptを導入
yarn add -D @types/jest
次にtsconfig.json
を変更します。
{ ..., "allowJs": true, // 削除 ..., "types": [ ..., "@types/jest" ] }
allowJs1がtrueだとCannot write file ... because it would overwrite input file.
とエラーとなりテストが失敗するため削除します。
test/Logo.spec.js
をtest/Logo.spec.ts
に変更し、テストを実行してみます。
$ yarn test yarn run v1.21.1 $ jest ts-jest[versions] (WARN) Version 24.9.0 of jest installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=25.0. 0 <26.0.0). Please do not report issues in ts-jest if you are using unsupported versions. PASS test/Logo.spec.ts Logo ✓ is a Vue instance (8ms) ------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ------------------|----------|----------|----------|----------|-------------------| All files | 23.08 | 100 | 25 | 25 | | components | 50 | 100 | 50 | 50 | | Logo.vue | 100 | 100 | 100 | 100 | | VuetifyLogo.vue | 0 | 100 | 0 | 0 | 1,6,9 | pages | 0 | 100 | 0 | 0 | | index.vue | 0 | 100 | 0 | 0 | 1,66,67,68,76 | inspire.vue | 0 | 100 | 0 | 0 | 1 | ------------------|----------|----------|----------|----------|-------------------| Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 3.034s Ran all test suites. ✨ Done in 3.98s.
テストが通りました✌️
Vuetifyのコンポーネントを使用したvueファイルをテストする場合の設定
jestでTypeScriptを使用できるようになりましたが、vuetifyのコンポーネントをjestで使用できるようにユニットテスト — Vuetify.jsをみながら設定します。
test/setup.js
を作成します。
import Vue from 'vue' import Vuetify from 'vuetify' Vue.use(Vuetify)
次に、jest.config.js
に設定を追加します。
module.exports = { setupFilesAfterEnv: ['./test/setup.js'], // 追加 ... }
試しにtest/index.spec.ts
を作成します。
import Vuetify from 'vuetify' import { mount, createLocalVue, RouterLinkStub } from '@vue/test-utils' import Index from '@/pages/index.vue' const localVue = createLocalVue() describe('Index', () => { let vuetify: typeof Vuetify beforeEach(() => { vuetify = new Vuetify() }) test('is a Vue instance', () => { const wrapper = mount(Index, { vuetify, localVue, stubs: { NuxtLink: RouterLinkStub } }) expect(wrapper.isVueInstance()).toBeTruthy() }) })
テストを実行してみます。
$ yarn test yarn run v1.21.1 $ jest ts-jest[versions] (WARN) Version 24.9.0 of jest installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=25.0. 0 <26.0.0). Please do not report issues in ts-jest if you are using unsupported ver sions. ts-jest[versions] (WARN) Version 24.9.0 of jest installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=25.0. 0 <26.0.0). Please do not report issues in ts-jest if you are using unsupported ver sions. PASS test/Logo.spec.ts PASS test/index.spec.ts ------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ------------------|----------|----------|----------|----------|-------------------| All files | 92.31 | 100 | 75 | 91.67 | | components | 100 | 100 | 100 | 100 | | Logo.vue | 100 | 100 | 100 | 100 | | VuetifyLogo.vue | 100 | 100 | 100 | 100 | | pages | 85.71 | 100 | 50 | 83.33 | | index.vue | 100 | 100 | 100 | 100 | | inspire.vue | 0 | 100 | 0 | 0 | 1 | ------------------|----------|----------|----------|----------|-------------------| Test Suites: 2 passed, 2 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 3.503s Ran all test suites. ✨ Done in 4.47s.
できました👍
Storybookを導入
Storybook for Vueを見ながらStorybookを導入していきます。今回も自動セットアップで行います。
$ npx -p @storybook/cli sb init --type vue
実行すると必要なパッケージがインストールされ、.storybookとstoriesというディレクトリが作成されて、その中にいくつかのファイルが作成されます。
また、package.jsonもstorybook, build-storybookというscriptsが新たに追加されています。
staticディレクトリのファイルを使用できるように、scriptsを以下のように変更します。2
{ ... "scripts": { ... "storybook": "start-storybook -p 6006 -s ./static", ... }, ... }
一度Storybookを表示してみます。
$ yarn storybook
サーバが起動したらhttp://localhost:6006/にアクセスします。
表示できました👍
では次に、StorybookでVuetifyを使用できるようにします。
Vuetify対応
StorybookでVuetifyを使用するため、.storybook/preview.jsを追加します。
import { addDecorator } from '@storybook/vue' import 'vuetify/dist/vuetify.css' import Vue from 'vue' import Vuetify from 'vuetify' Vue.use(Vuetify) Vue.component('nuxt-link', { props: ['to'], methods: { log() { action('link target')(this.to) } }, template: '<a href="#" @click.prevent="log()"><slot>NuxtLink</slot></a>' }) const vuetifyConfig = new Vuetify({ icons: { iconfont: 'fa' }, theme: { dark: false } }) addDecorator(() => { return { vuetify: vuetifyConfig, template: '<v-app><div><story/></div></v-app>' } })
では次に、JavaScriptで書かれたStorybookをTypeScriptに置き換えます。
TypeScript対応
Storybook用のwebpackをセットアップ
TypeScriptで書かれたstoriesファイルを読み込むために、
.storybook/main.js
を以下のように変更します。3
const path = require('path'); module.exports = { stories: ['../**/*.stories.ts'], addons: ['@storybook/addon-actions', '@storybook/addon-links'], webpackFinal: async config => { config.module.rules.push({ test: /\.ts$/, exclude: /node_modules/, use: [ { loader: 'ts-loader', options: { appendTsSuffixTo: [/\.vue$/], transpileOnly: true } } ] }) config.resolve.extensions.push('.ts') const rootPath = path.resolve(__dirname, '..') config.resolve.alias['@'] = rootPath config.resolve.alias['~'] = rootPath return config } };
これで設定は終わりです。
TypeScriptでStoryを書いてみる
components配下のLogo.vueとVuetifyLogo.vueのStoryをCSF4で書いてみます。
components/index.stories.ts
を作成します。
(表示したいコンポーネントと同じディレクトリにStroyを作成するとメンテナンスが簡単になります。5)
※ storiesディレクトリは使用しないので、削除します。
import Logo from './Logo.vue' import VuetifyLogo from './VuetifyLogo.vue' export default { title: 'Components' } export const logo = () => ({ components: { Logo }, template: '<logo />' }) export const vuetifyLogo = () => ({ components: { VuetifyLogo }, template: '<vuetify-logo />' })
yarn storybook
でstorybookを起動します。
できました!
今回作成したプロジェクトファイルはこちらで確認できます。
以上です。
-
https://www.typescriptlang.org/docs/handbook/compiler-options.html↩
-
https://storybook.js.org/docs/basics/writing-stories/#story-file-location↩
-
https://storybook.js.org/docs/configurations/custom-webpack-config/↩
-
https://storybook.js.org/docs/formats/component-story-format/↩
-
https://storybook.js.org/docs/basics/writing-stories/#story-file-location↩