Secrets of the JavaScript Ninja 2nd 読書メモ 第6章 未来のための関数:ジェネレータとプロミス

Secrets of the Javascript Ninja

ジェネレータ(Generators)は特殊な種類の関数である。通常の関数が1つの値しか返さないのに対して、ジェネレータは、実行を断続的に中止しながら、複数の値を生み出すことができる。

プロミス(Promises)は、JavaScriptに組み込みのオブジェクトであり、非同期処理の取り扱いに適している。

ジェネレータ関数

ジェネレータ関数は以下のように定義する。functionキーワードの直後に*(アスタリスク)がついているのがジェネレータ関数の 目印である。ジェネレータ関数の中ではyieldというキーワードを使って値を生成できる。

function* WeaponGenerator() {
  yield 'Katana';
  yield 'Wakizashi';
  yield 'Kusarigama';
}

for (let weapon of WeaponGenerator()) {
  console.log(weapon); // Katana, Wakizashi, Kusarigama
}

イテレータオブジェクトでジェネレータを制御する

ジェネレータの呼び出しはジェネレータ関数の本文の実行を意味しない。その代わりに、イテレータオブジェクトが作成される。このオブジェクトを通してジェネレータとコミュニケーションをとることができる。

function* WeaponGenerator() {
  yield 'Katana';
  yield 'Wakizashi';
}

const weaponIterator = WeaponGenerator();

const weapon1 = weaponIterator.next();
console.log(weapon1); // { value: 'Katana', done: false }

const weapon2 = weaponIterator.next();
console.log(weapon2); // { value: 'Wakizashi', done: false }

const weapon3 = weaponIterator.next();
console.log(weapon3); // { value: undefined, done: true }

イテレータはジェネレータの実行をコントロールするために使用される。イテレータオブジェクトはnextメソッドを持っていて、このメソッドを通してジェネレータに値を要求できる。

const weapon = weaponIterator.next();

この呼び出しに対して、ジェネレータはyieldに達するまでコードを実行する。yieldに達すると、その時点での結果を返して、実行を中断する。この結果には、その時点での値(value)と、ジェネレータが完了したか(done)が含まれる。

現在の値の生成が終わると、ジェネレータはそこで中断する。

イテレータの繰り返し

イテレータのnextメソッドを使うことで、ジェネレータに新しい値を要求できる。また、ジェネレータが次の値を持っているか知るには、結果として取得できるオブジェクトのdoneプロパティを使用する。これらを利用すると、古典的なwhileループでイテレータから繰り返し値を取得できる。

function* WeaponGenerator() {
  yield 'Katana';
  yield 'Wakizashi';
}

const weaponsIterator = WeaponGenerator();
let item;
while (!(item = weaponsIterator.next()).done) {
  console.log(item.value); // Katana, Wakizashi
}

for-ofループは、このループのシンタックスシュガーである。

for (let item of WeaponGenerator()) {
  console.log(item);
}

別のジェネレーターを生成する

ジェネレータの実行を別のジェネレータに任せたい場合もある。

function* NinjaGenerator() {
  yield 'Hattori';
  yield 'Yoshi';
}

function* WarriorGenerator() {
  yield 'Sun tzu';
  yield* NinjaGenerator();
  yield 'Genghis Khan';
}

