16
16

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-04-27

はじめに

以下の記事の 開発環境構築編 です。

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

Webpack2 を利用して Riot.js + RiotControl の環境を構築していきます。
以下の方であれば難なく理解できる内容となっています。:ok_woman:

  • Webpack をある程度理解している方
  • Migrating from v1 to v2 を読み Webpack v1 と v2 との違いを理解している方

いい感じに開発環境を構築した:sunglasses: ( と思っている:confounded: ) ので参考にしてもらえると助かります!

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

kotarella1110/riot-starter at v1.0.0

開発環境構築

今回構築する開発環境は以下がベースとなっています。

準備

プロジェクトを作成します。

$ mkdir riot-starter
$ cd riot-starter

Node.js のバージョン管理ツールとして nvm をインストールしてください。

まずは、Node.js のバージョンを固定するために、バージョン番号を含む .nvmrc ファイルをプロジェクトに作成します。

※ バージョン番号は現在 ( 2016/04 ) LTS の v6.10.2 を指定しています

.nvmrc
6.10.2

以下のコマンドにより、.nvmrc ファイルに記載されたバージョンの Node.js をインストールして使用する事ができます。

$ nvm install
$ nvm use

パッケージマネージャとして yarn をインストールしてください。npm install -g yarn でインストールする事ができます。

yarn を利用して package.json ファイルを作成します。

$ yarn init -y

Riot.js

dependencies
$ yarn add riot riot-route riotcontrol
  • riot: シンプルで洗練されたコンポーネントベースの UI ライブラリ
  • riot-route: シンプルなクライアント側のルータ
  • riotcontrol: Flux のインスピレーションを受けた Riot.js のためのシンプルな Central Event Controller / Dispatcher
devDependencies
$ yarn add --dev riot-tag-loader riot-hot-reload
  • riot-tag-loader: Riot.js のタグをトランスパイルする Webpack ローダー
  • riot-hot-reload: Riot.js を拡張してタグをホットリロード可能にする Api

Babel

Promise など、最新の ES の仕様を使いたい場合には、babel-polyfill をクライアントコードに含める必要があります。

この polyfill を使うと、バンドルのサイズが増えるため、これらの機能を使いたい場合のみ追加するようにしてください。

dependencies
$ yarn add --dev babel-polyfill
  • babel-polyfill: Babel の polyfill
devDependencies
$ yarn add --dev babel-core babel-loader babel-preset-latest babel-preset-es2015-riot
  • babel-core: Babel のコアパッケージ
  • babel-loader: JavasScript をトランスパイルする Webpack ローダー
  • babel-preset-latest: JavaScript のプリセット
  • babel-preset-es2015-riot: Riot.js のプリセット

.babelrc ファイルを作成します。
babel-preset-es2015-riot を設定しています。

.babelrc
{
  presets: [ 'es2015-riot' ]
}

Sass

変換には3つの別々のローダーとnode-sassライブラリーが関わっています。

devDependencies
$ yarn add --dev style-loader css-loader sass-loader node-sass
  • style-loader: ドキュメントの <style> タグ内にCSSを出力する Webpack ローダー
  • css-loader: CSS を解析して JavaScript で扱えるようにして、すべての依存オブジェクトを解決する Webpack ローダー
  • sass-loader: SassファイルをCSSへ変換する Webpack ローダー
  • node-sass: Sass のスタイルシートプリプロセッサの C 言語バージョン LibSass を Node.js で使用できるようにしたライブラリモジュール

画像

devDependencies
$ yarn add --dev file-loader url-loader
  • file-loader url-loader: 画像等を取り扱う Webpack ローダー

Webpack2

devDependencies
$ yarn add --dev webpack webpack-dev-server webpack-merge
  • webpack: JavaScript ファイルをブラウザで使用するためのバンドルや、あらゆるリソースやアセットを変換、バンドル、パッケージ化することができるモジュールバンドラ
  • webpack-dev-server: Webpack を使用したライブリロードを提供する開発サーバー
  • webpack-merge: Webpack の config 等をマージする関数
devDependencies
$ yarn add --dev html-webpack-plugin clean-webpack-plugin extract-text-webpack-plugin
  • html-webpack-plugin: バンドルに対応する HTML ファイルの作成を簡単にする Webpack プラグイン
  • clean-webpack-plugin: ビルドフォルダーをビルド実行する前に削除する Webpack プラグイン
  • extract-text-webpack-plugin: モジュールをバンドルから抽出してファイルに出力する ( Sass からコンパイルされた CSS を JavaScript バンドルファイルから抜き出し、CSSバンドルとして抽出する ) Webpack プラグイン

ディレクトリ作成

$ mkdir webpack src dist
  • webpack: Webpack の config 管理用のディレクトリ
  • src: コンパイル入力元のディレクトリ
  • dist: コンパイル出力先のディレクトリ

