自分的Storybook + Vue環境の作成方法
こんにちは。Nonです。
今回は
でTipsだけまとめているStorybookの環境構築や設定について自分なりに試行錯誤したものを書こうと思います。
弊社
では、デザイナーとフロントの協業というのがまだまだ弱く、フロント開発におけるテンプレートといいますか、明確なフローというのが確立していません。私はバックエンドの人間なので、デザイナーとどのように仕事をしていくかなどもわからなかったので、色々調べていった結果Storybookというものを発見しました。
なのでここでの目標は
- デザイナーとコンポーネント単位のデザインレビューや、コードレビューを行うこと
- Nuxtのようなフレームワークに依存せず、コンポーネント単位での開発ができること
- 同様にNuxtのようなフレームワークに依存せず、コンポーネントをinstall or importできること
としています。
Storybook導入
storybookを使用するので、当然インストールしなければなりません。他のサイトやブログなどではNuxt + storybook のような構成を取っていますが、Nuxtに依存したくないと思いましたので、このようなフレームワークの導入は検討していません。
しかし、Vue.jsには依存しています。
また、storybook-cliも利用していません。使った方が簡単ですが、最小構成にしたい and cliのinstallしたくない などの理由があります。そのうちCLI版の記事も書くかも。
storybook インストール
npm i @storybook/vue --save-dev
コマンドでstorybookを起動できるようにpackage.jsonに下記を追加。
この時点ではstorybookの設定をしていないので起動できません。
"scripts": {
"storybook": "start-storybook",
"build-storybook": "build-storybook",
},
Vue インストール
npm i vue --save-dev
Vue loader インストール
npm i vue-loader vue-template-compiler --save-dev
eslint インストール
npm i eslint eslint-loader eslint-plugin-vue --save-dev
linterの設定はプロダクトに合わせて設定してください。
コマンドでlinterを起動できるように、package.jsonに下記を追加
"scripts": {
"eslint": "eslint --ext .js,.vue ./",
"eslint-fix": "eslint --fix --ext .js,.vue ./"
},
Storybook設定
{project_root}/.storybook/main.js
に下記を追加
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
};
Storybook用のjsの場所はプロジェクト毎に決めていいと思います。
コンポーネント追加
{project_root}/src/components/atoms/Button/BaseButton.vue
を配置。
<template>
<button>button</button>
</template>
<script>
export default {
name: 'BaseButton'
};
</script>
<style scoped>
</style>
{project_root}/src/components/atoms/Button/Button.stories.js
を配置。
import {storiesOf} from '@storybook/vue';
import BaseButton from './BaseButton';
storiesOf('Button', module)
.add('buttons', () => ({
components: {BaseButton},
template: '<div>' +
'<base-button></base-button>' +
'</div>',
}));
外部でパッケージ利用できるように設定する
npm i
でパッケージインストールしたときに使用できるようにコンポーネントを登録するスクリプトを記載。
これに関しては色々なリリース方法があるので、用途に応じて変更して下さい。(CDNとかplane jsとか)
詳細はこちら。
import BaseButton from './src/components/atoms/Button/BaseButton';
const Components = {
BaseButton,
};
export function install(Vue) {
if (install.installed) return;
install.installed = true;
Object.keys(Components).forEach((component) => {
Vue.component(component, Components[component]);
});
}
const plugin = {
install,
};
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
export default Components;
css / scss / sass 関連
2021/02現在、最新のwebpackでsass-loaderを動かすとバージョンの整合性チェックでエラーが発生するので、10系
の最新バージョンを指定してインストールする。
npm i style-loader css-loader sass-loader@10.1.1 --save-dev
{project_root}/.storybook/main.js
に下記を追加
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-knobs',
'@storybook/addon-storysource',
'@storybook/addon-viewport',
'storybook-readme',
],
webpackFinal: (config) => {
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
});
return config;
}
};
npm run storybook
※↓エラーが発生する場合あり。
Node Sass does not yet support your current environment: OS X 64-bit with Unsupported runtime (88)
For more information on which environments are supported please see:
https://github.com/sass/node-sass/releases/tag/v4.13.1
storybookを起動しようとしてエラーが出た場合、node-sassをインストールしてビルドしておくこと。
npm i node-sass --save-dev
npm rebuild node-sass
npm run storybook
試しにscssファイルなどを作成してimportしてみる。
VueのScoped CSSで試しても大丈夫です。
{project_root}/src/sass/atoms/_button.scss
.btn {
color: red;
}
{project_root}/src/sass/app.scss
// My atom's scss.
@import 'atoms/button';
{project_root}/.storybook/preview.js
でscssファイルをimportする
import {addDecorator, addParameters} from "@storybook/vue";
import {INITIAL_VIEWPORTS} from "@storybook/addon-viewport";
import {addReadme} from 'storybook-readme/vue';
import '../src/sass/app.scss';
addParameters({
viewport: {
viewports: INITIAL_VIEWPORTS
}
});
addDecorator(addReadme);
アドオンを追加
storybookを使うにあたって便利なアドオンを追加します。これも各プロダクトによって導入するかどうかを決めて大丈夫です。
@storybook/addon-actions
npm i --save-dev @storybook/addon-actions
{project_root}/.storybook/main.js
にアドオン登録
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
addons: [
'@storybook/addon-actions',
]
};
{project_root}/src/components/atoms/Button/BaseButton.vue
にクリックイベント設置。
<template>
<button @click="$emit('click')">
button
</button>
</template>
{project_root}/src/components/atoms/Button/Button.stories.js
を書き換え。
import {storiesOf} from '@storybook/vue';
import BaseButton from './BaseButton';
import {action} from '@storybook/addon-actions';
storiesOf('Button', module)
.add('buttons', () => ({
components: {BaseButton},
template: '<div>' +
'<base-button @click="action"></base-button>' +
'</div>',
methods: {
action: action('clicked')
}
}));
@storybook/addon-knobs
npm i @storybook/addon-knobs --save-dev
{project_root}/.storybook/main.js
にアドオン登録
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-knobs',
]
};
{project_root}/src/components/atoms/Button/Button.stories.js
を書き換え。
import {storiesOf} from '@storybook/vue';
import BaseButton from './BaseButton';
import {action} from '@storybook/addon-actions';
import {text} from '@storybook/addon-knobs';
storiesOf('Button', module)
.add('buttons', () => ({
components: {BaseButton},
props: {
buttonLabel: {
default: text('label', 'button')
},
},
template: '<div>' +
'<base-button @click="action">{{ buttonLabel }}</base-button>' +
'</div>',
methods: {
action: action('clicked')
}
}));
{project_root}/src/components/atoms/Button/BaseButton.vue
を書き換え。
<template>
<button>button</button>
</template>
<script>
export default {
name: 'BaseButton'
};
</script>
<style scoped>
</style>
@storybook/addon-storysource
npm i @storybook/addon-storysource --save-dev
{project_root}/.storybook/main.js
にアドオン登録
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-knobs',
'@storybook/addon-storysource',
]
};
@storybook/addon-viewport
npm i @storybook/addon-viewport --save-dev
{project_root}/.storybook/main.js
にアドオン登録
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-knobs',
'@storybook/addon-storysource',
'@storybook/addon-viewport',
]
};
{project_root}/.storybook/preview.js
にviewport(iPhoneなどの画面情報)情報を追加。デフォルトでも使用可能。
import {addParameters} from "@storybook/vue";
import {INITIAL_VIEWPORTS} from "@storybook/addon-viewport";
addParameters({
viewport: {
viewports: INITIAL_VIEWPORTS
}
});
storybook-readme
npm i storybook-readme --save-dev
{project_root}/.storybook/main.js
にアドオン登録
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-knobs',
'@storybook/addon-storysource',
'@storybook/addon-viewport',
'storybook-readme',
]
};
{project_root}/.storybook/preview.js
に設定を追加。
import {addDecorator, addParameters} from "@storybook/vue";
import {INITIAL_VIEWPORTS} from "@storybook/addon-viewport";
import {addReadme} from 'storybook-readme/vue';
addParameters({
viewport: {
viewports: INITIAL_VIEWPORTS
}
});
addDecorator(addReadme);
{project_root}/src/components/atoms/Button/Button.stories.js
を書き換え。
import {storiesOf} from '@storybook/vue';
import ButtonReadme from './README.md';
import BaseButton from './BaseButton';
import {action} from '@storybook/addon-actions';
import {text} from '@storybook/addon-knobs';
storiesOf('Button', module)
.addParameters({
readme: {
sidebar: ButtonReadme
}
})
.add('buttons', () => ({
components: {BaseButton},
props: {
buttonLabel: {
default: text('label', 'button')
},
},
template: '<div>' +
'<base-button @click="action">{{ buttonLabel }}</base-button>' +
'</div>',
methods: {
action: action('clicked')
}
}));
{project_root}/src/components/atoms/Button/README.md
を追加。
# Usage🛠
その他設定はご自由に
- @babel/core
- vue-svg-loader
- @fortawesome/fontawesome-free
- bootstrap
のようなプロジェクトの要件などに関わるパッケージなどはご自由にインストールして下さい。
最後に
ほぼ、私のメモの用に書いてみました。
「こうしたほうがいいよ」とか、「ここ間違ってるくない?」とかあるかもしれませんが、その辺は自身の環境に合わせて設定を変えていただければと思います。
特にコンポーネントのパッケージ化についてはインストールする側がVueを利用していることを前提にしているので、使いにくいところがあるかもしれません。
CDN化やpure jsの対応はそのうちしておきたいなとは思っています。
個人的にはフレームワーク依存したくない(Vue / ReactのようなFWを除く)ので、それ単体で動く環境をCLIなどなしで構築できるようになっているはずです。
みなさんの役に立てれば幸いです。
React記事書こうとしていましたが、Storybookのまとめしてなかったなぁと思ったのでこちらを優先しました。Nuxtの案件も近いのでついでに...といった感じ。
Nuxtについてもそのうち書きたい。
そのときはよしなに。
.