for (let warrior of WarriorGenerator()) {
  console.log(warrior); // Sun tzu, Hattori, Yoshi, Genghis Khan

yeild*演算子をイテレータに使うことで、別のジェネレータから値を生成できる。

ジェネレータとやりとりする

ジェネレータから値を生み出すだけでなく、ジェネレータに値を送ることもできる。

ジェネレータ関数の引数としてデータを送る

function* NinjaGenerator(action) {
  // 値を生成することで、ジェネレータは中間の計算結果を返すことができる
  // イテレータのnextメソッドを引数付きで呼ぶと、
  // ジェネレータにデータを送り返すことができる
  const imposter = yield `Hattori ${action}`;
  // nextに送られた値がyieldされた式の値になる
  console.log(`imposter: ${imposter}`);
  yield `Yoshi (${imposter}) ${action}`;
}

const ninjaIterator = NinjaGenerator('skulk');
const result1 = ninjaIterator.next();
console.log(result1.value); // Hattori skulk
const result2 = ninjaIterator.next('Hanzo'); // HanzoをNinjaGeneratorに
console.log(result2.value); // Yoshi (Hanzo) skulk

ジェネレータに例外を投げる

function* NinjaGenerator() {
  try {
    yield 'Hattori';
    console.error("The expected exception didn't occur");
  } catch (e) {
    console.log('Aha! We caught an exception');
  }
}

const ninjaIterator = NinjaGenerator();
const result1 = ninjaIterator.next();
console.log(result1); // Hattori

ninjaIterator.throw('Catch this!'); // Aha! We caught an exception

ジェネレータの内部を探求する

ジェネレータは小さなプログラムのようなものであり、複数の状態を遷移するステートマシンでもある。

  • 開始前(Suspended start):ジェネレータが作成された直後の状態
  • 実行中(Executing):ジェネレータのコードが実行されている状態
  • 生成停止中(Suspended yield):実行中に、yield式に到達し、処理が中断している状態
  • 完了(Completed):returnに到達するかコードを最後まで実行し、値の生成が完了した状態
function* NinjaGenerator() {
  yield 'Hattori';
  yield 'Yoshi';
}

const ninjaIterator = NinjaGenerator();
// 新しいジェネレータを作成する(Suspended start)

const result1 = ninjaIterator.next();
// ジェネレータの実行を開始し、Executingに移行する
// yield 'Hattori'; までを実行し、Suspended yield状態になる
// 新しいオブジェクト { value: 'Hattori', done: false } を返す

const result2 = ninjaIterator.next();
// ジェネレータの実行を再度開始し、Executingに移行する
// yield 'Yoshi'; までを実行し、Suspended yield状態になる
// 新しいオブジェクト { value: 'Yoshi', done: false } を返す

const result3 = ninjaIterator.next();
// ジェネレータの実行を再度開始し、Executingに移行する
// 実行するコードがないのでCompleted状態に移行する
// 新しいオブジェクト { value: undefined, done: true } を返す

ジェネレータの実行コンテキストを追跡する

function* NinjaGenerator(action) {
  yield `Hattori ${action}`;
  return `Yoshi ${action}`;
}

// ninjaIterator変数はNinjaGeneratorの実行コンテキストを参照する
const ninjaIterator = NinjaGenerator('skulk');

// 通常の関数と異なり、next()メソッドは実行コンテキストを生成しない
// その代わりに、NinjaGeneratorの実行コンテキストで処理を続行する
const result1 = ninjaIterator.next();
const result2 = ninjaIterator.next();

Promiseとやりとりする

Promiseは、ES2015で導入された、非同期処理を扱いやすくするための概念である。今は存在せず、将来手に入る値のプレースホルダーである。

// Promiseオブジェクトのコンストラクタを呼び出し、
// resolve(成功時に実行する関数)とreject(失敗時に実行する関数)をとる
// コールバック関数(executor)を渡す
const ninjaPromise = new Promise((resolve, reject) => {
  resolve('Hattori');
  // 失敗時の処理は reject(new Error('An error ...')) のように書く
});

// Promiseのthenメソッドを使って、成功時のコールバック関数を渡す
ninjaPromise.then(ninja => {
  console.log(ninja); // Hattori
});

Promiseに渡したコールバック関数(executor)は即時実行される。

単純なコールバックの問題を理解する

私たちは、長く時間のかかる処理によってユーザの操作を妨げないために、非同期処理を行っている。非同期処理のための最も初歩的な道具はコールバック関数である。

getJSON('data/ninjas.json', function() {
  // 処理
});

このようなタスクの際にはエラーの発生する可能性がある。コールバック関数の問題は、try-catchのような言語の組み込み機能を使えない点だ。以下のコードではエラーはキャッチできない。

try {
  getJSON('data/ninjas.json', function() {
    // 処理
  });
} catch(e) {
  // エラー処理
}

コールバック関数を呼び出すコードはイベントループの同じステップにはないことが普通だたら、例外処理でキャッチはできず、エラーは失われる。

そのため、Node.jsの世界では、コールバック関数にerrとdataの2つの引数が渡されるような慣習が確立されている。errにnullでない値が渡された場合はエラーが発生しているということだ。

コールバック関数の第一の問題点はエラーハンドリングの難しさである。

また、長い処理の後には、取得した結果を使って何か処理を行うことが多い。この結果、別の長い処理が開始されることもある。

getJSON('data/ninjas.json', function(err, ninjas) {
  getJSON(ninjas[0].location, function(err, locationInfo) {
    sendOrder(locationInfo, function(err, status) {
      // statusについての処理
    });
  });
});

このように、連続したステップを実施するのが難しいことが、コールバック関数の第二の問題点である。

さらに、最終的な結果を得るためのステップが、依存関係にない場合もある。このような場合、実行時間を最適化するためには、並行して処理を行うのが望ましい。しかし、並行した処理をコールバック関数で実装するのはかなりトリッキーである(書籍にはコード例があるが、長いので割愛)。コールバック関数の第三の問題点は、並行処理が難しいことである。

コールバック関数は、try-catchやループのような言語の組み込み機能と併せて使うことが難しいのも欠点である。

Promiseを使ってみる

Promiseは非同期に実行するタスクの結果のプレースホルダーである。まだ手に入れておらず、将来入手できる値を意味する。Promiseはいくつかの状態をもつ。

Promiseは、Pending状態から始まる。この状態をUnresolvedと呼ぶこともある。プログラムの実行が進み、Promiseのresolve関数が呼ばれると、PromiseはFullfilled状態に移行する。このとき、望んだ値を手に入れることができる。

一方、Promiseのreject関数が呼ばれるか、例外が発生し、それがPromiseの中でハンドルされなかった場合、PromiseはRejected状態に移行する。この場合、エラーの内容を取得できる。

FullfilledおよびRejectedをまとめてResolved状態とも呼ぶ。一度Resolvedに移行すると、Unresolvedに戻ることはない。

console.log('At code start');

const ninjaDelayedPromise = new Promise((resolve, reject) => {
  console.log('ninjaDelayedPromise executor');
  // 500ミリ秒後にresolve
  setTimeout(() => {
    console.log('Resolving ninjaDelayedPromise');
    resolve('Hattori');
  }, 500);
});

console.log('After creating ninjaDelayedPromise');

ninjaDelayedPromise.then(ninja => {
  console.log(`ninjaDelayedPromise resolve handled with ${ninja}`);
});

const ninjaImmediatePromise = new Promise((resolve, reject) => {
  console.log('ninjaImmediatePromise executor. Immediate resolve.');
  resolve('Yoshi');
});

ninjaImmediatePromise.then(ninja => {
  console.log(`ninjaImmediatePromise resolve handled with ${ninja}`);
});

console.log('At code end');

// ↑の実行結果は↓のようになる。Promiseのresolveは即時呼び出しではなく、次のイベントループの実行まで遅延する。
// At code start
// ninjaDelayedPromise executor
// After creating ninjaDelayedPromise
// ninjaImmediatePromise executor. Immediate resolve.
// At code end
// ninjaImmediatePromise resolve handled with Yoshi
// Resolving ninjaDelayedPromise
// ninjaDelayedPromise resolve handled with Hattori

Promiseの却下(reject)

Promiseの実行を失敗させる(rejectする)方法は2つある。明示的に reject メソッドを実行するか、ハンドルされない例外を発生させるかである。

const explicit = new Promise((resolve, reject) => {
  reject('明示的なreject');
});
explicit.then(() => console.log('実行されないはず'))
  .catch(error => console.error(error));

const implicit = new Promise((resolve, reject) => {
  // 未定義変数の書き換えによる例外が発生する
  undeclaredVariable += 1;
});
implicit.then(() => console.log('実行されないはず'))
  .catch(error => console.error(error));

このように、Promiseの中で発生したエラーを一括して処理する仕組みが存在するのがPromiseの大きな利点である。

Promiseの連鎖

then を実行すると新しいPromiseが返る。そのため、 then を繰り返すことでPromiseを連鎖させることができる。

fetch('ninjas.json')
  .then(response => response.json()) // レスポンスをJSON.parse()する
  .then(ninjas => console.log(ninjas)) // 取得したデータを出力
  .catch(error => console.error(error));

Promiseの連鎖の中で発生したエラーは、末尾の catch で一括でキャッチできる。

複数のPromise

依存関係のない複数の非同期処理を並行して実行することもできる。

Promise.all([
  fetch('ninjas.json'),
  fetch('mapInfo.json'),
  fetch('plan.json'),
]).then(results => {
  const ninjas = results[0];
  const mapInfo = results[1];
  const plan = results[2];
  console.log(ninjas, mapInfo, plan);
}).catch(console.error);

Promise.all メソッドはPromiseの配列をとり、全てのPromiseが成功したら返る新しいPromiseを作成する。いずれか1つのPromiseが失敗した場合は、Promiseは失敗する。

いくつかの非同期タスクのうち、最初に完了したものの結果だけを取得したい場合は、 Promsie.raceが利用できる。

Promise.race([
  fetch('yoshi.json),
  fetch('hattori.json),
  fetch('hanzo.json),
]).then(ninja => {
  console.log(ninja);
});

ジェネレータとPromiseを組み合わせる

非同期処理を行うコードをジェネレータの中に置くことで、シーケンシャルな非同期処理をエレガントに書くことができる。

function async(generator) {
  const iterator = generator();
  const handle = iteratorResult => {
    if (iteratorResult.done) return;

    const iteratorValue = iteratorResult.value;
    if (iteratorValue instanceof Promise) {
      iteratorValue.then(res => handle(iterator.next(res));
                   .catch(err => iterator.throw(err));
    }
  };

  try {
    handle(iterator.next());
  } catch (e) {
    iterator.throw(e);
  }
}

async(function* () {
  try {
    const ninjas = yield fetch('ninjas.json);
    const missions = yield fetch(ninjas[0].missionsUrl);
    console.log(missions);
fetch(missions[0].detailsUrl);
  } catch(e) {
    console.error(e);
  }
});

async関数

JavaScriptのasync関数は、前述したPromiseとジェネレータの組み合わせを言語機能化したものである。

(async function() {
  try {
    const ninjas = await fetch('ninja.json');
    const mission = await fetch(ninjas[0].missionsUrl);
    console.log(missions);
  } catch(e) {
    console.error(e);
  }
})();

asyncキーワードを使って非同期関数であることを宣言すると、この中ではawaitキーワードを使って、Promiseがresolveされるのを待つことができる。

『超速! Webページ速度改善ガイド』を読んだ

超速! Webページ速度改善ガイド ── 使いやすさは「速さ」から始まる (WEB+DB PRESS plus)

ここ最近、仕事でWebサイトのパフォーマンス改善をしているので、最新のベストプラクティスを押さえるために読みました。

パフォーマンス改善については、『ハイパフォーマンスWebサイト』という古典的名著があり、ここ最近の本としては『Webフロントエンド ハイパフォーマンスチューニング』などがあります。

パフォーマンス改善には、計測(ボトルネックの特定)=>改善というステップが存在します。本書は、計測方法や改善の方策が、具体的にわかりやすく解説されているのが特徴です。また、章立てが 基礎知識 => 調査と改善 で統一されているため、効率よく知識を身につけることができます。

Chrome DevTools はパフォーマンスの改善には欠かせないツールですが、高機能なぶんどのような機能があるか把握するのが大変です。本書では、ネットワーク・スクリプティング・ペインティング・メモリ等、様々なシーンでDevToolsを使って計測をする方法が解説されています。

また、改善方法も、小さなサンプルコードが載っているため、すぐに導入しやすいです。

Webサイトのパフォーマンスに悩んでいる人が、最初に読む本としてオススメ。


『React入門』を読んだ

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)

『React入門』は、JavaScriptの定番ライブラリ、React.jsの入門書です。

Reactの入門書は他にもありますが、本書が特に優れているのは、実務で使う上で必要なポイントが書かれている点です。

Reactはそれほど機能の多くないビューライブラリで、Reactと他のライブラリを適宜組み合わせることで、リッチなユーザインターフェースを実現します。

しかし、従来の入門書の多くは、React本体の入門で終わっています。ある程度の規模になると必要になる状態管理ライブラリや、リッチなインタフェースを実現するためのUIライブラリ等、Reactは本体以外にも学ぶべきことが色々あります。

本書では、他の入門書で扱っているようなReact本体の入門は前半3分の1くらいで、その後はReduxやUIライブラリ、ユニットテスト、作ったアプリケーションの公開方法(GitHub PagesとFirebase)からサーバサイドレンダリングまで、より実践的なトピックを手厚く紹介しています。

Vue.jsやAngular等、モダンなJavaScriptフレームワークの経験者であれば、本書でいきなりReactに入門できると思います。そうでない人は、もう1冊他の入門書を挟んでから本書を読んだ方が良いかもしれません。

Webフロントエンド技術を扱った本の宿命として、本書もそれほど賞味期限は長くないので、早めに読むのがオススメです。

Gutenberg コードリーディングメモ

Gutenberg本体のコードを読む上で、とっかかりになりそうな部分をメモ。

registerBlockType()

https://github.com/WordPress/gutenberg/blob/master/blocks/api/registration.js#L51

色々バリデーションが書いてあって、最終的には

return blocks[ name ] = settings;

でローカル変数 blocks に第1引数(name)をキー、第2引数(settings)を値として追加する。

setAttributes()

BlockListBlock というブロックで定義されている。

https://github.com/WordPress/gutenberg/blob/master/editor/components/block-list/block.js#L184-L203

setAttributes( attributes ) {
  const { block, onChange } = this.props;
  const type = getBlockType( block.name );
  onChange( block.uid, attributes );

  const metaAttributes = reduce( attributes, ( result, value, key ) => {
    if ( get( type, [ 'attributes', key, 'source' ] ) === 'meta' ) {
      result[ type.attributes[ key ].meta ] = value;
    }

    return result;
  }, {} );

  if ( size( metaAttributes ) ) {
    this.props.onMetaChange( {
      ...this.props.meta,
      ...metaAttributes,
    } );
  }
}

ここではBlockListBlockがpropsに持っているonChange()メソッドが重要。

https://github.com/WordPress/gutenberg/blob/master/editor/components/block-list/block.js#L465

onChange( uid, attributes ) {
  dispatch( updateBlockAttributes( uid, attributes ) );
},

onChangeはStoreをdispatchする。属性の設定処理の本体はreducerにある。

https://github.com/WordPress/gutenberg/blob/master/editor/store/reducer.js#L129

case 'UPDATE_BLOCK_ATTRIBUTES':
  // Ignore updates if block isn't known
  if ( ! state[ action.uid ] ) {
    return state;
  }

  // Consider as updates only changed values
  const nextAttributes = reduce( action.attributes, ( result, value, key ) => {
    if ( value !== result[ key ] ) {
      // Avoid mutating original block by creating shallow clone
      if ( result === state[ action.uid ].attributes ) {
        result = { ...result };
      }

      result[ key ] = value;
    }

    return result;
  }, state[ action.uid ].attributes );

  // Skip update if nothing has been changed. The reference will
  // match the original block if `reduce` had no changed values.
  if ( nextAttributes === state[ action.uid ].attributes ) {
    return state;
  }

  // Otherwise merge attributes into state
  return {
    ...state,
    [ action.uid ]: {
      ...state[ action.uid ],
      attributes: nextAttributes,
    },
};

Gutenbergのコードを読むなら、ある程度Flux(およびその実装であるRedux)については理解しておく必要がある。

Gutenberg Handbook 読書メモ (3) Reference

↓の続き。今回はリファレンスをざっくり。

Gutenberg Handbook 読書メモ (1)
Gutenberg Handbook 読書メモ (2)

Attributes

https://wordpress.org/gutenberg/handbook/reference/attributes/

ソース(source)

属性のsourceはブロックの属性の値の取得方法を定義する。

それぞれのsourceは、第1引数にセレクタを取る(オプショナル)。セレクタが指定されると、sourceは、ブロック内で対応する要素を取得する。

内部では、sourcehpqライブラリのスーパーセットとなっている。

attribute

attributeを使うと、マークアップから属性を取得できる。

以下の例では、imgタグからsrc属性を抜き出している。

{
    url: {
        source: 'attribute',
        selector: 'img',
        attribute: 'src',
    }
}
// { "url": "https://lorempixel.com/1200/800/" }

text

textを使うと、マークアップの内側のテキストを取得できる。

{
    content: {
        source: 'text',
        selector: 'figcaption',
    }
}
// { "content": "The inner text of the figcaption element" }

html

htmlを使うとマークアップの内側のHTMLを取得できる。

{
    content: {
        source: 'html',
        selector: 'figcaption',
    }
}
// { "content": "The inner text of the <strong>figcaption</strong> element" }

children

childrenを使うとマッチした要素の子ノードを取得できる。Editableコンポーネントとの組み合わせでよく使われる。

{
    content: {
        source: 'children',
        selector: 'p'
    }
}
// {
//   "content": [
//     "Vestibulum eu ",
//     { "type": "strong", "children": "tortor" },
//     " vel urna."
//   ]
// }

query

queryを使うと、複雑なセレクタでマークアップから値を取得できる。

以下では、それぞれのimg要素からurlalt属性を抜き出している。

{
    images: {
        source: 'query'
        selector: 'img',
        query: {
            url: { source: 'attribute', attribute: 'src' },
            alt: { source: 'attribute', attribute: 'alt' },
        }
    }
}
// {
//   "images": [
//     { "url": "https://lorempixel.com/1200/800/", "alt": "large image" },
//     { "url": "https://lorempixel.com/50/50/", "alt": "small image" }
//   ]
// }

Meta

記事のメタデータ(post meta)から属性を取得することもできる。

attributes: {
    author: {
        type: 'string',
        source: 'meta',
        meta: 'author'
    },
},

このようにすると、ブロックからメタ属性に対して読み書きできるようになる。

edit( { attributes, setAttributes } ) {
    function onChange( event ) {
        setAttributes( { author: event.target.value } );
    }

    return <input value={ attributes.author } onChange={ onChange } />;
},

検討事項

デフォルトでは、メタフィールドは記事のオブジェクトのメタデータからは除外されている。この制限は、フィールドを明示的に可視化することで回避可能である。

function gutenberg_my_block_init() {
    register_meta( 'post', 'author', array(
        'show_in_rest' => true,
    ) );
}
add_action( 'init', 'gutenberg_my_block_init' );

さらに、WordPressは以下のようなデフォルト設定になっている点に注意が必要。

  • メタデータをユニークなものとしては扱わず、値の配列を返す
  • データを文字列として扱う

どちらの振る舞いも望ましくないなら、register_meta関数の呼び出しを以下のようにすることで、補うことができる。

function gutenberg_my_block_init() {
    register_meta( 'post', 'author_count', array(
        'show_in_rest' => true,
        'single' => true,
        'type' => 'integer',
    ) );
}
add_action( 'init', 'gutenberg_my_block_init' );

最後に、属性に値を設定する際はデータの型に注意が必要である。

function onChange( event ) {
    props.setAttributes( { authorCount: Number( event.target.value ) } );
}

Blocks support by themes

https://wordpress.org/gutenberg/handbook/reference/theme-support/

基本的に、ブロックのスタイルのカスタマイズはテーマ側のCSSで行うことができる。しかし、一部の機能はテーマ側での明示的な有効化(オプトイン)が必要になる。

function mytheme_setup_theme_supported_features() {
    add_theme_support( 'gutenberg', array(
        'wide-images' => true,
    ) );
}

add_action( 'after_setup_theme', 'mytheme_setup_theme_supported_features' );

Meta Boxes

https://wordpress.org/gutenberg/handbook/reference/meta-box/

※色々書いているけど一旦飛ばす

Glossary

https://wordpress.org/gutenberg/handbook/reference/glossary/

用語集

  • 属性ソース(Attribute sources): ブロックの属性の形を記述するオブジェクト
  • 属性(Attributes): ブロックの現在の状態を表現するオブジェクト
  • ブロック(Block): マークアップの単位を表す抽象的な用語。ブロックは組み合わせて使用され、Webページのコンテンツやレイアウトを構成する。
  • ブロックの名前(Block name): ブロック型のユニークな識別子
  • ブロック型(Block type): ブロックの振る舞いの定義
  • 動的ブロック(Dynamic block): 保存時にコンテンツが確定せず、記事が表示されるタイミングで描画されるブロック
  • Editable: リッチエディタ機能を提供するコンポーネント
  • Inspector: ブロック設定用のUIコンポーネント
  • 記事の設定(Post settings): 記事編集画面のサイドバーのこと
  • シリアライゼーション(Serialization): 記事の保存時にブロックの属性オブジェクトをHTMLマークアップに変換するプロセス
  • 静的ブロック(Static Block): 記事の保存時に内容が確定するブロック
  • TinyMCE: リッチエディタ(WYSIWYGエディタ)
  • ツールバー(Toolbar): ブロックの上に表示される一連のボタンのこと

Design Priciples

https://wordpress.org/gutenberg/handbook/reference/design-principles/

抽象的な話なので割愛

History

https://wordpress.org/gutenberg/handbook/reference/history/

内容がないので割愛

Coding Guidelines

https://wordpress.org/gutenberg/handbook/reference/coding-guidelines/

Gutenbergのコードに関することなので割愛。

Testing Overview

https://wordpress.org/gutenberg/handbook/reference/testing-overview/

JSはnpm testでテストが実行できる。

Jestのスナップショットテストを使っていて、スナップショットテストを使う際のベストプラクティス等について言及してある。

FAQ

https://wordpress.org/gutenberg/handbook/reference/faq/

いずれ公式で訳されるはず。