browserify, babelify, watchify が遅い時の注意点

browserify, babelify, watchify

browserify, babelify, watchifyを組み合わせて使う場合,transform の位置に注意しないと(ファイルの変更をせずに書き込みした場合でも)トランスパイルの度に遅くなっていきます.

watchify\babelify bundle() 内で transform(babelify) transform: [babelify]
plugin: [watchify] 遅い 速い
watchify() 遅い 速い

遅くなるパターン

watchify でファイル監視を行う場合,bundler.on('update', bundle); のように update イベントでトランスパイルを行うように設定します.
このとき,transformbundle() 内で実行してしまうと,トランスパイルの度に遅くなっていきます.

plugin: [watchify]bundle() 内で transform(babelify) で計測

// sample1.js
const babelify = require('babelify');
const browserify = require('browserify');
const watchify = require('watchify');
const fs = require('fs');

const bundler = browserify('src/js/index.js', {
  cache: {},
  packageCache: {},
  plugin: [watchify],
});

const bundle = () => {
  return bundler.transform(babelify) // <- bundle() 内に transform(babelify) にあるため遅くなる
    .bundle()
    .pipe(fs.createWriteStream('dest/js/bundle.js'));
};
bundler.on('log', (msg) => console.log(msg));
bundler.on('update', bundle);

bundle();

// 計測
let count = 0;
let max = 10;
const code = `
import add from './add';
console.log(add(1, 2));
`;
const writeFile = () => {
  setTimeout(function() { 
    fs.writeFileSync('src/js/index.js', code, 'utf8');
    count += 1;
    if (count < max) {
      writeFile();
    }
  }, 1000);
};
writeFile();
$ node sample1.js
911 bytes written (0.21 seconds)
913 bytes written (0.06 seconds)
913 bytes written (0.06 seconds)
913 bytes written (0.07 seconds)
913 bytes written (0.09 seconds)
913 bytes written (0.11 seconds)
913 bytes written (0.13 seconds)
913 bytes written (0.15 seconds)
913 bytes written (0.21 seconds)
913 bytes written (0.21 seconds)
913 bytes written (0.22 seconds)
^C

watchify()bundle() 内で transform(babelify) で計測

// sample2.js
const babelify = require('babelify');
const browserify = require('browserify');
const watchify = require('watchify');
const fs = require('fs');

const bundler = watchify(browserify('src/js/index.js', {
  cache: {},
  packageCache: {},
}));

const bundle = () => {
  return bundler.transform(babelify) // <- bundle() 内に transform(babelify) にあるため遅くなる
    .bundle()
    .pipe(fs.createWriteStream('dest/js/bundle.js'));
};

bundler.on('log', (msg) => console.log(msg));
bundler.on('update', bundle);

bundle();

// 計測
let count = 0;
let max = 10;
const code = `
import add from './add';
console.log(add(1, 2));
`;
const writeFile = () => {
  setTimeout(function() { 
    fs.writeFileSync('src/js/index.js', code, 'utf8');
    count += 1;
    if (count < max) {
      writeFile();
    }
  }, 1000);
};
writeFile();
$ node sample2.js
911 bytes written (0.21 seconds)
913 bytes written (0.06 seconds)
913 bytes written (0.06 seconds)
913 bytes written (0.08 seconds)
913 bytes written (0.09 seconds)
913 bytes written (0.11 seconds)
913 bytes written (0.13 seconds)
913 bytes written (0.15 seconds)
913 bytes written (0.20 seconds)
913 bytes written (0.24 seconds)
913 bytes written (0.25 seconds)
^C

遅くならないパターン

transform の指定位置を bundle() 外にすると,トランスパイルの度に遅くなりません.

plugin: [watchify]transform: [babelify] で計測

// sample3.js
const babelify = require('babelify');
const browserify = require('browserify');
const watchify = require('watchify');
const fs = require('fs');

const bundler = browserify('src/js/index.js', {
  cache: {},
  packageCache: {},
  plugin: [watchify],
  transform: [babelify], // <- bundle() 外で transform: [babelify] を指定すれば遅くならない
});

// もしくは,bundle() 外で transform(babelify) でも良い
// bundler.transform(babelify);

