フロントの未来の話

  • 70
    いいね
  • 0
    コメント

前置き

会社の勉強会資料になります(6/16発表)

フロントの未来の話というか、色々なライブラリの紹介です

ほとんどが5月上旬くらいに書いた資料なので、
それ以降に変化があったライブラリについては、追記という形で資料の中にコメント入れてます


モジュールバンドラーの未来

モジュールバンドラーとは簡単に言うと、JavaScriptのビルドツールのことです

最近はフロントエンドでも、機能ごと・共通化などの理由によって、JSファイルを分けてコーディングをするので、
最終的にそれをまとめて、1個のJSにする必要がある

その時に使われるのが、モジュールバンドラー


有名どころのツール

みんな大好きwebpack

後は、rollupとかbrowserifyとか

ですが、、


これからの時代は

fuse-box

https://github.com/fuse-box/fuse-box


fuse-boxとは

webpackと同様なモジュールバンドラー

設定ファイルがシンプル

webpack.configの例 ↓

webpack.config.js
module.exports = {
  entry: [
    './src/index.html',
    './src/main.ts'
  ],
  output: {
    path: `${__dirname}/dist`,
    filename: 'app.js'
  },
  devServer: {
    port: 4445
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.tsx$/,
        use: 'ts-loader',
        exclude: /node_modules/
      },
      {
        test: /\.html$/,
        use: 'html-loader',
        exclude: /node_modules/
      },
      {
        test: /\.scss$/,
        use: [
          'css-loader',
          'sass-loader'
        ]
      },
      {
        test: /index.html$/,
        use: 'file-loader?name=[name].[ext]'
      }
    ]
  }
};

これをfuse-boxで書くと ↓

fuse.js
const fsbx = require('fuse-box');

const fuseBox = fsbx.FuseBox.init({
  homeDir: 'src/',
  sourcemaps : true,
  outFile: './dist/app.js',
  plugins: [
    [
      fsbx.SassPlugin({ outputStyle: 'compressed' }),
      fsbx.CSSPlugin({})
    ],
    fsbx.TypeScriptHelpers(),
    fsbx.JSONPlugin(),
    fsbx.HTMLPlugin({ useDefault: false })
  ]
});

fuseBox.devServer('>main.ts **/*.html', {
  port: 4445
});

シンプル!!


fuse-boxの特徴として

・TypeScriptはデフォルトサポート
⇒公式に「FuseBox loves typescript」って書いてる

・HotModuleReplacement(HMR)はデフォルトサポート
⇒画面を再描画しなくても、コードの変更をブラウザに反映してくれるやつ

・なんとかloaderとか色々インストールしなくていい
⇒fuse-box内製のプラグインを使う

・めっちゃ速い!


webpackの場合

 2017-05-10 15.42.44.png

バンドル後が620kbくらいのファイルだと

初回ビルドが
1529ms

差分ビルドが
441ms


fuse-boxの場合

 2017-05-10 15.46.48.png
 2017-05-10 15.46.55.png

初回ビルドが
426ms!!!

差分ビルドが
17ms!!!!1

超速い
なんかバンドル後のファイルも軽くなってる(255.9kb)

5/11追記
fuse-box2.0がリリースされました
https://medium.com/fusebox/fusebox-2-0-the-beast-reborn-e4ec0443b782
・更にパフォーマンスを向上させた
・メソッドチェインで設定を書けるようにした
・新しいプラグインの追加
・ドキュメントの更新
など
後、fuse-box2.0は「The beast reborn(生まれ変わった獣)」って言ってます


まとめ

実務で使うのは、まだ厳しいかもしれません
私はあまりがっつり使ってませんが、バグがけっこう多いとのこと

ドキュメントとかは、当時はまだ充実していない感じでしたが
今はリリースして半年くらい経ってるので、多少充実しているはず

5/11追記
fuse-box2.0が出たタイミングでサイトも一新されたので、使いやすくなってるかも!


デザインの未来