config ファイル作成

異なる環境で複数の設定を念頭に置いているため、環境ごとに別々の js ファイルを作成しています。

まずは、Webpack で扱うパスを保持したオブジェクトを返す paths ファイルを作成します。

webpack/path.js
import path from 'path';

export default {
  srcHtml: path.join(__dirname, '../src/index.html'),
  srcJs: path.join(__dirname, '../src/index.js'),
  distDir: path.join(__dirname, '../dist')
}

開発環境と本番環境に適用する共通の config ファイルを作成します。

webpack/common.config.js
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import CleanWebpackPlugin from 'clean-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import paths from './paths.js';

export default (env) => {
  return {
    entry: {
      polyfills: [
        'babel-polyfill'
      ],
      main: [
        paths.srcJs
      ]
    },
    output: {
      path: paths.distDir,
      filename: '[name].bundle.js',
      // publicPath: publicPath,
      sourceMapFilename: '[name].map'
    },
    module: {
      rules: [
        { // js
          test: /\.(js|tag)$/,
          include: /src/,
          exclude: /node_modules/,
          enforce: 'post',
          loader: 'babel-loader',
          options: {
            babelrc: false,
            presets: [
              [ 'latest', { modules: false } ]
            ]
          }
        },
        { // css
          test: /\.css$/,
          include: /src/,
          exclude: /node_modules/,
          enforce: 'post',
          use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: 'css-loader'
          })
        },
        { // sass
          test: /\.(scss|sass)$/,
          include: /src/,
          exclude: /node_modules/,
          enforce: 'post',
          use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: [
              'css-loader',
              'sass-loader'
            ]
          })
        },
        { // img
          test: /\.(jpg|png|gif)$/,
          include: /src/,
          exclude: /node_modules/,
          enforce: 'post',
          use: 'file-loader'
        },
        { // font
          test: /\.(woff|woff2|eot|ttf|svg)$/,
          include: /src/,
          exclude: /node_modules/,
          enforce: 'post',
          use: {
            loader: 'url-loader',
            options: {
              limit: 100000
            }
          }
        }
      ]
    },
    resolve: {
      extensions: ['.js'],
    },
    plugins: [
      new webpack.ProvidePlugin({
        riot: 'riot'
      }),
      new HtmlWebpackPlugin({
        template: paths.srcHtml,
        inject: true
      }),
      new CleanWebpackPlugin(['*'], {
        root: paths.distDir,
        exclude: '.gitkeep'
      }),
      new ExtractTextPlugin('main.css')
    ]
  };
}

babel-preset-latest に ES モジュールの書き換えを無効化する事で Tree Shaking 機能を有効にして、エクスポートしたのに使われていない部分をバンドルファイルから取り除いてサイズを抑えます。

presets: [
  [ 'latest', { modules: false } ]
]

開発環境のみに適用する config ファイルを作成します。

webpack/dev.config.js
import webpack from 'webpack';
import env from '../env.js';
import paths from './paths.js';

export default () => {
  return {
    entry: {
      vendor: [
        'riot-hot-reload'
      ]
    },
    module: {
      rules: [
        { // riot
          test: /\.(tag)$/,
          include: /src/,
          exclude: /node_modules/,
          enforce: 'pre',
          loader: 'riot-tag-loader',
          options: {
            type: 'es6', // transpile the riot tags using babel
            hot: true,
            debug: true
          }
        }
      ]
    },
    plugins: [
      new webpack.DefinePlugin({
        'process.env': {
          'NODE_ENV': JSON.stringify('development'),
        }
      }),
      new webpack.HotModuleReplacementPlugin()
    ],
    devtool: 'inline-source-map',
    devServer: {
      contentBase: paths.distDir,
      host: env.devServer.host || 'localhost',
      port: env.devServer.port || 8080,
      open: true,
      hot: true,
      inline: true,
    }
  };
}

本番環境のみに適用する config ファイルを作成します。

webpack/prod.config.js
import webpack from 'webpack';

export default () => {
  return {
    module: {
      rules: [
        { // riot
          test: /\.(tag)$/,
          include: /src/,
          exclude: /node_modules/,
          enforce: 'pre',
          loader: 'riot-tag-loader',
          options: {
            type: 'es6', // transpile the riot tags using babel
          }
        }
      ]
    },
    plugins: [
      new webpack.DefinePlugin({
        'process.env': {
          'NODE_ENV': JSON.stringify('production'),
        }
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false,
          drop_console: true,
          drop_debugger: true
        },
        output: {
          comments: false
        }
      })
    ]
  };
}

webpack.config.babel.js ファイルを作成します。
webpack-merge を利用して、共通の config ファイルと環境に応じた config ファイルをマージして返しています。

