LoginSignup
12
12

More than 5 years have passed since last update.

Riot.js + webpack + babel でitunes APIを利用したWEBアプリを作ってみる

Last updated at Posted at 2017-01-20

javascriptは、jQueryを少し触った程度の私ですが、Riot.jsの公式でも紹介されているこちらのLive Ajax Searchをベースに、ES2015やRiot.jsを使ってwebアプリを作成しました。
この記事では、どんな資料を参考にしたか、どんなことに躓いたか、備忘的に記したいと思います。

完成したwebアプリはこちらに公開していますので、興味がお有りでしたら遊んでみてください。
(はじめてgithubにリポジトリを公開しました。.gitignoreを作る前にcommitしていたため、node_modulesフォルダもアップロードされてしまい、ドキドキしながら対処しました。)

git clone https://github.com/higeaaa/riot-webpack.git
cd riot-webpack
npm install
npm start 

環境

  • node v7.2.0
  • npm v3.10.9
package.jsonの抜粋.json
  "devDependencies": {
    "babel": "^6.5.2",
    "babel-core": "^6.21.0",
    "babel-loader": "^6.2.10",
    "babel-preset-es2015-riot": "^1.1.0",
    "eslint": "^3.13.1",
    "eslint-loader": "^1.6.1",
    "eslint-plugin-riot": "^0.1.6",
    "file-loader": "^0.9.0",
    "font-awesome": "^4.7.0",
    "font-awesome-webpack": "0.0.4",
    "node-sass": "^4.3.0",
    "riot": "^3.0.7",
    "riotjs-loader": "^4.0.0",
    "url-loader": "^0.5.7",
    "webpack": "^1.14.0",
    "webpack-dev-server": "^1.16.2"
  }

環境構築

webpackの設定

たくさん困った結果がwebpack.config.jsにあらわれています。
1. cors
2. ES6からES5にトランスパイルされない
3. font-awesomeを使いたい

webpack.config.js
const webpack = require('webpack')

module.exports = {
  entry: './app/main.js',
  output: {
    path: '/public',
    publicPath: '/public/',
    filename: 'bundle.js',
    crossOriginLoading: 'anonymous'
  },
  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.ProvidePlugin({ riot: 'riot' })
  ],
  module: {
    preLoaders: [
      {
//        test   : /\.js$|.tag$|.es6$/, <= k-kuwaharaさんからご指摘いただき、以下に修正
        test   : /\.js|\.tag|\.es6$/,
        exclude: /node_modules/,
        loader : 'eslint-loader'
      },
      {
        test   : /\.tag$/,
        exclude: /node_modules/,
        loader : 'riotjs-loader'
      }
    ],
    loaders: [
      {
        test   : /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: "url-loader?limit=10000&minetype=application/font-woff"
      },
      { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: "file-loader"
      }
    ],
    postLoaders: [
      {
//        test   : /\.js$|\.tag$/, <= k-kuwaharaさんからご指摘いただき、以下に修正
        test   : /\.js|\.tag$/,
        exclude: /node_modules/,
        loader : 'babel-loader',
        query  : {
          presets: ['es2015-riot']
        }
      }
    ]
  },
  devServer: {
    historyApiFallback: true,
    watchOptions: { aggregateTimeout: 300, poll: 1000 },
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
      "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
    }
  }

}

1. CORS問題 => 解決

fetch()をすると、chromeにCORSがなんちゃらと怒られ、APIで取得して表示ができない。
CORSについてはこちらがわかりやすかった。
webpack.config.jsdevServerheaderにいろいろ設定しましたが、うまくいかなかったので、結局google chromeの拡張でCORS Toggleを利用することで、解決しました。

2. ES6からES5にトランスパイルされない => 未解決 => 解決

CORS問題が解決し、動いているのを確認していたら、javascriptがES5にトランスパイルされておらず、chromeがES6を直に読み込んで動いていることがわかりました。例えば、ファイルsearch.tag<script type='babel'>とすると、riotのイベントハンドラとして作ったsearch(e){}が、syntax errorとなってしまい、困ってしまいました。
(ちなみに、<script>とすると、riotjs-loaderthis.search = function(e) {}としてくれます。)

その後、loaderqueryにいろいろなtypepresetsを設定しましたが、うまくいかず。また、preLoader(最初に読み込むローダーを指定する)にriotjs-loaderpostLoader(最後に読み込むローダーを指定する)にbabel-loaderと設定しましたが、やはりbabelがES5にトランスパイルしてくれません。

調べていたところ、こちらで話題になってもいましたが、結局解決できず、ES6のままブラウザに読み込ますようにします。

<<2017/1/24 追記>>
webpack.config.jsの設定誤りであることがご指摘でわかりました。上記のwebpack.config.jsには反映済みです。k-kuwaharaさん、ありがとうございました。

3. font-awesomeを使いたい => 解決

なんとなく使ってみたかった。公式HPのget-startedを見ると、CDNを利用するか、ダウンロード、railsならgemがあるよと書いてある。調べてみるとNPMのパッケージがあったので使った。
webpack.config.jsでローダーの設定をして、jsファイルでrequireすればよい。今回は、webpackが最初に読み込むjsファイルmain.jsでrequireする。