今のフロントエンドの開発はSketchでデザインして、それ見ながらコンポーネント設計して作るのが最近のトレンド

Sketchはエンジニアがデザインを作りやすくすることを意識している気がする
Photoshopよりも難易度が低くシンプルな為、デザインデビューしたエンジニアも増えたはず

しかし、、


「俺達はReactからSketchをレンダリングする」 by Airbnb

React Sketch.app

https://github.com/airbnb/react-sketchapp
※β版


つまり、SketchでUI作ってからコーディングするとは逆のアプローチ
Reactから作って、Sketch上にレンダリングするというやり方

使い所で言えば、例えば本番データなどをSketch上にレンダリングすることが容易にできるようになると思う


まとめ

使い所が難しい

react-sketchappのgithub上でも斬新な使い方を募集してるので、
思いついた方がいらっしゃったら、教えてあげてください


Vue.jsの未来

Vue.js十分小さいんだけど、さらにコンパクトなやつないかなー
Reactで言うpreact的なやつ
※preactとは、Reactの最低限の部分だけ残してたサブセット的ライブラリ

ゲーム作りたいから、軽さと速さにこだわりたい!


安心してください!

Moon

がありますよ!

https://github.com/KingPixil/moon


Moonとは、Vue.jsのサブセット的ライブラリ
まだバージョン0.9
当初はpreactを目標として開発していた

以下のライブラリのサイズ比較を見ても、相当軽い

 2017-05-11 15.53.45.png

以下はTODOアプリの速度ベンチマーク
100項目追加・完了・削除の速度ベンチマーク?

 2017-05-11 15.58.56.png

Vue.jsに依存はしていないので、Moon単体で動く

IE9以降がPolyfill無しで動く


まとめ

preactもMoonも使ったことないので、紹介程度に留めますが
見た感じVue.jsのサブセットというよりかは、
「Vue.jsと似たAPIを提供している、軽量のまったく別のライブラリ」
と言った方が正しいかもしれません


Reactの未来

Reactの現行のバージョンは15系なのですが、ここにきて実は

Reactの内部のコードが大きく書き直されていることをご存知でしょうか?


そうです!

React Fiber

です!

https://github.com/facebook/react/tree/master/src/renderers
(fiberが置いてあるコード、この直下のdomとかnativeとかの下にそれぞれfiberコードが配置されている)


大幅なバージョンアップになりました
こちらは先日のF8というイベントで紹介されています

Facebookは、
Reactの既存バージョンと完全な後方互換性を保ち、
DOMのレンダリング性能の大幅な改善などを行っている
(非同期でのレンダリングができる)

大まかなスケジュール

v15.5.x
現在の最新バージョン
※6/13にv15.6が出たのですが、この資料の中ではv15.5が最新として進めさせてください

v16
今年の夏頃リリース予定
このバージョンのデフォルトはまだ同期レンダリング
15.5と後方互換性があり

v17
未定
このバージョンでデフォルトで非同期レンダリングとなる


今やること(v15.5でやっておくこと)

Reactのバージョンアップを怠らずにやっていれば、v15.5へのバージョンアップ時に
システムが動かないレベルのエラーが出ることは無いと思う

ただし、React.PropTypesReact.createClassを使っているとログに警告が出ているはず
これらの機能はv16では、削除される機能
それを予め、v15.5の段階で教えてくれている

依存ライブラリも含めて、削除予定の機能を使っていると警告を出すので
この警告が消えたタイミングがv16へ移行する準備が整ったということになる

なので自分のシステムでは早めに対応しておき、依存ライブラリは対応されるのを待ちましょう

・React.PropTypes
私のチームでは、prop-types.PropTypesに置き換えました
ただし、型チェックはTypeScriptやFlowなどを使うのが推奨されているようです

・React.createClass
こちらは私のチームでは使ってなかったのですが、使っている場合は
class構文などで置き換えることが推奨されているようです


見てほしいサイト
http://blog.koba04.com/post/2017/04/25/a-state-of-react-fiber/
https://html5experts.jp/shumpei-shiraishi/23265/

