Skip to content
FuseBoxを使ってみる(清書してブログにする)
JavaScript TypeScript HTML CSS
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src
.gitignore
.prettierrc.json
README.md
fuse.js
package.json
tsconfig.json
yarn.lock

README.md

FuseBoxを使ってみる

FuseBoxというイケてるモジュールバンドラーがあるらしいので使ってみる。

どこらへんがイケてるのか

  • 早い
  • デフォルトでTypeScriptに対応している
  • devサーバーが用意されている
  • HMRがサポートされている
  • configファイルがわりと簡素

らしい

実際に使ってみた

公式ページに乗っているconfigファイルをちょっと書き換えてfuse.jsの名前で保存する。 これをnodeコマンドで読み込めば実行されるらしい。webpack-cliみたいな専用のCLIアプリをインストールする必要がない点は個人的には好き。

// fuse.js
const { FuseBox } = require("fuse-box");

const fuse = FuseBox.init({ // initの引数の型はd.tsが準備されてるので結構書きやすい
  homeDir: "src",
  output: "dist/$name.js",
  target: "browser@es5", // es5で出力してみる
  sourceMaps: { inline: true, vendor: true }, // inlineSourceあり、ブラウザ対応ありにしておく
});

fuse.bundle("app").instructions(`> index.ts`);

fuse.run();

srcディレクトリに適当なtsファイルindex.tsを入れておく。

// index.ts
// 適当にES6やTypeScriptの機能が入ったコードを書く

enum LogLevel {
    Info,
    Error,
}
type Message = { type: LogLevel; message: string };

const showMessage = (m: Message): string => {
    switch (m.type) {
        case LogLevel.Info:
            return `📢 ${m.message}`;
        case LogLevel.Error:
            return `⚠️ ${m.message}`;
        default:
            throw new Error("invalid message");
    }
};

const hello: Message = { type: LogLevel.Info, message: "Hello World" };
document.querySelector("body").innerHTML = showMessage(hello);
node fuse

dist/app.jsdist/app.jsが出力される。 また、.fuseboxというcacheファイルも生成される(ファイルを更新したのに反映されない的な不具合があったらコレを消すとよさそう)

// app.js の一部

var LogLevel;
(function (LogLevel) {
    LogLevel[LogLevel["Info"] = 0] = "Info";
    LogLevel[LogLevel["Error"] = 1] = "Error";
})(LogLevel || (LogLevel = {}));
var showMessage = function (m) {
    switch (m.type) {
        case LogLevel.Info:
            return "\uD83D\uDCE2\u00A0" + m.message;
        case LogLevel.Error:
            return "\u26A0\uFE0F " + m.message;
        default:
            throw new Error("invalid message");
    }
};

ES5に置き換えられてる。

tsconfigを定義していない場合、src以下に自動生成される。

// FuseBoxが自動生成したやつ

{
  "compilerOptions": {
    "module": "CommonJS",
    "target": "ES5",
    "sourceMap": true,
    "inlineSources": true,
    "jsx": "react",
    "baseUrl": ".",
    "importHelpers": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}

devサーバーを使う

まずdist/index.htmlを用意しする。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script src="./app.js"></script>
</body>
</html>

fuse.jsにdevを入れる。

fuse.dev({
    port: 8888 // port番号。デフォルトは4444。
});

fuse.bundle("app")
    .instructions(`> index.ts`)
    .watch() // tsを書き換えるたびに差分コンパイルを行う
    .hmr(); // hot module replacementを有効にする

http://localhost:8888にアクセスすると「📢 Hello World」が表示される。 devサーバーはoutputディレクトリをルートするので、distにindex.htmlとかfavicon.icoとかを入れておくと自動的に読み込まれる。

つまづきポイント fuse.bundle()より後ろの行でfuse.dev({ports:8888})を呼んだところ、 HMRの接続先がws://localhost:4444になって動かなかった

HMRが有効なのでブラウザの再読込を押さなくても変更が画面に反映される。

TSXにも対応してるんだって

react等で使われているjsx記法にも対応している。 実際に使ってみる。今回は私が使い慣れているのでsnabbdom-pragmaというJSXライブラリを用いる。

yarn add snabbdom snabbdom-pragma

適当にtsx記法で書かれたソースコードを用意する。

import * as SnabbdomPragma from "snabbdom-pragma";

export function view(URL: string) {
    return (
        <div>
            <h1>Hello World</h1>
            <p>This page is written by snabbdom-pragma</p>
            <p>{URL}</p>
        </div>
    );
}

index.tsでこいつを読み取って、ブラウザに表示させます。

import * as snabbdom from "snabbdom";
import { view } from "./view";

const patch = snabbdom.init([]);
patch(document.querySelector("#app"), view(window.location.href));

動く。

静的ファイルのコピー

index.htmldist/に置くのがなんか気持ち悪いので、publicディレクトリに入れて、実行時にdist/へコピーするようにしたい。 ついでにfaviconとかも適当に使ってみる(favicon作成はこのサイトを使った)。

src(["index.html", "favicon.ico"], { base: "public" })
    .dest("dist")
    .exec();

Pluginを使えばindex.htmlを用意する必要も無い

↑でやる必要はなくて、WebIndexPluginを使うといい感じのindex.htmlを勝手に作ってくれる

const fuse = FuseBox.init({
    // 中略
    plugins: [WebIndexPlugin({ title: "My Page" })],
});

dist/index.htmlができている(インデントがめちゃくちゃなのはご愛嬌)。

これだとdiv#appがなくて動かない。 テンプレートからhtmlを作る方法もあるらしい。 scriptタグを入れてほしい箇所に$bundlesと書いたhtmlファイルを用意する。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>My Page</title>
</head>
<body>
  <div id="app"></div>
  $bundles
</body>
</html>
    plugins: [WebIndexPlugin({ template: "public/template.html" })],

これでいい感じに動く

cssを使う

CSSもいじる。最近は素のCSSよりもAltCSSがよく使われている。 私も業務ではStylusを使っている。 FuseBoxにはだいたいのAltCSS用のpluginがデフォルトで用意されている。

    plugins: [[StylusPlugin(), CSSPlugin()], WebIndexPlugin({ template: "src/public/template.html" })],

適当にstylusファイルを作る

// sub.styl
$myColor = #ff0000

// main.styl
@import "sub.styl";

h1
  color $myColor
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>My Page</title>
</head>
$css
<body>
  <div id="app"></div>
  $bundles
</body>
</html>

スタイルが適応される。これはcssファイルを書き出すのではなく、htmlにinlineでstyleを書き込むらしい(ココらへんはconfigで変えられそうだけど)。

まとめ

とりあえず動かすところまでやってみた。 TSのトランスパイル周りは結構いい感じな気がする。 cssとか、今回紹介しなかったtask runner周りが絡んでくるとconfigがごちゃごちゃしてくる感じがする。 ↑ドキュメントが少ないのもあいまって

速さに関して、体感時間的にはイライラするレベルで待たされることは無いとだけ言っておく。 (ココらへんを客観的に語るなら他のモジュールバンドラーとの比較検証が必須になるけど、面倒なので。サンプルで書いたコードも短いし)

You can’t perform that action at this time.