フォーマット
「ブロックエディターの投稿」(block editor post) は、投稿の、ブロックによる一表現です。各ブロックが何で、そのブロックの重要なデータが何かを、意味的な一貫性をもって記述したコレクションです。メモリーの中だけに存在します。ちょうど活版印刷における枠であり、活字を追加したり並べ替えることで、常に変化します。
「ブロックエディターの投稿」は、ブロックエディターの生成物である post_content
とは異なります。先の例で言えば post_content
は印刷されたページであり読者向けに最適化されていますが、「ブロックエディターの投稿」には、後工程の編集のための見えないマーキングが含まれています。
ブロックエディターの入力と出力は、以下のフォーマットのブロックオブジェクトのツリーです。
const value = [ block1, block2, block3 ];
ブロックオブジェクト
各ブロックオブジェクトは、id と属性の集合、そして、オプションで子ブロックを持ちます。
const block = {
clientId, // ユニークな文字列識別子。
type, // ブロックタイプ (paragraph, image...)
attributes, // (キー, 値) 現行ブロックの直接のプロパティやコンテンツを表す属性の集合
innerBlocks // 子ブロックやインナーブロックの集合
}
注意: 属性のキーと型、許可されるインナーブロックはブロックタイプで定義されます。たとえばコアの引用ブロックには cite
文字列属性で引用のコンテンツを表し、見出しブロックには数値型の level
属性があり、見出しのレベル (1から6) を表します。
エディター内のブロックの生存期間において、ブロックオブジェクトは追加のメタデータを受信します。
isValid
: ブール値。ブロックが正しい (valid) かどうかを表す。originalContent
: ブロックの元の HTML シリアライゼーション
例
// A simple paragraph block.
const paragraphBlock1 = {
clientId: '51828be1-5f0d-4a6b-8099-f4c6f897e0a3',
type: 'core/paragraph',
attributes: {
content: 'This is the <strong>content</strong> of the paragraph block',
dropCap: true,
},
};
// A separator block.
const separatorBlock = {
clientId: '51828be1-5f0d-4a6b-8099-f4c6f897e0a4',
type: 'core/separator',
attributes: {},
};
// A columns block with a paragraph block on each column.
const columnsBlock = {
clientId: '51828be1-5f0d-4a6b-8099-f4c6f897e0a7',
type: 'core/columns',
attributes: {},
innerBlocks: [
{
clientId: '51828be1-5f0d-4a6b-8099-f4c6f897e0a5',
type: 'core/column',
attributes: {},
innerBlocks: [ paragraphBlock1 ],
},
{
clientId: '51828be1-5f0d-4a6b-8099-f4c6f897e0a6',
type: 'core/column',
attributes: {},
innerBlocks: [ paragraphBlock2 ],
},
],
};
シリアライゼーションとパース
このデータモデルは、投稿の編集中にメモリー内で存在し、レンダーの際のページビューアで見ることはできません。これは印刷されたページに、印刷の元となった活字の構造の跡が無いのと同じです。
WordPress のエコシステム全体は、投稿のレンダリングや編集の際に HTML を受け取ることを期待しています。このため、ブロックエディターもシリアライゼーションを通じて、post_content
に保存できる形にデータを変換します。これにより、コンテンツのただ一つの真のソースが存在し、かつ、このソースが読み取り可能で、既存の WordPress コンテンツを処理できるすべてのツールとの互換性を保証します。仮にオブジェクトツリーを別個に保存すれば、post_content
とツリーが同期せず、両方でデータが重複するリスクに直面します。
シリアライゼーションプロセスは、ブロックツリーを HTML に変換します。このとき、非 HTML 形式で属性を含められる明示的なブロックデリミタとして、HTML コメントが使用されます。これを印刷ページに例えれば、見えない記号を印刷して、オリジナルの構造の意図の跡を残しています。
これでプロセスの一方向は完了です。逆方向は、投稿が再び編集される際に、どのようにブロックのコレクションを再作成するかです。これには正規の文法が定義されています。「ブロックエディターの投稿」のシリアライズされた表現をどのようにロードすべきか、ツリーを HTML に似た文字列にするいくつかの基本ルールとして示されています。「ブロックエディターの投稿」は、手動で編集するものとして設計されていません。また、本質的に HTML ではありませんので、HTML 文書として編集するものとしても設計されていません。
ちなみに、これは偶然ですが post_content
には、レガシーなシステムでも一切の変換無しで読み出し可能な形式で保存されています。確かに、保存された HTML を対応する仕組みなしでロードすればエクスペリエンスは下がりますし、コンテンツの動的ブロックの動的要素はロードされません。サーバー生成コンテンツは表示されず、インタラクティブなコンテンツも動きません。しかし、少なくとも、ブロックを知らないテーマやインストールでも「ブロックエディターの投稿」を表示でき、コンテンツに最もアクセスしやすい方法を提供します。言い換えるなら、保存されたHTMLがそのままレンダーされても、投稿はほぼ表示されます。
デリミタと解析表現文法
私たちは既存の HTML 構文内で形式、わかりやすさ、明確さを維持する方法を探しましたが、HTML の中にはいくつかの選択肢がありました。
選択肢の中から、斬新なアプローチが提案されました。HTML コメントにデータを格納すれば、ブラウザはこのデータを無視する一方で、ドキュメント内の残りのHTMLは破壊されず、またドキュメントを解析するアプローチを単純化できます。
HTML コメントの特徴として、文法的に曖昧な位置に存在できない点があります。たとえば、<img alt='data-id="14"'>
の HTML 属性の中にコメントを置けません。一方で、コメントは寛容です。HTML 属性の正しいパースは複雑であるのに対し、コメントは極めて簡単で、先頭の <!--
に始まり、続いて、--
以外の任意の文字、そして、最初の -->
で終了する、と記述できます。この単純さと寛容さにより、HTMLを正しく理解しなくても、さまざまな方法でパーサーを実装できます。また、コメント内部では、ダブルハイフン (--
) をエスケープしさえすれば、より便利な構文を使用する自由があります。この利点を活かし、コメント内ではブロック属性を JSON リテラルとして保存しています。
これをパーサーで処理すると、慣用的な方法で操作できるシンプルなオブジェクトが作成されます。データのエスケープやアンエスケープを気にかける必要はなく、シリアライゼーションプロセスの過程で処理されます。コメントは他の HTML タグと大きく異なり、また、最上位レベルのブロックをワンパスで取得できるため、HTML が完全に妥当である必要さえありません。
これは、パーサーをどれだけシンプルで高性能にできるかという点で劇的な意味を持ちます。明確な境界は、1つのブロックの障害が他のブロックに波及したり、ドキュメント全体が汚染されることを防ぎます。また、システムはブロックをレンダリングする前に、認識できないブロックを識別できます。
注意: ブロックの定義を考えてみると、それはブロックのセマンティクスであり、提供される隔離の仕組みであり、つまりは、ブロックのアイデンティティとなります。一方、ブロックのデータがどこの保存されるかという点になると、もう少し自由になります。ブロックは、静的なローカルデータだけではない、HTML コメントやブロックの HTML 内の JSON リテラルを介した、より多くの保存場所と、より多くのメカニズム (例 グローバルブロック、または、そうでなければ補完的な WP_Post
オブジェクト内の領域) をサポートすることが期待されます。詳細については属性を参照してください。
シリアライズされたブロックの詳細
ブロックが編集セッション後にコンテンツに保存される際、その属性は、ブロックの性質に応じて、明示的なコメントデリミタにシリアライズされます。
<!-- wp:image -->
<figure class="wp-block-image"><img src="source.jpg" alt="" /></figure>
<!-- /wp:image -->
表示前にサーバーでレンダーされる純粋なダイナミックブロックは、次のようになります。
<!-- wp:latest-posts {"postsToShow":4,"displayPostDate":true} /-->
データのライフサイクル
要約すると、ブロックエディタのワークフローは、トークンデリミタを利用して、保存されたドキュメントをメモリー内のブロックツリーにパースします。編集中、すべての操作はブロックツリーの中で行います。そしてプロセスは、ブロックをシリアライズし、 post_content
に戻して、終了します。
ワークフロープロセスは、投稿を永続化する際、シリアライゼーションとパーサーのペアに依存します。仮定の話ですが理論上は、投稿データ構造はプラグインを使用して保存したり、リモートの JSON ファイルを取得してブロックツリーに変換できます。