Gutenberg

解体新書(初稿)

Ryo Utsunomiya(@ryo511)

WordCamp Tokyo 2018

2018-09-15

自己紹介

  • 宇都宮 諒(うつのみや りょう)​
  • 株式会社 一休 宿泊事業本部
  • 職種は「フロントエンドエンジニア」
  • 主なお仕事
  • ​WordPress歴
    • ​ユーザとして4年くらい
    • 開発者として1年くらい
  • ​Twitter: @ryo511
  • 個人ブログ( https://ryo511.info/ )

Agenda

  • Gutenbergはどういう技術で成り立っているのか
  • Gutenbergを拡張する(プラグインを作る)ためにはどういう技術が必要か

第1部

Gutenbergの

技術的な成り立ち

Gutenbergの成分表示

  • GutenbergはWordPressプラグイン
  • 主要成分はJavaScript
  • PHPもそれなりに

JavaScript

  • Webブラウザ上で動作するプログラミング言語
  • JavaScriptの仕様はECMA Internationalという団体で策定されている
  • この仕様をECMAScriptと呼ぶ
  • 従来はES 3、ES 5のようなバージョン番号が使われていた
  • 2015年のES 6以降は毎年新バージョンが出るようになったため、発表年で呼ばれるように
  • 最新の仕様はES2018

ECMAScriptのブラウザ対応

  • ECMAScriptの仕様化には、ブラウザでの実装状況も考慮される
    
  • ES2018の主要機能は、現在の主要なブラウザの最新版では動作する
  • ただし、IE11はES 5で更新が止まっている

JavaScriptライブラリとnpm

  • Gutenbergでは、JavaScriptのライブラリ管理にはnpm(Node Package Manager)を使用している
  • npmはJavaScriptライブラリ管理のデファクトスタンダード
  • npmでは、依存しているライブラリをpackage.jsonというファイルに列挙する
  • package.jsonを見ることで、Gutenbergがどのようなライブラリを使っているかわかる

Gutenbergのpackage.json

  • dependencies(動作に必要なライブラリ)と、devDependencies(開発に必要なライブラリ)に分かれる
    
  • dependencies
    • React.js
    • jQuery
    • その他、@wordpress 系のライブラリ群も
  • devDependencies
    • Babel
    • webpack
    • その他沢山

React.js

  • Gutenbergの中心を成すライブラリ
  • Facebookが開発した、リッチなUIを開発するためのJavaScriptライブラリ
  • 現在最も人気のあるJavaScriptライブラリの1つ
    • npmでは2017年に5900万回ダウンロードされた
    • 参考値:jQuery 4600万、Angular 1100万、Vue.js 800万

React.jsはコンポーネント指向

  • React.jsは、リッチな=複雑なUIを、効率よく開発するためのライブラリ
  • Reactでは、コードを「コンポーネント」という単位で管理する
  • コンポーネントは、振る舞いと見た目を併せ持つ
  • Gutenbergでは、それぞれの「ブロック」が、Reactコンポーネントとして実装されている

React.jsのサンプルコード

// ES2015のclass構文
class HelloMessage extends React.Component {
  render() {
    return (
      // JavaScriptの中にHTMLを書く(JSX)
      <div>
        Hello {this.props.name}
      </div>
    );
  }
}

// #app というidを持つ要素にReactアプリケーションを描画
ReactDOM.render(
  <HelloMessage name="Taylor" />,
  document.getElementById("app")
);

最大の特徴は、JavaScriptの中にHTMLを書くこと

JSX

// JavaScript関数の戻り値にHTMLを書く
function Hello() {
  return <div>Hello World</div>
}

// HTMLの中にJavaScriptの変数を埋め込むことができる
function Hello(name) {
  return <div>Hello {name}</div>
}

// 関数呼び出しも可能
function Hello(name) {
  return <div>{greeting(name)}</div>
}
  • React.jsは、JSXによって、高い表現力を実現している
  • しかし、JSXはブラウザでそのまま実行できるコードではない…

Babel

  • JavaScriptトランスパイラ
    
  • JSX => JavaScriptの変換のほか、新しいバージョンのJavaScript(ES2018等)を、古いバージョンのJavaScript(ES5)に変換することもできる
  • React.jsを使った開発では、ほぼ必須のソフトウェア
  • もちろん、Gutenbergでも使っている

JSXトランスパイル

// ESNext + JSX
const hw = <h1>Hello World</h1>;


// ES5(React)
var hw = React.createElement("h1", null, "Hello World");

// ES5(Gutenberg)
var hw = wp.element.createElement("h1", null, "Hello World");
  • BabelはJSXをES5に変換する
  • JSXのHTMLタグは、React.createElement() の呼び出しと同じ
  • GutenbergはReact.createElement() をラップした独自メソッドを用意している

createElementを手書きする

// JSX
<div className="container">
    <h1>Header1</h1>
    <p>Paragraph</p>
</div>

// ES5
var el = wp.element.createElement;
el(
    'div',
    { className: 'container' },
    [
        el( 'h1', null, 'Header1' ),
        el( 'p', null, 'Paragraph' ),
    ],
);
  • JSXなしのReact開発も可能
  • ただし、記法が煩雑なので非推奨

Babelとセットで使う、

webpackというものがあると

聞いたんだけど… 🤔

webpack

  • webpackは、ES2015のモジュールシステムを、レガシーブラウザ(IE11等)でも動くようにするためのツール
  • Babelのようなトランスパイラとセットで使うことが多い
  • Gutenbergでもwebpackを使っている

モジュールシステム

  • jsファイルの外側から、ライブラリ等を読み込んで使用できるようにするための構文
    
  • import/exportというキーワードを使う
  • PHPのrequire/includeとだいたい同じ
  • モダンブラウザ(Chrome, Safari, Firefox, Edge等)では動く
  • IE11では動かないので、webpackを使って動作する形に変換する必要がある

ここまでのおさらい

  • GutenbergはWordPressプラグインとして実装され、
  • npmを使ってライブラリを管理している
  • React.jsを使ってリッチなUIを実現している
  • Babelを使ってReactのJSXをブラウザで動くコードに変換している
  • webpackを使ってモジュールシステムがIE11でも動くようにしている

ところで、

Babelとかwebpackとか、

絶対に覚えないとダメ? 😨

最低限必要なのはReactだけ

  • webpack + Babelのビルドの仕組み作りはそれなりに複雑で、学習コストがかかる
    
  • しかし、開発効率のためにはJSXを使えた方がよい…
  • create-guten-block等、Gutenbergの開発環境をセットアップしてくれるツールが出てきている。初めはツールの助けを借りて、徐々に自分でも設定できるようにしておけばOK

第2部

Gutenbergのプラグイン開発

Gutenbergの基本はブロック

  • 記事の編集に使用する、再利用可能な部品

GutenbergのブロックAPI

  • 自分で定義したブロックを登録し、Gutenberg
    で利用可能にできる
  • 段落(paragraph)や画像(image)のようなコアブロックも、ブロックAPIを利用している
  • コアブロックは packages/block-library の下にあるので、一度は覗いてみるのをおすすめ

ブロックの登録

import HelloBlock from './HelloBlock';

wp.blocks.registerBlockType( 'my-block/hello', {
	title: 'Greeting',
	icon: 'cart',
	category: 'common',
        // ブロックがもつメタデータを定義
	attributes: {
		// 省略
	},
        // ブロックの初回表示・編集時の動作を定義
	edit( { attributes, setAttributes } ) {
		return (
			<HelloBlock
				attributes={ attributes }
				setAttributes={ setAttributes }
			/>
		);
	},
        // ブロックを保存する際の動作を定義
	save( { attributes } ) {
		// 省略
	},
} );

ブロックはReactで作る

/* global React */

export default class HelloBlock extends React.Component {
	render() {
		const { name } = this.props.attributes;
		return <div>{ name }</div>;
	}
}
  • たとえば、コアの段落ブロックはParagraphBlockというReactコンポーネントとして実装されている

記事データの保存形式(1)

  • WordPressの記事データはwp_postsテーブルのpost_contentというカラムに保存される
    
  • クラシックエディタでは、保存されるのはHTML文字列
  • Gutenbergでも、保存先や保存形式は同じ
  • ただし、Gutenbergの場合、メタデータをコメントに埋め込む

記事データの保存形式(2)

<!-- wp:core/heading -->
<h2>What is Gutenberg?</h2>
<!-- /wp:core/heading -->

<!-- wp:core/cover-image {"url":"http://localhost:8888/wp-content/uploads/2017/12/Screen-Shot-2017-12-06-at-23.34.27.png","id":45} -->
<section class="wp-block-cover-image has-background-dim" style="background-image:url(http://localhost:8888/wp-content/uploads/2017/12/Screen-Shot-2017-12-06-at-23.34.27.png)">
</section>
<!-- /wp:core/cover-image -->

<!-- wp:core/paragraph -->
<p>Gutenberg is a new UX of WordPress editor.</p>
<!-- /wp:core/paragraph -->

Gutenbergブロックのメタデータは

HTMLコメントの中にJSON形式で埋め込まれる

記事データの保存形式(3)

<h2>What is Gutenberg?</h2>
<section class="wp-block-cover-image has-background-dim" style="background-image:url(http://localhost:8888/wp-content/uploads/2017/12/Screen-Shot-2017-12-06-at-23.34.27.png)">
</section>
<p>Gutenberg is a new UX of WordPress editor.</p>
  • 記事データとして(the_post()などで)出力されるHTMLからは、メタデータ(コメント)は取り除かれる
  • 記事のレンダリングはサーバサイド(PHP)で行われる。Reactは記事のレンダリングには使わない。

ブロックのライフサイクル

  1. edit() で初期状態を出力(保存されたコンテンツを編集する際は、以前の状態の復元)
  2. ユーザがコンテンツを編集する
  3. save() の結果をpost_contentに保存

Attributes API

  • Gutenbergのブロックのメタデータのことをattributesと呼ぶ
    • 例:段落ブロックの文字サイズ等
  • ユーザがブロックに行った設定を反映するには、edit()の中でsetAttributes()メソッドを使用する
  • save() の中でattributesを参照して、最終的に保存するコンテンツを決定する
  • どのようなattributesを定義して、保存するか、がGutenbergのブロック開発のキモ

Gutenbergプラグインの構造

  • 通常のWordPressプラグインと変わらない
  • enque_block_editor_assets アクションでブロックのJSを読み込む
<?php
// index.php

/**
 * Register Block JavaScript file.
 */
add_action( 'enqueue_block_editor_assets', function () {
	wp_enqueue_script(
		'my-block',
		plugins_url( 'build/index.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element' )
	);
} );

まとめ

  • Gutenbergは様々なJavaScriptライブラリによって成り立っている
  • React.jsを作った、モダンなJavaScriptアプリケーションとして実装されている
  • モダンなWebフロントエンドの技術を身につけることが、Gutenberg対応プラグイン開発の基盤になる

参考文献

  • Gutenberg Handbook
  • React.js公式ドキュメント
  • Babel公式ドキュメント
  • webpack公式ドキュメント

【初稿】Gutenberg解体新書

By Ryo Utsunomiya

【初稿】Gutenberg解体新書

WordCamp Tokyo 2018セッションスライド初稿

  • 2,099