虽然 Gutenberg 是用 React 构建的,但我们编写自定义块的代码并非如此。当然,它看起来很像一个 React 组件,所以我认为进行一些练习来熟悉这种方法很有用。到目前为止,在本系列文章中我们已经阅读了很多内容,所以让我们卷起袖子,制作一些很酷的东西。
文章系列
让我们创建一个“关于我”组件
我们将创建一个单独的 React 组件,它根据您输入到几个字段中的数据更新页面的背景颜色和简介文本。“我以为这应该很酷,”我听到你们都在咕哝。我承认,我可能夸大了,但我们将学习 **基于状态的 JavaScript** 的一些核心概念,这在深入研究我们的 Gutenberg 块时会派上用场。
作为参考,这是我们最终将得到的结果
开始
我们要做的第一件事是启动 CodePen。CodePen 可以免费使用,所以去那里并 创建一个新的 Pen。
接下来,我们将引入一些 JavaScript 依赖项。这里有三个编辑器屏幕——找到 JS
屏幕并点击设置齿轮。这将打开一个 Pen 设置模态窗口,您可以在其中找到名为 **添加外部脚本/笔** 的部分。在最底部,有一个 **快速添加** 选择菜单。打开它。

从菜单中,选择 **React**。选择后,打开菜单并选择 **ReactDOM**。您会看到这已预先填充了一些文本框。
最后,我们需要启用我们的 ES6 代码,所以在名为 **JavaScript 预处理器** 的菜单中,选择 **Babel**。
现在,点击大型 **保存并关闭** 按钮。
我们在这里所做的是提取主要的 React JS 库和 ReactDOM 库。这些将使我们能够深入研究并编写我们的代码,这是我们的下一步。
设置我们的 CSS
让我们让它看起来很酷。首先,让我们设置我们的 CSS 编辑器。我们要做的第一件事是将其设置为为我们编译 Sass。就像我们对 JS 编辑器所做的那样,点击设置齿轮,这将再次调出 **Pen 设置** 模态窗口——这次是 CSS 设置。
在顶部,有一个 **CSS 预处理器** 菜单。从那里选择 **SCSS**。
完成后,转到 **添加外部样式表/笔** 并将以下三个链接粘贴到单独的文本框中
https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.css
https://fonts.googleapis.com/css?family=Work+Sans:300
https://rawgit.com/hankchizljaw/boilerform/master/dist/css/boilerform.min.css
这三个依次为我们提供了一个重置、一个花哨的字体和一些 有用的表单样式。
现在它们都已设置好,再次点击“保存并关闭”按钮。
添加一些样式
我们都已设置好,因此此步骤应该很容易。将以下 Sass 粘贴到 CSS 编辑器中
:root {
--text-color: #f3f3f3;
}
* {
box-sizing: border-box;
}
html {
height: 100%;
font-size: 16px;
}
body {
height: 100%;
position: relative;
font-size: 1rem;
line-height: 1.4;
font-family: "Work Sans", sans-serif;
font-weight: 300;
background: #f3f3f3;
color: #232323;
}
.about {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
color: var(--text-color);
transition: all 2000ms ease-in-out;
&__inner {
display: flex;
flex-direction: column;
height: 100%;
margin: 0 auto;
padding: 1.2rem;
}
&__content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1 1 auto;
font-size: 3rem;
line-height: 1.2;
> * {
max-width: 30ch;
}
}
&__form {
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem 0;
width: 100%;
max-width: 60rem;
margin: 0 auto;
@media(min-width: 32rem) {
flex-direction: row;
justify-content: space-between;
padding: 2rem;
}
> * {
width: 15rem;
}
> * + * {
margin: 1rem 0 0 0;
@media(min-width: 32rem) {
margin: 0;
}
}
label {
display: block;
}
}
}
// Boilerform overrides
.c-select-field {
&,
&__menu {
width: 100%;
}
}
.c-input-field {
width: 100%;
}
.c-label {
color: var(--text-color);
}
这是一大块 CSS,看起来好像什么也没发生,但没关系——在本节的其余部分,我们不必担心 CSS。
深入 React
我们要做的第一件事是为 React 提供一些可以依附的东西。将此粘贴到 Pen 的 HTML 编辑器中
<div id="root"></div>
HTML 就这些了——您可以继续最大化您的 JS 编辑器,以便我们能够完全专注。
让我们开始编写组件代码,通过创建一个新的 React 组件实例,编写以下 JavaScript
class AboutMe extends React.Component {
}
这段代码的作用是创建一个新的 AboutMe
组件并扩展 React 的 Component
类,这为我们免费提供了大量的代码和工具。
好的,所以我们有一个类,现在我们需要构造它!在括号内添加以下代码
constructor(props) {
super(props);
let self = this;
};
这里有一些事情正在发生,所以我会解释每个部分
constructor
是当您编写 new AboutMe()
或在 JSX 中编写 <AboutMe />
时调用的方法。这将构造对象。props
参数是您将在 React 中经常看到的内容。这是传递到组件的一组属性。例如:如果您编写 <AboutMe name="Andy" />
,您将能够使用 props.name
在您的 constructor
中访问它。
super
是我们告诉我们扩展的类使用它自己的 constructor
进行构造的方式。您会看到我们也将其中的 props
传递给它,以防任何父组件需要访问它们。
最后,let self = this
是一种控制 this
作用域的方法。请记住,因为我们使用的是 let
,所以 self
仅在 constructor
函数中可用。
this
和对象原型 以及 作用域和闭包。保证质量不错。现在我们已经介绍了构造函数,让我们为它添加更多代码。在 let self = this;
行之后,粘贴以下代码
self.availableColors = [
{
"name": "Red",
"value": "#ca3814"
},
{
"name": "Blue",
"value": "#0086cc"
},
{
"name": "Green",
"value": "#3aa22b"
}
];
我们这里有一个对象数组,定义了我们选择您最喜欢的颜色的选项。如果您还没有添加,请继续添加自己的选项!
您的类定义和构造函数现在应该如下所示
class AboutMe extends React.Component {
constructor(props) {
super(props);
let self = this;
// Set a list of available colors that render in the select menu
self.availableColors = [
{
"name": "Red",
"value": "#ca3814"
},
{
"name": "Blue",
"value": "#0086cc"
},
{
"name": "Green",
"value": "#3aa22b"
}
];
};
}
到目前为止,非常简单,对吧?让我们继续,为我们的响应式状态设置一些初始值。在 self.availableColors
的结束括号之后添加以下内容
// Set our initial reactive state values
self.state = {
name: 'Foo',
color: self.availableColors[0].value
};
此初始状态设置使我们的组件能够在加载时渲染名称和颜色,从而防止它看起来损坏。
接下来,我们将添加我们的 render
函数。这是一个纯函数,除了根据初始状态或组件生命周期中的任何状态更改渲染组件之外,它什么也不做。您可能已经猜到了,但这是我们的 JSX 主要内容所在的地方。
现在,由于此单个组件中有相当多的标记,因此我们将把所有内容复制到我们的函数中。在您的 constructor
下添加以下内容
render() {
let self = this;
return (
<main className="about" style={ { background: self.state.color } }>
<section className="about__inner">
<article className="about__content">
{ self.state.name ? <p>Hello there. My name is { self.state.name }, and my favourite color is { self.getActiveColorName() }</p> : null }
</article>
<form className="[ about__form ] [ boilerform ]">
<div>
<label className="c-label" htmlFor="name_field">Your name</label>
<input className="c-input-field" type="text" id="name_field" value={ self.state.name } onChange={ self.updateName.bind(self) } />
</div>
<div>
<label className="c-label" htmlFor="color_field">Your favourite color</label>
<div className="c-select-field">
<select className="c-select-field__menu" value={ self.state.color } onChange={ self.updateColor.bind(self) } id="color_field">
{ self.availableColors.map((color, index) => {
return (
<option key={ index } value={ color.value }>{ color.name }</option>
);
})}
</select>
<span className="c-select-field__decor" aria-hidden="true" role="presentation">▾</span>
</div>
</div>
</form>
</section>
</main>
);
};
您可能会想:“天哪,这里发生了很多事情。”让我们剖析它,所以不用担心现在复制代码——我会让您知道我们将在哪里再次执行此操作。现在,让我们只关注一些关键部分。
在 JSX 中,你需要返回单个元素,该元素可以包含子元素。因为我们所有的代码都包裹在一个<main>
标签中,所以我们在这方面都很好。在那个<main>
标签上,你会看到我们在一个属性中有一个表达式,就像我们在第 2 部分中介绍的那样。此表达式将背景颜色设置为我们状态中当前设置的活动颜色。当用户更改其颜色选择时,这将像魔法一样更新,而我们无需为此渲染函数编写另一行代码。很酷,是吗?
在<article class="about__content">
元素内部,你会注意到这一点
{ self.state.name ? <p>Hello there. My name is { self.state.name }, and my favourite color is { self.getActiveColorName() }</p> : null }
这个三元运算符检查是否有设置名称,并呈现包含名称的句子或null
。在 JSX 中返回null
是告诉它不要向客户端呈现任何内容的方法。也与这段代码相关:我们能够在我们的 JSX 中运行这个三元运算符,因为我们通过打开一些括号创建了一个表达式。这是一种在render
函数中散布少量简单显示逻辑的非常有用的方法。
接下来,让我们看看事件绑定
<input className="c-input-field" type="text" id="name_field" value={ self.state.name } onChange={ self.updateName.bind(self) } />
如果你没有将事件绑定到你的输入字段,它将是只读的。不过不要惊慌失措。React 会在你的控制台中友好地提醒你。
记住,self
等于this
,所以我们正在做的是将updateName
函数附加到输入的onChange
事件,但我们也绑定了self
,以便当我们在updateName
函数中时,this
将等于AboutMe
,它是我们的组件。
我们在render
函数中要看的最后一件事是循环。这是渲染颜色菜单的代码段
<select className="c-select-field__menu" value={ self.state.color } onChange={ self.updateColor.bind(self) } id="color_field">
{ self.availableColors.map((color, index) => {
return (
<option key={ index } value={ color.value }>{ color.name }</option>
);
}) }
</select>
值和更改设置与上面的<input />
元素相同,因此我们将忽略它们并直接进入循环。我们所做的是打开一个表达式,在其中运行一个非常标准的Array Map 函数,但重要的是,它在每次迭代中返回 JSX,这允许每个选项与其余 JSX 一起呈现。
连接所有内容
现在我们已经让组件的核心方面运行起来,我们需要将它连接起来。你会注意到你的 CodePen 目前什么也没做。这是因为两件事
- 我们还没有将组件附加到 DOM
- 我们还没有编写任何方法使其具有交互性
让我们从前者开始,并添加我们的更改事件处理程序。在你的constructor
函数下面添加以下内容
updateName(evt) {
let self = this;
self.setState({
name: evt.target.value
})
};
updateColor(evt) {
let self = this;
self.setState({
color: evt.target.value
})
};
这两个函数处理<select>
和<input>
的onChange
事件,并使用 React 的setState
函数在其状态中设置它们的值。现在这些值已在状态中,任何订阅它们的内容都将自动更新。这意味着当你键入/选择时,呈现你的姓名和背景颜色的三元语句将实时更改。太棒了,对吧?
现在,建议通过将这两个函数合并为一个更改事件处理程序来使你的代码更 DRY,该处理程序更新相关状态。但是对于本系列来说,让我们保持简单易懂。😀
接下来,让我们向组件添加最后一个方法。在您最近添加的更新方法下方添加以下内容
// Return active color name from available colors, based on state value
getActiveColorName() {
let self = this;
return self.availableColors.filter(color => color.value === self.state.color)[0].name;
};
此函数使用我最喜欢的 JavaScript 数组方法之一:filter
。使用 ES6,我们可以在一行代码中根据对象值挑选数组项,这非常强大。凭借这种能力,我们可以挑选当前活动的availableColors
项的人类可读名称并将其返回。
将组件附加到 DOM
我们要做的最后一件事是使用ReactDOM将我们的组件附加到 DOM。我们正在做的是说,“嘿浏览器,帮我获取<div id="root">
元素,并在其中渲染这个 React 组件。”ReactDOM 正在执行使这一切成为可能的所有魔法。
ReactDOM 是一个非常智能的包,它会捕获动态 React 组件中的更改,计算 DOM 中需要更改的内容,并以最有效的方式应用这些更改。使用 ReactDOM 的renderToString()
方法,你还可以将你的 React 组件渲染为静态字符串,然后可以使用你的服务器端代码将其插入到你的页面中。这样做的好处是添加了引用,因此如果你的前端获取了一些服务器渲染的 React,它将计算出需要哪些组件,并自动使整个静态标记块动态化。非常聪明,对吧?
无论如何,回到我们的 Pen。在你的 JS 编辑器的最底部添加这个
// Attach our component to the <div id="root"> element
ReactDOM.render(<AboutMe />, document.getElementById('root'));
现在,你会注意到你的预览窗口突然活跃起来!恭喜——你刚刚编写了一个 React 组件🎉
查看 Andy Bell 在 CodePen 上的 Pen About Me React Component (@hankchizlja)。
总结
在本部分中,你学习了通过编写 React 组件来进行反应式、组件化 JavaScript。这与你的学习相关,因为自定义 Gutenberg 块遵循与 React 组件非常相似的设置。现在你已经更好地了解了 React 组件的工作原理,你应该能够理解自定义 Gutenberg 块的工作原理。
post_content
列中的数据库的一种方式。在 WordPress 网站的前端使用 React 构建类似这样的内容将与我们本系列中将要进行的操作分开。在本系列的下一部分中,我们将编辑我们的 WordPress 主题,以便我们可以构建我们的自定义 Gutenberg 块。
我在这里只想说,整个 Gutenberg 系列都太棒了!我没有在所有文章中发表评论,因为那样看起来很廉价——但每天我都有新的期待 :)
谢谢 Max!我希望你发现这个系列有用 :)
我的 CodePen 没有运行。查看控制台,我发现我首先加载了 react-dom.development.js,然后加载了 react.development.js。一旦我把 react.development.js 外部资源放在顶部,一切就正常了。
此外,我发现为了在 render( ) 函数内编写注释,你需要用花括号将其括起来。D
例如:{/* 使用 JavaScript Array map( ) 函数构建列表框 */}
是的,在你的 JSX 中,它们需要在
{}
对中,因为它们创建了一个表达式。不过,这对可能遇到此问题的人来说是一个很好的提示,所以谢谢你 :)