WordPress 制作自定义块(gutenberg block)

因为之前用的两个代码高亮插件不是很顺手,所以打算自己制作一个

先创建一个空的块插件

npx @wordpress/create-block gutenpride
cd gutenpride

如果电脑本来就有wordpress,则可以直接生成在 /wp-content/plugins 目录

如果没有,则可以使用 wp-env 工具,配合 docker ,生成一个专门调试插件的环境

npm -g install @wordpress/env ## 安装 wp-env
wp-env start ## 在当前目录启动WordPress调试环境

然后打开 http://localhost:8888/ 就可以看到一个WP站点了,并且默认启用了刚才创建的插件(登陆账号密码为 admin/password )

然后使用 npm start (wp-scripts start)命令,开启实时更改重构

npm start
npm start 检测到文件更改时自动重构

使用npx创建的插件中,包含一个 block.json 文件

这个文件定义了插件的所有信息,其中 attributes 字段表示这个古腾堡块中会创建哪些属性

	"attributes": {
	    "code": {
	    	"type": "string",
	    	"source": "text",
	    	"selector": "code"
	    },
	    "lang": {
	    	"type": "string"
	    }
	},

比如我创建了一个 code 值表示插入的代码,lang 则为代码语言

我不需要 lang 值直接呈现出来,所以不需要添加 source 和 selector 值。它将会储存在古腾堡块的注释中

lang 值

selector 为这个块的内容输出到的HTML标记的选择器,可以是HTML标签、class或id

我在 supports 段中加了 align 和 alignWide 的支持

这样这个块就能调整宽度了

code block 块

在生成的插件中的src目录下, 有 edit.js 和 save.js 两个文件,它们负责这个块的功能实现和渲染。

在 Edit 函数中 return 的内容就是在编辑器中呈现的东西

save 函数 return 的内容 就是渲染成HTML的内容

我这里需要在编辑器中可以写代码语言和内容,分别用 TextControl 和 TextareaControl 两个组件制作,先要引入这两个组件

import {TextControl, TextareaControl} from '@wordpress/components';

然后把它们包裹在一个div块中返回

	return (
		<div {...blockProps} >
			<TextControl
				label="代码语言"
				value={attributes.lang}
				onChange={(val) => setAttributes({lang: val})}
			/>

			<TextareaControl
				label="代码"
				value={attributes.code}
				onChange={(val) => setAttributes({code: val})}
			/>

		</div>
	)

然后在 save.js 文件中将它们渲染成HTML内容

	return (
		<div {...blockProps}>
		  <pre>
			  <code className={lang}>{attributes.code}</code>
		  </pre>
		</div>
	)

添加 {…blockProps} 可以让div带有古腾堡块的class

const blockProps = useBlockProps(); //edit
const blockProps = useBlockProps.save();  //save

editor.scss 中的样式只会在编辑器里加载

style.scss 则在网页和编辑器都加载

Textarea高度自适应

我用于输入代码的组件是 TextareaControl ,它会返回一个 textarea 元素,textarea 是无法根据内容长度自动改变高度的,创建一个函数获取内容换行数自动设置高度

export function setHeightCodeBlocks(elem) {
	const num = elem.value.match(/\r\n|\n/g);
	let line;
	if (null !== num) {
		line = num.length + 1;
	} else {
		line = 1;
	}
	elem.style.height = line * 1.8 + 3.8 + 'em';
}

useEffect 钩子检测到代码组件重新渲染时触发设置高度

import {useEffect} from '@wordpress/element';

	useEffect(() => {
		// console.log('useEffect');
		setTimeout(() => {
			// alert(clientId)
			const codeBlock = document.getElementById('block-' + clientId);
			const textarea = codeBlock.querySelector('textarea');
			setHeightCodeBlocks(textarea);
		}, 10);
	}, [clientId, attributes.code]);

然后在插件的php文件中引入prismjs就可以实现代码高亮了

function load_plugin_scripts() {
    wp_enqueue_script(
        'prismjs',
       'https://cdn.jsdelivr.net/npm/prismjs@1.25.0/prism.min.js'
    );
    wp_enqueue_script(
        'prismjs-autoloader',
       'https://cdn.jsdelivr.net/npm/prismjs@1.25.0/plugins/autoloader/prism-autoloader.min.js'
    );

   // wp_enqueue_style( 'my-theme', 'https://cdn.jsdelivr.net/npm/prismjs@1.25.0/themes/prism.css' );

}

add_action( 'wp_enqueue_scripts', 'load_plugin_scripts' );

区块转换

古腾堡区块支持互相转换,实例如下

form 表示可以从哪些区块转换来, to 表示可以转换成什么

我添加了让WP默认code块转换为我自己块的支持(不过如果是插件中的块,插件卸载后,就不能转换了)

	transforms: {
		from: [
			{
				type: 'block',
				blocks: ['core/code'], 
				transform: (attributes) => {
					return createBlock('xiyuyyy/code-block', {
						code: attributes.content,
					});
				},
			},
			{
				type: 'block',
				blocks: ['loos-hcb/code-block'],
				transform: (attributes) => {
					// console.log(attributes)
					return createBlock('xiyuyyy/code-block', {
						lang: attributes.langType,
						code: attributes.code,
					});
				},
			},
		],
		to: [
			// {
			// 	type: 'block',
			// 	blocks: ['core/code'],
			// 	transform: (attributes) => {
			// 		return createBlock('core/code', {
			// 			content: attributes.code,
			// 		});
			// 	},
			// },
		],
	},