こちらのページにとても詳しく・分かりやすく書いてあります
実際に使う前の予習として、夏までに読んでおきましょう

個人的に面白いなーって思った新機能は
renderの戻り値を文字列や配列で返せる点
React(JSX)経験者には分かると思うが、返却する要素が複数あった場合に
divタグで囲んで、無理やり1つの要素するという作業が無くなる


 
 2017-06-13 19.27.57.png


動作サンプル

https://koba04.github.io/react-fiber-resources/examples/


コード最適化の未来

皆さんも酔っぱらいながら、コード書くとだいたいこんな感じになると思います

sample.js
(function () {
   function hello() { return 'hello'; }
  function world() { return 'world' }
          global.s = hello() + ' ' + world()
})();




(function () {
  function fibonacci(x) {
      return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
  }
      global.x = fibonacci(23)
})();

(function(){
  function fib(x) {
    return x <= 1 ? x : fib(x - 1) + fib(x - 2);
  }
      var x = Date.now()
  if (x === 0) x = fib(10);
     global.result = x;
})();

ESLintもプンプン:anger:です

 2017-05-11 16.55.25.png

普通だと1個1個エラー見ながら直すと思うのですが、、、


そこに

Prettier

をドーーン!

https://github.com/prettier/prettier


Prettierとは

前回の勉強会でも少し話しましたが、JSのコードフォーマッタです
Go言語やってる人は、gofmtみたいなものと思ってください

インストール

yarn global add prettier

今回は試すだけなので、globalで入れてる

実行

prettier --write src/sample.js

※writeを付けないと、ファイルを上書きせずに結果だけコンソール上に出す

結果

sample.js
(function() {
  function hello() {
    return "hello";
  }
  function world() {
    return "world";
  }
  global.s = hello() + " " + world();
})();

(function() {
  function fibonacci(x) {
    return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
  }
  global.x = fibonacci(23);
})();

(function() {
  function fib(x) {
    return x <= 1 ? x : fib(x - 1) + fib(x - 2);
  }
  var x = Date.now();
  if (x === 0) x = fib(10);
  global.result = x;
})();

すごく綺麗になりました


まだだ、、
まだ終わらんよ!!


さらに

Prepack

をドーーン!!

https://github.com/facebook/prepack


Prepackとは

・Facebook製のJSをAOT(事前)コンパイルするツール
・その際にコードの最適化を行う
・今年のGWらへんで公開されたばっかりのツール

インストール

yarn global add prepack

今回は試すだけなので、globalで入れてる

実行

prepack src/sample.js --out src/output.js

※先程のPrettier結果後のsample.jsを読み込み、コードを最適化してoutput.jsで出す

結果

output.js
(function () {
  s = "hello world";
  x = 28657;

  var _$0 = Date.now();

  if (typeof _$0 !== "number") {
    throw new Error("Prepack model invariant violation: " + _$0);
  }

  result = _$0 === 0 ? 55 : _$0;
})();

比較用 ↓
Prepackする前のやつ

sample.js
(function() {
  function hello() {
    return "hello";
  }
  function world() {
    return "world";
  }
  global.s = hello() + " " + world();
})();

(function() {
  function fibonacci(x) {
    return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
  }
  global.x = fibonacci(23);
})();

(function() {
  function fib(x) {
    return x <= 1 ? x : fib(x - 1) + fib(x - 2);
  }
  var x = Date.now();
  if (x === 0) x = fib(10);
  global.result = x;
})();

sは文字列が連結された状態になってる!
xは計算された状態になってる!
resultもかなり最適化されて、エラー処理も追加されてる!
functionも1個になってる!

すごいね:thumbsup:


ESLintとPrettierが喧嘩せずに動いてほしいです

ESLint側でPrettierを設定しましょう
以下のプラグインを設定してあげることで、ESLintのルールとしてPrettierを追加できる

