WordPress5.0から採用されるGutenbergだが、決定的に新しい概念として、「ブロック」というものがある。ブロックの正体は <!--wp:xxx--><!--/wp:xxx-->
というHTMLコメントで囲まれた独自タグのようなものだが、これは独自に追加することができる。ショートコードのようなものだと思ってもらっていい。
これにかなり近い機能として、現時点でも使えるプラグインに Shortcake というものがあり、筆者も便利に使っていたのだが、こちらはBackbone製でおいそれとカスタマイズできるものではないので、公式採用にいたらなかったのだろう。
なにはともあれ、Gutenbergをカスタマイズして使いこなすにあたって避けられられないのがブロックである。今回はReactがなんなのかをよくわからないまま、とりあえずコピペでブロックを作ってみたい。
Gutenbergの拡張ドキュメント
日本語で書かれた資料としては、明日(3/1)開催のGutenberg Meetup(筆者も登壇する)の主催者である宇都宮さんのQiita記事に詳しい。ReactやBabelなどのフロントエンドに詳しい人向けの内容なので、本稿を読んで物足りなかった人は読んでみてほしい。
もっともたやすくカスタムブロックを追加できるのは、Github上のGutenbergリポジトリである。Gutenbergのドキュメントは非常によく整備されており、各フォルダのREADMEを読むだけで実装は可能だ。WordPressの文化をよく理解した人たちが作っているので、いきなり npm install
を求めることはなく、素のJavascriptだけでそれなりに動くよう書かれている。
ステップ1. エントリーポイントの作成
さて、まずは準備段階として、プラグインを作成しよう。プラグインフォルダに capital-block/capital-block.php
というファイルを作成し、こんな内容を書く。
<?php /** * Plugin Name: Capital Block * Plugin URI: https://capitalp.jp/ * Description: Original custom blocks for Gutenberg. * Version: 0.8.0 * Author: Takahashi_Fumiki * Author URI: https://capitalp.jp * License: GPL-3.0 or later * License URI: https://www.gnu.org/licenses/gpl-3.0.html * Text Domain: capitalb * Domain Path: languages */ defined( 'ABSPATH' ) || die();
これで何もしないプラグイン Capital Block ができたので、とりあえず有効化しよう。これで入り口が完成した。
ステップ2. Javascriptの読み込み
GutenbergはJavascriptが大半を占めているので、Javascriptを読み込ませよう。まずは capitalp-block/block.js
というファイルを作成し、先ほど作成した capitalp-block.php
に読み込みの指示を書く。
add_action( 'enqueue_block_editor_assets', function() { wp_enqueue_script( 'myplugin-block', plugins_url( 'block.js', __FILE__ ), [ 'wp-blocks', 'wp-element' ] ); } );
これは先ほど紹介したREADMEに書いてあるコードをほとんどコピペしたものだ。ポイントは下記の通り。
- Javascriptの読み込みは普通
wp_enqueue_scripts
あるいはadmin_enqueue_scripts
だが、Gutenbergにおいてはenqueue_block_editor_assets
となっている。 wp-blocks
およびwp-element
はカスタムブロックが依存するファイル。wp-element は実のところ、Reactへのショートハンドである。なぜReactと直接参照しないかというと、WordPressよりも先にReactがなくなる、あるいはReactを使わなくなる可能性があるので、抽象化しているわけだ。
これでエディターを開くと、block.js
が読み込まれるはずだ。読み込まれたか不安な人は、alertでも書いておけば良い。
ステップ3. ブロックを記述する
さて、ブロックを記述するのだが、とりあえずREADMEのコードをコピペしてみよう。これは Random Image というブロックで、画像のプレースホルダーサービス lorempixel.com からジャンルを選んだ上でランダムに画像を表示するブロックのようだ。
( function( blocks, element ) { var el = element.createElement, source = blocks.source; function RandomImage( props ) { var src = 'http://lorempixel.com/400/200/' + props.category; return el( 'img', { src: src, alt: props.category } ); } blocks.registerBlockType( 'myplugin/random-image', { title: 'Random Image', icon: 'format-image', category: 'common', attributes: { category: { type: 'string', source: 'attribute', attribute: 'alt', selector: 'img', } }, edit: function( props ) { var category = props.attributes.category, children; function setCategory( event ) { var selected = event.target.querySelector( 'option:checked' ); props.setAttributes( { category: selected.value } ); event.preventDefault(); } children = []; if ( category ) { children.push( RandomImage( { category: category } ) ); } children.push( el( 'select', { value: category, onChange: setCategory }, el( 'option', null, '- Select -' ), el( 'option', { value: 'sports' }, 'Sports' ), el( 'option', { value: 'animals' }, 'Animals' ), el( 'option', { value: 'nature' }, 'Nature' ) ) ); return el( 'form', { onSubmit: setCategory }, children ); }, save: function( props ) { return RandomImage( { category: props.attributes.category } ); } } ); } )( window.wp.blocks, window.wp.element );
さて、このコードを読むためにいくつか説明をしよう。まず、冒頭の el
である。おまえはいったいなんなのだ?
これは React.createElement
というメソッドのショートハンドで、Reactはこのメソッドを使ってDOMオブジェクトのようなものを作成する。 <p></p>
と書く代わりに、 React.createElement('p')
と書くわけだ。これを返すと、Reactはpタグを描画する。Javascriptに親しんだ人なら、 document.createElement('div')
と書いたことがあるかもしれないが、似たようなものだ。今回のケースでは、毎回 wp.element.createElement
と書くのがめんどくさいので、 var el = element.createElement
としている。
el( 'option', { value: 'sports' }, 'Sports' )
のような表記は、<option value="sports">Sports</option>
のようなHTMLとして描画される。ただし、HTMLとまったくイコールなわけではない。
続いて、 blocks.registerBlockType
である。ここで新しいブロックを登録しているわけだ。引数は ブロックのID(myplugin/random-image
)と設定である。設定はオブジェクトになっており、属性を持っている。 title
やicon
については特に説明はいらないだろう。category
はGutenbergのブロックがリストに並ぶときに使われるようだ。
edit
はブロックを編集している時の中身を表示するメソッド、そしてsave
は保存すべき内容を返すメソッドである。このコードでは、RandomImage
というメソッドを定義することで、edit
とsave
の共通部分を切り出している。
props
という変数は、おそらくそのブロックが持つ設定値を保持するプロパティだ。Reactでは、このprops
を持ち回ることで描画と保存を一方向同期することができる。ポイントとしては、 props.category = 'animal'
とするのはダメで、 props.setAttributes({category: 'animal'})
としなければならない。やや冗長ではあるが、このように書くことで、レンダリングが自動的に反映される仕組みになっている。
結論
さて、ほぼコピペではあるが、カスタムブロックを追加することができた。思ったほど難しくはない印象だ。もちろん、動的なブロック(ex. ログインしているユーザーにだけ内容を表示)も作ることはできるのだが、本日はここら辺で終わりとしよう。続きは Gutenberg Meetupでお届けする予定だ。
有料会員向けに、ブロックを実際にカスタマイズするところを撮影した動画があるので、そちらをご覧いただきたい。また、Capital Pの有料会員は、管理画面から「聖書」というカスタム投稿タイプをGutenbergで編集できるようにしているので、使ってみてほしい。
追記
Gutenberg Meetup Vol.1で発表した資料を添付しておく。セッションの内容はだいたいこの記事と同じである。