14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Riot.js + RiotControl で Flux を実装してみる - スタイルガイド編

Last updated at Posted at 2017-05-01

はじめに

以下の記事の スタイルガイド編 です。

Riot.js と RiotControl で Flux を実装してみる - Qiita

De Voorhoede 社チームのための Riot.js スタイルガイドに載っとり開発しました。スタイルガイドに載っとることでソースコードの一貫性や品質を保つことが出来たかなと思います。

実際に作成したプロジェクト ( 開発環境 ) は以下にリポジトリを置いています。

kotarella1110/riot-starter at v2.0.0

スタイルガイドに載っとる

RiotJS Style Guide badge

今回は 開発環境構築編 で作成したプロジェクトに修正を加えてスタイルガイドを反映させていきたいと思います。既にスタイルガイドが反映されている箇所もありますので、そこに関しての説明は省きます。

1モジュール = 1ディレクトリ

差分

1モジュール = 1ディレクトリにするために以下の構成から

src
.
├── .gitkeep 
├── index.html
├── index.js
├── todo.scss
└── todo.tag

以下の構成へ修正します。

src
.
├── index.html
├── index.js
└── todo
    ├── todo.scss
    └── todo.tag

src/index.js を以下のように修正します。

src/index.js
-import './todo.tag';
+import './todo/todo.tag';

*.tag.html を使う

差分

src/todo/todo.tag から src/todo/todo.tag.html のように変更し、タグの拡張子を修正します。

以下のように *.tag.html ファイルを babel-loaderriot-tag-loader が読み込めるように Webpack を修正します。