const bundle = () => {
  return bundler.bundle()
    .pipe(fs.createWriteStream('dest/js/bundle.js'));
};

bundler.on('log', (msg) => console.log(msg));
bundler.on('update', bundle);

bundle();

// 計測
let count = 0;
let max = 10;
const code = `
import add from './add';
console.log(add(1, 2));
`;
const writeFile = () => {
  setTimeout(function() { 
    fs.writeFileSync('src/js/index.js', code, 'utf8');
    count += 1;
    if (count < max) {
      writeFile();
    }
  }, 1000);
};
writeFile();
$ node sample3.js
911 bytes written (0.25 seconds)
911 bytes written (0.02 seconds)
911 bytes written (0.03 seconds)
911 bytes written (0.03 seconds)
911 bytes written (0.01 seconds)
911 bytes written (0.02 seconds)
911 bytes written (0.01 seconds)
911 bytes written (0.02 seconds)
911 bytes written (0.01 seconds)
911 bytes written (0.01 seconds)
911 bytes written (0.01 seconds)
^C

watchify()transform: [babelify] で計測

// sample4.js
const babelify = require('babelify');
const browserify = require('browserify');
const watchify = require('watchify');
const fs = require('fs');

const bundler = watchify(browserify('src/js/index.js', {
  cache: {},
  packageCache: {},
  transform: [babelify], // <- bundle() 外で transform: [babelify] を指定すれば遅くならない
}));

// もしくは,bundle() 外で transform(babelify) でも良い
// bundler.transform(babelify);

const bundle = () => {
  return bundler.bundle()
    .pipe(fs.createWriteStream('dest/js/bundle.js'));
};

bundler.on('log', (msg) => console.log(msg));
bundler.on('update', bundle);

bundle();

// 計測
let count = 0;
let max = 10;
const code = `
import add from './add';
console.log(add(1, 2));
`;
const writeFile = () => {
  setTimeout(function() { 
    fs.writeFileSync('src/js/index.js', code, 'utf8');
    count += 1;
    if (count < max) {
      writeFile();
    }
  }, 1000);
};
writeFile();
$ node sample4.js
911 bytes written (0.22 seconds)
911 bytes written (0.03 seconds)
911 bytes written (0.03 seconds)
911 bytes written (0.02 seconds)
911 bytes written (0.02 seconds)
911 bytes written (0.02 seconds)
911 bytes written (0.01 seconds)
911 bytes written (0.02 seconds)
911 bytes written (0.01 seconds)
911 bytes written (0.01 seconds)
911 bytes written (0.01 seconds)
^C

まとめ

transform: [babelify] を用いるか,bundle() の外で bundler.transform(babelify) を指定しましょう.

React での「...」(ドット3つ)の記法

React では以下のような「...」(ドット3つ)の記法を時々見かけます.

render() {
  return <Counter {...this.state} />
}

Spread Attributes

これは,Spread Attributes という JSX の記法です.

facebook.github.io

子の Component へ props を渡す場合,Spread Attributes を用いると,以下のように書き換えられます.

1つずつ渡す場合

const props = { foo: "foo", bar: "bar" };
render() {
  return <Child foo={props.foo} bar={props.bar} />
}

Spread Attributes を用いて渡す場合

const props = { foo: "foo", bar: "bar" };
render() {
  return <Child {...props} />
}

ES.next でのスプレッド演算子

「...」(ドット3つ)の記法は,スプレッド演算子として ES2015 で Array に導入されています.

let ary1 = ["first", "second"];
let ary2 = [...ary1, "third"]; // ["first", "second", "third"]

ES.next では,「...」(ドット3つ)の記法を,Object で使える Object Rest/Spread Properties が提案されています.

let obj1 = { x: 1, y: 2 };
let obj2 = { ...obj1, z: 3 }; // { x: 1, y: 2, z: 3 }

これを用いると,以下のような Object.assign() を用いていたコードが簡潔に書けるようになります.

let obj1 = { x: 1, y: 2 };
let obj2 = Object.assign({}, obj1, { z: 3 });

babel で用いる場合は,Stage 2 presetObject rest spread transform を用いてください.