yarn add prettier eslint-plugin-prettier eslint-config-prettier --dev
.eslintrc
{
  ・・・
  "extends": [
    "prettier"
  ],
  "plugins": [
    "prettier"
  ],
  ・・・
}

webpackにもeslint-loaderを設定しておく

webpack.config.js
module.exports = {
  ・・・
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.js$/,
        use: 'eslint-loader',
        exclude: /node_modules/,
        enforce: 'pre'
      },
    ]
  }
  ・・・
}

先程のコードをwebpackで実行

webpack
先程の酔っぱらいコード.js
(function () {
   function hello() { return 'hello'; }
  function world() { return 'world' }
          global.s = hello() + ' ' + world()
})();




(function () {
  function fibonacci(x) {
      return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
  }
      global.x = fibonacci(23)
})();

(function(){
  function fib(x) {
    return x <= 1 ? x : fib(x - 1) + fib(x - 2);
  }
      var x = Date.now()
  if (x === 0) x = fib(10);
     global.result = x;
})();

エラーがだいぶ減りましたね

 2017-05-11 18.58.23.png

つまり、Prettierされたものに対して、ESLintが走っているということです
残ったエラーはPrettierでは対応できないESLintエラーなので、手動で直しましょう


PrepackがES6以降で書いたやつでも動いてほしいです

PrepackはES6の変換に対応していない
公式のロードマップを見ると、ES6に対応するのはまだ先だと思うので
Prepackで実行する前に、Babel等でトランスパイルしてあげる必要がある

今回はbabel-loaderを装備したwebpackにPrepackを組み込んでみる
prepack-webpack-pluginというプラグインを利用する

yarn add prepack-webpack-plugin --dev
webpack.config.js
const path = require('path')
const PrepackWebpackPlugin = require('prepack-webpack-plugin').default

module.exports = {
  entry: [
    path.join(__dirname, '../src/sample')
  ],
  output: {
    path: path.join(__dirname, '../dist'),
    filename: 'bundle.js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
    ]
  },
  plugins: [
    new PrepackWebpackPlugin()
  ]
}

先程のコードをES6のアロー関数にしてwebpackで実行

webpack
先程の酔っぱらいコード・アローバージョン.js
(() => {
   function hello() { return 'hello'; }
  function world() { return 'world' }
          global.s = hello() + ' ' + world()
})();




(() => {
  function fibonacci(x) {
      return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
  }
      global.x = fibonacci(23)
})();

(() => {
  function fib(x) {
    return x <= 1 ? x : fib(x - 1) + fib(x - 2);
  }
      var x = Date.now()
  if (x === 0) x = fib(10);
     global.result = x;
})();

成功しました

 2017-05-11 19.17.13.png

Prepackもされた!
アロー関数もfunctionに変換された!

bundle.js
(function () {
  s = "hello world";
  x = 28657;

  var _$0 = Date.now();

  if (typeof _$0 !== "number") {
    throw new Error("Prepack model invariant violation: " + _$0);
  }

  result = _$0 === 0 ? 55 : _$0;
})();

Prepackの速度に関して

いつものwebpackに加えて、Prepackをするので
当然ビルドする速度は落ちるのですが、どのくらい違うのか見てみました

使用するコードは、先程のコード

 2017-05-11 19.22.29.png

だいぶ遅い
けど、ファイルサイズを見てもかなり最適化されたのは分かる

そのうち速度は改善されるかもしれませんが、、
今後、速度に問題が残れば、ローカル開発時にはPrepackを使わずに、
本番やステージングにてPrepackを適用するというやり方になるかもしれません

ちなみにPrettierは速いよ!


おまけ

①AtomやVimでもPrettier使いたい!

あるよ!

Atom
https://github.com/prettier/prettier-atom
Vim
https://github.com/heavenshell/vim-prettier

他にもあるかも
保存した時にPrettierが走る感じ
てかエディタがPrettierに対応していたら、設定するべき!


②CSS置いていかれてない??

Prettier for CSS
https://github.com/stylelint/stylelint/issues/2532