webpack/common.config.js
     module: {
       rules: [
         { // js
-          test: /\.(js|tag)$/,
+          test: /\.(js|tag|tag.html)$/,
           include: /src/,
           exclude: /node_modules/,
           enforce: 'post',
webpack/dev.config.js&prod.config.js
     module: {
       rules: [
         { // riot
-          test: /\.tag$/,
+          test: /\.(tag|tag.html)$/,
           include: /src/,
           exclude: /node_modules/,
           enforce: 'pre',

src/todo/todo.tag.htmlimport './todo/todo.tag' とすれば import できるように修正します。

webpack/common.config.js
       ]
     },
     resolve: {
-      extensions: ['.js'],
+      extensions: ['.js', '.html'],
     },
     plugins: [
       new webpack.ProvidePlugin({

タグオプションを設定する

差分

src/todo/todo.tag.html
 <todo>
 
-  <h3>{ opts.title }</h3>
+  <h3>{ title }</h3>
 
   <ul>
     <li each={ items.filter(whatShow) }>
...
   <!-- this script this is optional -->
   <script>
-    this.items = opts.items;
+    this.title = opts.title || '';
+    this.items = opts.items || [];
+    this.text = '';
 
     this.edit = (e) => {
       this.text = e.target.value;

thisをtagにアサインする

差分

src/todo/todo.tag.html
  <!-- this script this is optional -->
   <script>
-    this.title = opts.title || '';
-    this.items = opts.items || [];
-    this.text = '';
+    const tag = this;
 
-    this.edit = (e) => {
-      this.text = e.target.value;
+    tag.title = opts.title || '';
+    tag.items = opts.items || [];
+    tag.text = '';
+
+    tag.edit = (e) => {
+      tag.text = e.target.value;
     };
 
-    this.add = (e) => {
-      if (this.text) {
-        this.items.push({ title: this.text })
-        this.text = this.refs.input.value = '';
+    tag.add = (e) => {
+      if (tag.text) {
+        tag.items.push({ title: tag.text })
+        tag.text = tag.refs.input.value = '';
       }
       e.preventDefault();
     };
 
-    this.removeAllDone = (e) => {
-      this.items = this.items.filter((item) => {
+    tag.removeAllDone = (e) => {
+      tag.items = tag.items.filter((item) => {
         return !item.done;
       });
     };
 
     // an two example how to filter items on the list
-    this.whatShow = (item) => {
+    tag.whatShow = (item) => {
       return !item.hidden;
     };
 
-    this.onlyDone = (item) => {
+    tag.onlyDone = (item) => {
       return item.done;
     };
 
-    this.toggle = (e) => {
+    tag.toggle = (e) => {
       const item = e.item;
       item.done = !item.done;
       return true;

each ... inシンタックスを使う

差分

src/todo/todo.tag.html
  <h3>{ title }</h3>
 
   <ul>
-    <li each={ items.filter(whatShow) }>
-      <label class={ completed: done }>
-        <input type="checkbox" checked={ done } onclick={ parent.toggle }> { title }
+    <li each={ item in items.filter(whatShow) }>
+      <label class={ completed: item.done }>
+        <input type="checkbox" checked={ item.done } onclick={ parent.toggle }> { item.title }
       </label>
     </li>
   </ul>
...
     };
 
     tag.toggle = (e) => {
-      const item = e.item;
+      const item = e.item.item;
       item.done = !item.done;
       return true;
     };

タグ名をスタイルスコープとして使う

差分

以下の src/todo/todo.scss ファイルの bodytodo を分離します。

src/todo/todo.scss
body {
  font-family: 'myriad pro', sans-serif;
  font-size: 20px;
  border: 0;

  todo {
    display: block;
    max-width: 400px;
    margin: 5% auto;
...

以下のように src/main.scss という新規ファイルを作成し、body のスタイルを記述します。

src/main.scss
body {
  font-family: 'myriad pro', sans-serif;
  font-size: 20px;
  border: 0;
}

src/todo/todo.scss は以下のように修正します。

data-is 属性を使ってカスタムタグを作成しているケースも考えられるため、以下のように指定するのが良いでしょう。

src/todo/todo.scss
todo, [data-is="todo"] {
...
}

src/main.scss ファイルを src/index.js で import することを忘れないでください。

src/index.js
+import './main.scss';
 import './todo/todo.tag';
  
 riot.mount('todo', {
...

自分のタグAPIをドキュメント化する

差分

todo タグのドキュメントを以下のように作成します。

README.md
# Todo

## Functionality

Quickly add a todo to a list, and strike them off when you're done.

## Usage

`<todo>` supports the following custom tag attributes:

| attribute | type | description |
| --- | --- | --- |
| `title` | String | Name of the todo list. |
| `items` | Array _optional_ | List of todo items with `title` ( String ) and `done` state ( Boolean ) and `hidden` state ( Boolean ). E.g. `[{ title:'Alpha' }, { title:'Beta', done:true }, { title:'Omega', hidden:true }]`. |

タグのデモを追加する

差分

本稿で最もためになる部分かなと筆者は思っています。

タグについては Sass と ES6 で記述し Webpack でビルドしているため、De Voorhoede 社のスタイルガイドの内容とは異なる方法でデモを追加し、そのデモをブラウザで確認できるようにする必要がありました。アプリ用の HTML とデモ用の HTML を分けてビルドするようにすることでデモをブラウザで確認できるようできるようにしています。

少し詳しくお話ししますと、以下のような構成から

src
.
├── demos
│   ├── demo
│   │   ├── demo.scss
│   │   └── demo.tag.html
│   ├── index.html          // デモ用の HTML
│   └── index.js            // デモ用の HTML から読み込まれる JavaScript
├── todo
│   ├── README.md
│   ├── todo-demos.tag.html // todo タグのデモ用のタグ
│   ├── todo.scss
│   └── todo.tag.html
├── index.html              // アプリ用の HTML
├── index.js                // アプリ用の HTML から読み込まれる JavaScript
└──  main.scss

yarn build でビルドした際に、以下のように出力されるように Webpack の config を設定しています。

dist
.
├── demos.bundle.js     // デモ用の HTML から読み込まれる JavaScript
├── demos.html          // デモ用の HTML
├── index.html          // アプリ用の HTML
├── main.bundle.js      // アプリ用の HTML から読み込まれる JavaScript
├── main.css
├── polyfills.bundle.js
└── vendor.bundle.js

まずは demo タグを作成します。

demo タグの <yield/> を使うことでデモをしたいタグを埋め込めるようにします。

src/demos/demo/demo.tag.html
import './demo.scss';

<demo>
  <p>{ label }</p>
  <yield />
  <script>
    const tag = this;
    tag.label = opts.label || '';
  </script>
</demo>
src/demos/demo/demo.scss
demo, [data-is="demo"] {
  p {
    display: block;
    background: #F3F5F5;
    padding: .5em;
    clear: both;
    margin: .5em 0;
  }
}

todo-demos タグという todo タグのデモ用のタグを作成します。

demo タグに todo タグを埋め込むことでデモを追加することができます。

src/todo/todo-demos.tag.html
import '../demos/demo/demo.tag';
import './todo.tag';

<todo-demos>

  <h2>todo demos</h2>

  <demo label="todo with title and empty list">
    <todo title="Groceries" />
  </demo>

  <demo label="todo with title and initial items">
    <todo title="Groceries" items="{ [{ title:'Apples' }, { title:'Oranges', done:true }, { title:'Grapes', hidden:true }] }" />
  </demo>

</todo-demos>

src/demos/index.js ファイルを作成します。

先ほど作成した todo-demos タグを import し、マウントします。

src/demos/index.js
import '../todo/todo-demos.tag';

riot.mount('*');

最後に HTML ファイルを作成します。

demos/demo.html
<!doctype html>

<html>
  <head>
    <title>Riot Demos</title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <!--[if lt IE 9]>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.0.5/es5-shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script>html5.addElements('todo')</script>
    <![endif]-->

  </head>

  <body>

    <todo-demos></todo-demos>

  </body>

</html>

後は Webpack の config を修正して、 デモ用の HTML として出力するようにするだけです。

まずは、 webpack/paths.js に先ほど作成したデモ用の HTML と JavaScript のパスを追加します。

webpack/paths.js
...
  export default {
   srcHtml: path.join(__dirname, '../src/index.html'),
+  srcDemosHtml: path.join(__dirname, '../src/demos/index.html'),
   srcJs: path.join(__dirname, '../src/index.js'),
+  srcDemosJs: path.join(__dirname, '../src/demos/index.js'),
   distDir: path.join(__dirname, '../dist')
 } 

html-webpack-plugin を使って HTML の出力を分けるようにしてあげましょう。chunks ではどの entry を HTML ファイルに含めるかを定義しています。

デモ用の HTML は開発環境の時だけ出力されるように webpack/dev.config.js ファイルを修正します。

webpack/dev.config.js
 import webpack from 'webpack';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
 import env from '../env.js';
 import paths from './paths.js';
 
...
     entry: {
       vendor: [
         'riot-hot-reload'
+      ],
+      demo: [
+        paths.srcDemosJs
       ]
     },
     module: {
...
           'NODE_ENV': JSON.stringify('development'),
         }
       }),
-      new webpack.HotModuleReplacementPlugin()
+      new webpack.HotModuleReplacementPlugin(),
+      new HtmlWebpackPlugin({
+        template: paths.srcDemosHtml,
+        filename: 'demos.html',
+        chunks: ['demo', 'polyfills', 'vendor']
+      }),
     ],
     devtool: 'inline-source-map',
     devServer: {

アプリ用の HTML も今まで通り出力されように webpack/dev.config.js ファイルを修正します。

webpack/common.config.js
       }),
       new HtmlWebpackPlugin({
         template: paths.srcHtml,
+        chunks: ['main', 'polyfills', 'vendor']
       }),
       new CleanWebpackPlugin(['*'], {
         root: paths.distDir,

これでデモページを表示できるようになりました。

yarn start を実行すると、ブラウザ上でアプリ用の HTML が表示されると思います。 /demos.html とパスを追加してあげるとデモ用の HTML が表示できます。

タグファイルをlintする

省略

プロジェクトにバッジをつける

差分

README.md にバッジをつけます。

最後に

筆者が作成したプロジェクトに誤りがある場合はプルリクエストやコメント等でご指摘お願いします。:thumbsup:

参考文献

14
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?