学习 Gutenberg:React 101

Avatar of Andy Bell
Andy Bell 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

虽然 Gutenberg 是用 React 构建的,但我们编写自定义块的代码并非如此。当然,它看起来很像一个 React 组件,所以我认为进行一些练习来熟悉这种方法很有用。到目前为止,在本系列文章中我们已经阅读了很多内容,所以让我们卷起袖子,制作一些很酷的东西。

文章系列

  1. 系列介绍
  2. 什么是 Gutenberg?
  3. 使用 create-guten-block 的入门指南
  4. 现代 JavaScript 语法
  5. React 101 (本文)
  6. 设置自定义 webpack
  7. 自定义“卡片”块

让我们创建一个“关于我”组件

我们将创建一个单独的 React 组件,它根据您输入到几个字段中的数据更新页面的背景颜色和简介文本。“我以为这应该很酷,”我听到你们都在咕哝。我承认,我可能夸大了,但我们将学习 **基于状态的 JavaScript** 的一些核心概念,这在深入研究我们的 Gutenberg 块时会派上用场。

作为参考,这是我们最终将得到的结果

开始

我们要做的第一件事是启动 CodePen。CodePen 可以免费使用,所以去那里并 创建一个新的 Pen

接下来,我们将引入一些 JavaScript 依赖项。这里有三个编辑器屏幕——找到 JS 屏幕并点击设置齿轮。这将打开一个 Pen 设置模态窗口,您可以在其中找到名为 **添加外部脚本/笔** 的部分。在最底部,有一个 **快速添加** 选择菜单。打开它。

A screenshot of the CodePen interface with the JavaScript settings open. The settings are in a split pane where the settings are on the left in a white box and advanced settings are on the right in a dark gray box.

从菜单中,选择 **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 函数中可用。

对于那些对 JavaScript 不太自信的读者,快速说明一下:我发现深入研究 JavaScript 中的 **作用域** 会在我的学习过程中产生很多“啊哈”时刻。我强烈推荐 Kyle SimpsonYou Don’t Know JS 书籍系列(可在 GitHub 上免费获取!)。值得注意的卷: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 主要内容所在的地方。

等等!什么是纯函数?欢迎来到函数式编程,这是 React 世界中的一个热门话题。函数是指,对于输入 X,输出始终为 Y 的函数。在“不纯”函数中,输入 X 可能会产生不同的输出,具体取决于程序的其他部分。这里有一个 CodePen 比较纯函数和不纯函数。还可以查看 这篇文章 以了解更多详细信息。

现在,由于此单个组件中有相当多的标记,因此我们将把所有内容复制到我们的函数中。在您的 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项的人类可读名称并将其返回。

JavaScript 数组方法非常酷,并且在 React 生态系统中经常出现。Sarah Drasner 创建了一个非常棒的“数组浏览器”——在这里查看!

将组件附加到 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 块的工作原理。

我花了一段时间才理解,就 Gutenberg 而言,React 仅与在管理后台构建块相关。在 Gutenberg 中,React 充当准备将标记保存到post_content列中的数据库的一种方式。在 WordPress 网站的前端使用 React 构建类似这样的内容将与我们本系列中将要进行的操作分开。

在本系列的下一部分中,我们将编辑我们的 WordPress 主题,以便我们可以构建我们的自定义 Gutenberg 块。


文章系列

  1. 系列介绍
  2. 什么是 Gutenberg?
  3. 使用 create-guten-block 的入门指南
  4. 现代 JavaScript 语法
  5. React 101 (本文)
  6. 设置自定义 webpack
  7. 自定义“卡片”块