sassを使いたい

なんとなく使ってみたかった。node-sassを入れて、各tagファイルのstyletype='scss'と追記する。

アプリ作成

基本はこちらのLive Ajax Searchをベースに、itunes APIを利用して、itunesにある曲を検索する、という簡単なアプリを作りました。riotを体感することも楽しかったのですが、debounce()fetch()に触れることができて、楽しかったです。

アプリは、2つのtagファイルで構成されます。

  • search.tag: 検索フォームやitunes APIを使って情報を取得する
  • result-cards: search.tagで取得した検索結果をカード形式で表示する
search.tag
<search>
  <form onsubmit={ search }>
    <label>
      <input type="search"
             oninput={ search }
             onchange={ search }
             ref="s"
             placeholder="&#xf002; Search music tracks"
             id="search">
    </label>
  </form>

  <div if={ isLoading } class="loader">
    <div class="circle"> </div>
  </div>

  <p class="error" if={ error }>{ error }</p>


  <script>
    require('./results-cards.tag');

    var lastSearch = null;
    var searchUrl  = null;

    // Reset tag attributes to hide the errors and cleaning the results list
    let reset = () => {
      this.results = [];
      this.error   = false;
      searchUrl    = null;
    }

    // generate url for itunes API.
    let getApiUrl = searchWord => {
      let urlParams = {
        country: 'jp',
        lang: 'ja_jp',
        media: 'music',
        entity: 'song',
        app: 'music',
        limit: 15,
        term: searchWord
      };
      let url = 'https://itunes.apple.com/search';
      let keyValue, pat, key, value;

      for (key in urlParams) {
        key      = encodeURIComponent(key);
        value    = encodeURIComponent(urlParams[key]);
        keyValue = key + '=' + value;
        url += (url.length > 32 ? '&' : '?') + keyValue;
      };
      return url;
    }

    // Debounce the api requests
    let doApiRequest = debounce( searchUrl => {
      fetch(searchUrl, { method: 'get', mode: 'cors'})
        .then( res  => { return res.json() })
        .then( data => {
          reset();
          var r;
          if (this.refs.s.value) {
            if (data.resultCount) this.results = data.results;
            else this.error = data.Error;
          }
          this.isLoading = false;
          this.update();
          r = this.results;
          riot.mount('results-cards', { r });
        });
    }, 300, false);

    // Public API/method
    this.results = [];

    // Search callback
    search(e) {
      let searchWord = this.refs.s.value;
      if (!searchWord) {
        reset();
        setTimeout(function(){
          var r = [];
          riot.mount('results-cards', { r });
        }, 1000);
      } else if (lastSearch !== searchWord) {
        reset();
        this.isLoading = true;
        searchUrl = getApiUrl(searchWord)
        doApiRequest(searchUrl);
      };

      lastSearch = searchWord ;
    }

  </script>
  <style>
    省略
  </style>
</search>

result-cards
<results-cards riot-tag='resultCards'>
  <div if={ opts.r.length } class="container">
    <a each={ opts.r } href="{ collectionViewUrl }" target="_blank">
      <div class="content">
        <img class="artwork" src={ artworkUrl100 } alt="">
        <div class="description">
          <label>{ trackName }</label>
          <span>{ collectionName } </span>
          <span>{ artistName }</span>
          <div class="logos">
            <img src="/public/img/Get_it_on_iTunes_Badge_JP_1214.svg" alt="">
            <img if={ isStreamable } src="/public/img/JP_Listen_on_Apple_Music_Badge.svg" alt="">
          </div>
        </div>
      </div>
    </a>
  </div>


  <style type="scss">
    省略
  </style>
</results-cards>

Riotの感想は次の通りです。
1. イベントハンドラがわかりやすい
2. refsがわかりやすい

1. イベントハンドラがわかりやすい

input oninput={search}でinput要素に入力があったらsearch(e)が発火する、という具合でとてもわかりやすい。

2. refsがわかりやすい

input ref='s'とすれば、javascriptからthis.refs.s.valueでinput要素に入力された値が取得できる、という具合でとてもわかりやすい。

また、次のことを備忘として記します。
3. itunes API
4. cssのflexbox

3. itunes API

アップルが詳しい。ただ、entityをsongにしてもmusicにしても検索結果は一緒だった。検索結果の順番の指定方法はわからなかった。
results-cardsでapple musicでも視聴可能な場合、apple musicのロゴを表示している。この判定にitunes APIの戻り値にあるisStreamabletrueであればapple musicで視聴可能としました。ドキュメントに記載はありませんでしたが、数曲調べたところ、多分あっていると思います。

4. cssのflexbox

results-cardsの表示や、カードの中身について、flexboxを利用しました。コリスが詳しい。
親要素をdisplay: 'flex'など、中身をどう配置したいかを設定し、あとは子要素で設定する。floatよりもなんとなくイメージがつかみやすい。

次にやること

  • Rails5のAPIと、このようなjsの環境を使って何か作りたいです。

あとがき

riotは、javascriptを学んでいくとやれることが簡単にできそうな気がして、わくわくします。

12
12
6

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