Nuxt, ElementUI, Storybook 構成のVueプロジェクトの始め方

Nuxt, ElementUI, Storybook 構成で始めようとしたときに、StorybookにElementUIを適用させるのにちょっと手こずったので手順を残しておきます。

最終的なソースです。
[howdy39/nuxt-storybook-element-boilerplate: Nuxt, Storybook, ElementUI, Boilerplate](https://github.com/howdy39/nuxt-storybook-element-boilerplate)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/18cb082f-0428-dd67-f10c-639c3a40ab0b.png)


## Nuxtプロジェクトを element-ui で作成

```bash
npx create-nuxt-app nuxt-storybook-element-ui
```

※ elment-ui をチェックするのを忘れないように。
<img width="600px" src="https://qiita-image-store.s3.amazonaws.com/0/35595/5115ccc6-87ce-80f6-c4d6-674a2ba1b821.png">

```bash:コマンド実行
cd nuxt-storybook-element-ui
yarn lint --fix
yarn dev
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/f59aeb7e-f18c-2bd0-4f91-3e5e4222d77c.png)


## Nuxt環境でElementUIのタグが使えるかを確認

### components 直下に Buttons.vue を新規作成
```vue:components/Buttons.vue
<template>
  <el-row>
    <el-button>Default</el-button>
    <el-button type="primary">Primary</el-button>
    <el-button type="success">Success</el-button>
    <el-button type="info">Info</el-button>
    <el-button type="warning">Warning</el-button>
    <el-button type="danger">Danger</el-button>
  </el-row>
</template>
```

### pages/index.vue に 先程作ったButtonsタグを追加

```diff:pages/index.vue
<template>
  <section class="container">
  ...
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey">GitHub</a>
      </div>
+       <Buttons />
    </div>
  </section>
</template>

<script>
import Logo from '~/components/Logo.vue'
+ import Buttons from '~/components/Buttons.vue'

export default {
  components: {
-     Logo
+     Logo,
+     Buttons
  }
}
</script>
```

### Nuxtの画面を確認
ちゃんとElementUIが使えてますね。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/0dc5f12d-fb75-57ac-169e-e225daca0801.png)



## Storybookの追加と起動
こっからが本題です。先程のButtons.vueをStorybookで確認できるようにしていきます。

```bash:Storybookの追加と起動
npx -p @storybook/cli sb init
yarn storybook
```

### Storybookの画面を確認
デフォルトのコンポーネントが設定された画面が表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/a606c31e-3a18-ad5e-385b-41c9648722ed.png)


## ストーリーを追加する
Storybookの画面にButtons.vueを追加します。
`stories/index.stories.js`を開いて次のように編集します。

```diff
...
import MyButton from './MyButton'
import Welcome from './Welcome'

+ import Buttons from '~/components/Buttons'

+ storiesOf('Buttons', module).add('Buttons', () => ({
+   components: { Buttons },
+   template: '<Buttons />'
+ }))

storiesOf('Welcome', module).add('to Storybook', () => ({
  components: { Welcome },
  template: '<welcome :showApp="action" />',
  methods: { action: linkTo('Button') }
}))
...
```

### Storybookの画面を確認
しかしこの状態だとモジュールが見つからない旨のエラーになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/f12e5992-e323-966c-5c77-0dce901b8d75.png)


## webpack.config.jsを作成する
`.storybook/webpack.config.js`を作成し、次のような内容を入力します。
ここにwebpackの設定を書くことで自動でstorybookがwebpackを解釈してくれます。

```js:.storybook/webpack.config.js
const path = require("path");
const rootPath = path.resolve(__dirname, '../');

module.exports = {
  resolve: {
    extensions: ['.vue'],
    alias: {
      '~': rootPath,
    }
  }
};
```

### Storybookの画面を確認
この状態で先程のエラーが消えますが、ボタンになっていませんね。
また、コンソールに`<el-row>`や`<el-button>`が解釈できない旨のエラーが出ています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/426aaed9-e0ff-2bc8-5b8a-cd8aeb76759d.png)


## ElementをVueに取り込む
`.storybook/config.js` を開いてElementUIをVueに取り込むようにします。
`plugins/element-ui.js`の中を見ると「あぁそういうことね」ってなると思います。

```diff:.storybook/config.js
+ import ElementUI from '~/plugins/element-ui';
+ ElementUI();

import { configure } from '@storybook/vue';

// automatically import all files ending in *.stories.js
const req = require.context('../stories', true, /.stories.js$/);
function loadStories() {
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);
```

### Storybookの画面を確認
やっとボタンが表示され、エラーが消えましたね。
しかし色やサイズが効いていません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/4cad341d-fd42-8e40-f5dd-1cfd2e4bae08.png)


## ElementのCSSを設定する
```diff:.storybook/config.js
+ import '!style-loader!css-loader!element-ui/lib/theme-chalk/index.css';
import ElementUI from '~/plugins/element-ui';
ElementUI();
```

### Storybookの画面を確認
今度はwoffファイルが読み込めない旨のエラーがでます。
※`index.css`の中でwoffファイルが定義されているのでしょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/f7cd5192-cc5e-cda6-7c91-24a049744282.png)


## フォント系のファイルを取り込めるようにする
webpackの設定を変えてフォント系ファイルを取り込めるようにします。

```diff:.storybook/webpack.config.js
const path = require("path");
const rootPath = path.resolve(__dirname, '../');

module.exports = {
+   module: {
+     rules: [
+       {
+         test: /\.(otf|eot|svg|ttf|woff|woff2)(\?.+)?$/,
+         loader: 'url-loader'
+       }
+     ]
+   },
  resolve: {
    extensions: ['.vue'],
    alias: {
      '~': rootPath,
    }
  }
};
```

storybookを再起動します。

```bash
yarn storybook
```

### Storybookの画面を確認
やっと Element が適用されたコンポーネントがStorybookでも表示できました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/3e8445ab-a89a-da07-b403-20260fdbc563.png)


## 何をしていたのか
Nuxtが裏でやっていることと同じようなことをStorybookにもやってあげました。

`nuxt.config.js`を開くとElmentUI用の設定が次のように`css`と`plugins`にされています。
これと同じようなことをしていたわけです。

```js:nuxt.config.js
  /*
  ** Global CSS
  */
  css: ['element-ui/lib/theme-chalk/index.css'],

  /*
  ** Plugins to load before mounting the App
  */
  plugins: ['@/plugins/element-ui'],
```