こちらのstylelintのissuesでも盛り上がってますが、Prettierが良すぎたので、
Prettier並のCSSコードフォーマッタが出る日も近いかもしれません

5/20追記
こんなプルリク上がってた
Initial CSS support
https://github.com/prettier/prettier/pull/1636

6/2追記
PostCSS経由でのCSS/SCSS/LESSに対応された:tada:
https://github.com/prettier/prettier/releases/tag/1.4.0


まとめ

Prettierは実用で使えるレベルだと思う
エディタが対応していたら、入れてみたらどうでしょうか
不安ならESLintも併用しておけば、品質に関しては担保できる

Prepackはこれからが期待されるツール
事前に計算処理をコンパイルの段階でやっておくことで、ブラウザで実行時のパフォーマンスアップが大いに期待できる
Prepackが単体で動いて、ES6以降の変換もできて、速く動いたら、かなり熱いコンパイルツールですね


さいごに

いくつかライブラリやツールを紹介しましたが、気になったものはありましたでしょうか?

正直どれも使わなくても、やっていけるのですが、、
気になったライブラリを見つけたら、軽く使って検証する癖を付けておくと、
今後システム選定をする際に、選択肢が増えて、良い判断ができ、より良いシステム開発ができると思います


おまけ

以下は話そうと思ったけど、調べる前に資料が大きくなったので、削りました
簡単に概要だけ紹介だけしときます

ReactXP

Microsoft製のクロスプラットフォーム開発用ライブラリ
例えばReactNative使っていても、プラットフォームごとに個別に実装が必要なUI部分はある
ReactXPは抽象なクロスプラットフォーム層を用意し、そこにView定義・スタイル・アニメーションを定義しておけば
各プラットフォームで共有できるようにしている
(継承とか、オーバーライド的なことをやるのだろうか?)

ReactとReactNativeをサポートしているので、WebもNativeもいける

https://github.com/Microsoft/reactxp


Weex

アリババ製のVue.js構文のサポートしているNativeアプリのフレームワーク
Vue.js版ReactNativeのようなものだと思う

https://github.com/alibaba/weex

6/13追記
こちらの勉強会でWeexについて話されるみたいなので、聞いてきます
https://vuejs-meetup.connpass.com/event/58071/?utm_campaign=&utm_source=notifications&utm_medium=email&utm_content=title_link


Relay(&GraphQL)

Facebook製のReactでデータ駆動型プログラムを実現するライブラリ

https://github.com/facebook/relay

先日のF8でも既存のRelayに新機能+パフォーマンス改善を行った
「Relay Modern」という新しいRelayが発表された

Relayを使うとサーバサイドから取得したデータをReactのpropsに渡すことが簡単にできるようになる
つまり、以下のことをRelayが全て行う
・アクションを検知
・GraphQLを使って、サーバへリクエスト
・サーバからのレスポンスをpropsに設定

※GraphQLを簡単に説明
RESTに比べて、直感的にリクエストを投げれる
以下はそれぞれのリクエストの例

REST
GET /users?id=1

GraphQL

{
  users(id: "1") {
    id
    name
    email
  }
}

RESTもGraphQLもレスポンスは以下となる

{
  "users": [
    {
      "id": 1,
      "name": "hoge",
      "email": "hoge@example.com"
    },
    {
      "id": 1,
      "name": "foo",
      "email": "foo@example.com"
    }
  ]
}

以下はRelayの公式の例

class HelloApp extends React.Component {
  render() {
    const {hello} = this.props.greetings;
    return <h1>{hello}</h1>;
  }
}

HelloApp = Relay.createContainer(HelloApp, {
  fragments: {
    greetings: () => Relay.QL`
      fragment on Greetings {
        hello,
      }
    `,
  },
});

第1引数のHelloAppはコンポーネント
第2引数はGraphQLを使って取得するデータ

コンポーネントで使うサーバサイドのデータが宣言的に取得できる
(必要なものだけ宣言して取れる)

5/19追記
v1.0.0リリースされた