Webpack + Riot + Materializeで、Webサイト環境を作った話
October 17, 2017

DEVELOPMENT

riot
web
js
node

RiotとWebPackを使う目的

うーちゃん家のWebサイトは、以前、サーバー上のRailsで動いてましたが、そんだけのために、Webサーバーを使ってるのがもったいなく感じてきたので、webpackを使いstaticな静的ファイルを生成してWebサーバーを使うのをやめようということで、モダンなjsで代表的なのはReactですが、(Angularはどうだろう)、今回はちゃちゃっと済ましたいので、Riotを使ってみることにしました。

環境作り

  • webpackでプレコンパイルして、staticな形で出力する
  • riot は.tagファイルで記述し読む
  • materializeを全体的に使いたい
  • 自作スタイルシートはsassで記述したいけど、js中でmoduleで読むのは面倒なので、cssファイルを出力し、タグ中で普通に使いたい
  • 画像はHTTPリクエストを最大限に減らすため、base64でコンパイルする

バージョンについて

  • webpack 3.8.0
  • riot 3.7.3

必要なパッケージを入れる

node 初期化

$ npm init

node が入ってない場合はbrewか何かで入れて下さい。

  • Webpack
$ npm install --save-dev webpack
  • Babel を入れる
$ npm install --save-dev babel-core babel-loader
  • ec2015
$ npm install --save-dev babel-preset-es2015 babel-preset-es2015-riot
  • webpack のためのローダー
$ npm install --save-dev riotjs-loader
  • Riot.js を入れる
$ npm install --save riot
  • Materialize とsassに必要なもの
$ npm install --save materialize-css
$ npm install --save-dev sass-loader css-loader file-loader url-loader materialize-loader
  • jquery ※materializeは、jqueryを使ってるため
$ npm install --save jquery

Webpackの設定ファイル

  • webpack.config.babel.js
    import path from 'path';
    import webpack from 'webpack';
    import ExtractTextPlugin from 'extract-text-webpack-plugin';

    export default [
        {
            context: path.join(__dirname, './source/javascripts'),
            entry: {
                app: './app.js'
            },
            output: {
                path: path.join(__dirname, './public'),
                filename: '[name].js'
            },
            module: {
                rules: [
                    {
                        test: /\.tag$/,
                        exclude: /node_modules/,
                        enforce: "pre",
                        loader: 'riotjs-loader'
                    },
                    {
                        test: /\.js[x]?$|\.tag$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                    },
                    {
                        test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/,
                        loader: 'url-loader',
                    },
                ],
            },
            resolve: {
                extensions: ['*', '.js', '.tag']
            },
            plugins: [
                new webpack.ProvidePlugin({
                    riot: 'riot'
                })
            ]
        },
        {
            context: path.join(__dirname, './source/stylesheets'),
            entry: {
                app: './style.sass'
            },
            output: {
                path: path.join(__dirname, './public'),
                filename: '[name].css'
            },
            module: {
                rules: [
                    {
                        test: /\.sass$/,
                        exclude: /node_modules/,
                        use: ExtractTextPlugin.extract({
                            fallback: "style-loader",
                            use: [
                                {
                                    loader: "css-loader",
                                    options: {
                                        url: false,
                                        minimize: true
                                    }
                                },
                                "sass-loader"
                            ]
                        })
                    },
                    {
                        test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/,
                        loader: 'url-loader',
                    },
                ]
            },
            plugins: [
                new ExtractTextPlugin({
                    filename: '[name].css'
                })
            ]
        }
    ]

※ entry や output部分のファイル名やパスは、必要に応じて変更して下さい。

  • jsの方は、riot-loaderと、babelのloaderを指定してます。
  • cssは、sassをコンパイルして、cssを履きその内容をタグの中でつかえるようにしました。moduleで読む方法もあるけど、毎回js中でimportするのが面倒なので、この方法をとりました。
  • 画像はjsからもcssからも、base64で読むようにurl-loaderをそれぞれで指定してます。そのためpublic以下にわざわざ画像を出力しません。
  • .babelrc
{
    "presets": [ "es2015", "es2015-riot", "stage-3" ]
}