webpack --env=production --progress --profile --colors と実行することで、 env.productiontrue になります。

webpack.config.babel.js
import webpackMerge from 'webpack-merge';
import commonConfig from './webpack/common.config.js';
import devConfig from './webpack/dev.config.js';
import prodConfig from './webpack/prod.config.js';

export default (env = {}) => {
  let isProd = !!env.production;
  if(isProd) {
    return webpackMerge(commonConfig(env), prodConfig());
  }
  return webpackMerge(commonConfig(env), devConfig());
}

env.example.js ファイルを作成します。

env.example.js
export default {
  devServer: {
    host: 'localhost',
    port: 8080
  }
};

yarn install の後に env.example.js をコピーして env.js を作成するようにしています。

env.js ファイルで定義されたホストとポートは webpack/dev.config.js ファイルで使われます。

webpack/dev.config.js
...
import env from '../env.js';
...
      host: env.devServer.host || 'localhost',
      port: env.devServer.port || 8080,
...

env.js ファイルのホストとポートを変更に応じて、webpack-dev-server のホストとポートも変更でるようにしています。

npm-script

例えば、筆者の場合 webpack-dev-server を起動する場合以下のようなコマンドを実行します。

webpack-dev-server --progress --colors

とても長くて覚えることができません。

以下のように、npm-script を使うことで yarn start という短いコマンドで webpack-dev-server を起動することができます。

package.json
+  "scripts": {
+    "postinstall": "cp env.example.js env.js",
+    "start": "webpack-dev-server --progress --colors",
+    "build": "webpack -d --progress --colors",
+    "build:prod": "webpack --env.production --progress --colors",
+    "watch": "webpack --watch --progress --colors",
+    "watch:prod": "webpack --watch --env.production --progress --colors"
+  },

postinstallnpm install ( yarn install ) の後に実行されます。

git

空ディレクトリも管理できるように .gitkeep ファイルを作成します。

touch webpack/.gitkeep src/.gitkeep dist/.gitkeep

.gitignore を作成します。

dist 以下と env.js を git 管理対象外としています。

.gitignore
# Created by https://www.gitignore.io/api/osx,node,linux,windows

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env


### OSX ###
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk

# End of https://www.gitignore.io/api/osx,node,linux,windows

/dist/*
/env.js

完成した開発環境の確認

プロジェクトのディレクトリ構造を確認してください。

.
├── dist
│   └── .gitkeep
├── src
│   └── .gitkeep
├── node_modules/
├── webpack
│   ├── .gitkeep
│   ├── common.config.js
│   ├── dev.config.js
│   ├── paths.js
│   └── prod.config.js
├── .babelrc
├── .gitignore
├── .nvmrc
├── env.example.js
├── package.json
├── webpack.config.babel.js
└── yarn.lock

package.json を確認にしてください。

package.json
{
  "name": "riot-starter",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "postinstall": "cp env.example.js env.js",
    "start": "webpack-dev-server --progress --colors",
    "build": "webpack -d --progress --colors",
    "build:prod": "webpack --env.production --progress --colors",
    "watch": "webpack --watch --progress --colors",
    "watch:prod": "webpack --watch --env.production --progress --colors"
  },
  "dependencies": {
    "riot": "^3.4.3",
    "riot-route": "^3.1.1",
    "riotcontrol": "^0.0.3"
  },
  "devDependencies": {
    "babel-core": "^6.24.1",
    "babel-loader": "^7.0.0",
    "babel-polyfill": "^6.23.0",
    "babel-preset-es2015-riot": "^1.1.0",
    "babel-preset-latest": "^6.24.1",
    "clean-webpack-plugin": "^0.1.16",
    "css-loader": "^0.28.0",
    "extract-text-webpack-plugin": "^2.1.0",
    "file-loader": "^0.11.1",
    "html-webpack-plugin": "^2.28.0",
    "node-sass": "^4.5.2",
    "riot-hot-reload": "^0.0.2",
    "riot-tag-loader": "^1.0.0",
    "sass-loader": "^6.0.3",
    "style-loader": "^0.16.1",
    "url-loader": "^0.5.8",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.4",
    "webpack-merge": "^4.1.0"
  }
}

やっと開発環境構築が完了です!:sparkles:

サンプルアプリを動かしてみる

開発環境整ったので、試しに Riot.js のアプリケーションを動かしてみましょう。

examples/todo-app at gh-pages · riot/examples を ES6 と Sass で書き直した riot-starter/src at v1.0.0 · kotarella1110/riot-startersrc ディレクトリ以下に格納してください。

yarn start と実行することでブラウザが立ち上がります。:v:

最後に

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

どうでもいい話ですが、今回 yarn に習って初めて絵文字使ってみました!!:laughing:

参考文献

16
16
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
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?