これで環境ができました。

あとは、riotとmaterializeを使って書いていきます。


Materialize

設定

  • webpack.config.babel.js に下記を追加します。
    context: path.join(__dirname, './source/javascripts'),
    entry: {
        app: './app.js'
    },
    .
    .
    .
    ここから
      {
          test: /\.scss$/,
          loader: ExtractTextPlugin.extract('style-loader', 'css-loader?-url&minimize&sourceMap!sass-loader')
      },
      {
          test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
          loader: 'url-loader?limit=10000&mimetype=application/font-woff'
      },
      {
          test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
          loader: 'file-loader'
      }
    ここまで
  • scssをプレコンパイル対象に追加したのは、materializeの色をカスタマイズするために、下記で設定するscssファイルが必要だったからです。
  • woffとttfなどは、materializeで仕様しているフォントファイル等が必要なためです。
  • file-loaderにより、public以下に出力されます。
  • url-loaderはmaterializeのcssファイル中から、fontを読めるようにするためです。

設定ファイルを用意

source/config ディレクトリを作成し

  • materialize.config.jsを作成
    module.exports = {
        styles: {
            'materialize': true,
        }
    };
  • materialize.config.scssを作成
    $primary-color: color("blue-grey", "lighten-2");
    $primary-color-light: lighten($primary-color, 15%);
    $primary-color-dark: darken($primary-color, 15%);

※好きな色を指定する(色をカスタマイズしたいときのみ)

materialize.config.cssを作成

読み込み

  • app.jsでrequireする
    require('materialize-loader!../config/materialize.config.js');
    require('materialize-css');

これでmaterializeが適応されました

  • materialize-loaderはcssしかロードしてくれないような感じでしたので、jsも適応したいので、materialize-css を直接記述してます。

Material icon

$ npm install --save-dev material-design-icons

## Riot 試しにカウントアップ・ダウンのサンプルをやってみます source/tags ディレクトリを作成し、 count.tagを作成
    <count>
        <p>カウント: { count }</p>
        <button onclick={ add }>+</button>
        <button onclick={ minus }>-</button>
        <script>
            this.count = 0;
            add() {
                this.count += 1;
            }
            minus() {
                this.count -= 1;
            }
        </script>
    </count>
  • scriptタグ中のthis.xxxと定義すると、htmlタグ中で、{xxx}の形で変数を扱えます。
  • methodについても、script中で定義したmethodは{xxxx}で扱えます。 わかりやすいですね。
  • source/javascripts/app.jsを編集
    import count from '../tags/count'

    riot.mount('count')

mountし、描画される

複数の.tagを読み込む際、何行もimportするのを回避する

  • source/tags/index.jsを用意
    import count from './count'
    import navigation from './navigation'

※もっといい方法あれば教えてください。

source/tags/navigation.tagsを適当に用意する

そうすると、app.jsでは

    import {count, navigation} from '../tags'

こう書くことができ、一気にたくさんimportできます

これで整いました

まとめ

webpackでの、画像の取扱や、cssのloaderがたくさんあるので、少し戸惑いましたが、理解できてスッキリしました。 riotは視覚的に非常にわかりやすく、モダンにjsを書きたいけど、reactまでやるのはちょっとって方にはいいかもしれません。

うかい / 株式会社UKAI
うかい@代表取締役兼エンジニア株式会社UKAI
Nobuyuki Ukai

株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから

🙏よかったらシェアお願いします🙏
WRITTEN BY
うかい / 株式会社UKAI
うかい@代表取締役兼エンジニア株式会社UKAI

Nobuyuki Ukai

株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから

CONACT
入力して下さい
Slack からもどうぞ

※お気軽にどうぞ!(6月20日まで有効!お早めに)

COPYRIGHT © 2020 UKAI CO., LTD. ALL RIGHTS RESERVED.