MDC-112 Web:将 MDC 与 Web 框架集成

MDC-112 Web:将 MDC 与 Web 框架集成

关于此 Codelab

subject上次更新时间:10月 11, 2020
account_circleLiz Mitchell, Abhinay Omkar 编写

1. 简介

logo_components_color_2x_web_96dp.png

Material Components (MDC) 有助于开发者实现 Material Design。MDC 是由一组 Google 工程师和用户体验设计人员倾心打造的,提供数十种精美实用的界面组件,可用于 Android、iOS、Web 和 Flutter.material.io/develop

MDC Web 经过精心设计,可集成到任何前端框架,同时遵循 Material Design 原则。以下 Codelab 将引导您构建一个使用 MDC Web 为基础的 React 组件。在此 Codelab 中学习的原理可应用于任何 JavaScript 框架。

MDC Web 的构建方式

MDC Web 的 JavaScript 层由每个组件对应的三个类组成:组件基础适配器。此模式使 MDC Web 能够灵活地与前端框架集成。

基础包含实现 Material Design 的业务逻辑。该基础架构不会引用任何 HTML 元素。这样,我们就可以将 HTML 交互逻辑提取到适配器中。基础知识包含适配器

适配器是一个接口。Adapter 接口由 Foundation 引用,以实现 Material Design 业务逻辑。您可以在不同的框架(例如 Angular 或 React)中实现适配器。适配器的实现会与 DOM 结构进行交互。

组件具有基础,其作用是

  1. 使用非框架 JavaScript 实现适配器,并
  2. 提供代理到 Foundation 中方法的公共方法。

MDC Web 提供的内容

MDC Web 中的每个软件包都包含一个组件基础架构适配器。如需实例化组件,您必须将根元素传递给组件的构造函数方法。Component 会实现 Adapter,后者能与 DOM 和 HTML 元素互动。然后,组件会实例化 Foundation,后者会调用 Adapter 方法。

如需将 MDC Web 集成到框架中,您需要使用该框架的语言/语法创建自己的组件。框架组件会实现 MDC Web 的适配器,并使用 MDC Web 的基础架构

构建内容

此 Codelab 演示了如何构建自定义适配器,以使用 Foundation 逻辑实现 Material Design React 组件。其中涵盖了集成到框架中中的高级主题。此 Codelab 中使用 React 作为示例框架,但此方法可应用于任何其他框架。

在此 Codelab 中,您将构建顶部应用栏,并重新创建顶部应用栏演示页面。演示页面布局已设置完毕,您可以开始着手处理顶部应用栏了。顶部应用栏将包含:

  • 导航图标
  • 待办项
  • 有 4 种变体可供选择:简短始终收起固定醒目变体

所需条件

  • 较新版本的 Node.js(与 npm 捆绑在一起,npm 是一个 JavaScript 软件包管理器)
  • 示例代码(将在下一步中下载)
  • 具备 HTML、CSS、JavaScript 和 React 方面的基础知识

您如何评价自己在 Web 开发方面的经验水平?

2. 设置开发环境

下载起始 Codelab 应用

起始应用位于 material-components-web-codelabs-master/mdc-112/starter 目录中。

…或从 GitHub 克隆

如需从 GitHub 克隆此 Codelab,请运行以下命令:

git clone https://github.com/material-components/material-components-web-codelabs
cd material
-components-web-codelabs/mdc-112/starter

安装项目依赖项

在起始目录 material-components-web-codelabs/mdc-112/starter 中,运行以下命令:

npm install

您会看到大量 activity,并且在最后,您的终端应该会显示已成功安装:

22a33efc2a687408

运行起始应用

在同一目录中,运行以下命令:

npm start

系统将启动 webpack-dev-server。将浏览器指向 http://localhost:8080/ 以查看该页面。

b55c66dd400cf34f.png

大功告成!您的浏览器中现在应该正在运行顶部应用栏 React 演示页面的起始代码。您应该会看到一面由 lorem ipsum 文本组成的墙、一个 Controls 框(位于右下角)和一个未完成的顶部应用栏:

4ca3cf6d216f9290.png

查看代码和项目

如果您打开代码编辑器,项目目录应如下所示:

e9a3270d6a67c589.png

打开 App.js 文件,然后查看 render 方法(其中包含 <TopAppBar> 组件):

App.js

render() {
   
const {isFixed, isShort, isRtl, isProminent, isAlwaysCollapsed, shouldReinit} = this.state;

   
return (
     
<section
        dir
={isRtl ? 'rtl' : 'ltr'}
        className
='mdc-typography'>
       
{
          shouldReinit
? null :
         
<TopAppBar
            navIcon
={this.renderNavIcon()}
           
short={isShort}
            prominent
={isProminent}
           
fixed={isFixed}
            alwaysCollapsed
={isAlwaysCollapsed}
            title
='Mountain View, CA'
            actionItems
={this.actionItems}
         
/>
       
}
       
<div className={classnames('mdc-top-app-bar--fixed-adjust', {
         
'mdc-top-app-bar--short-fixed-adjust': isShort || isAlwaysCollapsed,
         
'mdc-top-app-bar--prominent-fixed-adjust': isProminent,
       
})}>
         
{this.renderDemoParagraphs()}
       
</div>

        {this.renderControls()}
      </
section>
   
);
 
}

这是应用中 TopAppBar 的入口点。

打开 TopAppBar.js 文件,它是一个带有 render 方法的裸 React Component 类:

TopAppBar.js

import React from 'react';

export default class TopAppBar extends React.Component {
  render
() {
   
return (
     
<header>
        TOP APP BAR
     
</header>
   
);
 
}
}

3. 组件组成

在 React 中,render 方法会输出组件的 HTML。顶部应用栏组件将呈现 <header /> 标记,并由 2 个主要部分组成:

  1. 导航图标和标题部分
  2. 操作图标部分

如果您对构成顶部应用栏的元素有任何疑问,请访问 GitHub 上的文档。

修改 TopAppBar.js 中的 render() 方法,使其如下所示:

  render() {
   
const {
      title
,
      navIcon
,
   
} = this.props;

   
return (
     
<header
        className
={this.classes}
        style
={this.getMergedStyles()}
       
ref={this.topAppBarElement}
     
>
       
<div className='mdc-top-app-bar__row'>
         
<section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-start'>
           
{navIcon ? navIcon : null}
           
<span className="mdc-top-app-bar__title">
             
{title}
           
</span>
          </
section>
         
{this.renderActionItems()}
       
</div>
      </
header>
   
);
 
}

此 HTML 中包含两个 section 元素。第一个包含导航图标和标题。第二个包含操作图标。

接下来,添加 renderActionItems 方法:

renderActionItems() {
 
const {actionItems} = this.props;
 
if (!actionItems) {
   
return;
 
}

 
return (
   
<section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-end' role='toolbar'>
     
{/* need to clone element to set key */}
     
{actionItems.map((item, key) => React.cloneElement(item, {key}))}
   
</section>
 
);
}

开发者会将 TopAppBar 导入其 React 应用中,并将操作图标传递给 TopAppBar 元素。您可以在 App.js 中查看用于在 App.js 中初始化 TopAppBar 的示例代码。

缺少 getMergedStyles 方法,该方法在 render 方法中使用。请将以下 JavaScript 方法添加到 TopAppBar 类中:

getMergedStyles = () => {
 
const {style} = this.props;
 
const {style: internalStyle} = this.state;
 
return Object.assign({}, internalStyle, style);
}

render 方法中也缺少 this.classes,稍后会加以介绍。除了缺少的 getter 方法 this.classes 之外,您还需要实现 TopAppBar 的部分内容,才能正确呈现顶部应用栏。

顶部应用栏中仍缺少 React 组件的以下部分:

  • 已初始化的框架
  • 要传递到基础架构的适配器方法
  • JSX 标记
  • 变体管理(固定、简短、始终收起、醒目)

采用的方法

  1. 实现 Adapter 方法。
  2. componentDidMount 中初始化 Foundation
  3. componentWillUnmount 中调用 Foundation.destroy 方法。
  4. 通过组合适当的类名称的 getter 方法建立变体管理。

4. 实现 Adapter 方法

非框架 JS TopAppBar 组件会实现以下 Adapter 方法(详见此处):

  • hasClass()
  • addClass()
  • removeClass()
  • registerNavigationIconInteractionHandler()
  • deregisterNavigationIconInteractionHandler()
  • notifyNavigationIconClicked()
  • setStyle()
  • getTopAppBarHeight()
  • registerScrollHandler()
  • deregisterScrollHandler()
  • registerResizeHandler()
  • deregisterResizeHandler()
  • getViewportScrollY()
  • getTotalActionItems()

由于 React 具有合成事件和不同的最佳编码实践和模式,因此需要重新实现 Adapter 方法。

适配器 getter 方法

TopAppBar.js 文件中,将以下 JavaScript 方法添加到 TopAppBar

get adapter() {
 
const {actionItems} = this.props;

 
return {
    hasClass
: (className) => this.classes.split(' ').includes(className),
    addClass
: (className) => this.setState({classList: this.state.classList.add(className)}),
    removeClass
: (className) => {
     
const {classList} = this.state;
      classList
.delete(className);
     
this.setState({classList});
   
},
    setStyle
: this.setStyle,
    getTopAppBarHeight
: () => this.topAppBarElement.current.clientHeight,
    registerScrollHandler
: (handler) => window.addEventListener('scroll', handler),
    deregisterScrollHandler
: (handler) => window.removeEventListener('scroll', handler),
    registerResizeHandler
: (handler) => window.addEventListener('resize', handler),
    deregisterResizeHandler
: (handler) => window.removeEventListener('resize', handler),
    getViewportScrollY
: () => window.pageYOffset,
    getTotalActionItems
: () => actionItems && actionItems.length,
 
};
}

用于滚动和调整大小事件注册的适配器 API 的实现与非框架 JS 版本相同,因为 React 没有用于滚动或调整大小的任何合成事件,而是会推迟到原生 DOM 事件系统。getViewPortScrollY 还需要延迟到原生 DOM,因为它是 window 对象上的函数,而该对象不在 React 的 API 中。每个框架的适配器实现各不相同。

您可能会注意到缺少 this.setStyle,该方法由 get adapter 方法调用。在 TopAppBar.js 文件中,将缺少的 JavaScript 方法添加到 TopAppBar 类:

setStyle = (varName, value) => {
 
const updatedStyle = Object.assign({}, this.state.style);
  updatedStyle
[varName] = value;
 
this.setState({style: updatedStyle});
}

您刚刚实现了 Adapter!请注意,此时您可能会在控制台中看到错误,因为完整实现尚未完成。下一部分将指导您如何添加和移除 CSS 类。

5. 实现组件方法

管理变体和类

React 没有用于管理类的 API。如需模仿原生 JavaScript 的添加/移除 CSS 类方法,请添加 classList 状态变量。TopAppBar 中有三段代码与 CSS 类交互:

  1. <TopAppBar /> 组件(通过 className 属性)
  2. 通过 addClassremoveClass 使用 Adapter 方法。
  3. <TopAppBar /> React 组件中硬编码。

首先,在 TopAppBar.js 顶部的现有 import 下方添加以下 import:

import classnames from 'classnames';

然后,在 TopAppBar 组件的类声明中添加以下代码:

export default class TopAppBar extends React.Component {
  constructor
(props) {
   
super(props);
   
this.topAppBarElement = React.createRef();
 
}

  state
= {
    classList
: new Set(),
    style
: {},
 
};

 
get classes() {
   
const {classList} = this.state;
   
const {
      alwaysCollapsed
,
      className
,
     
short,
     
fixed,
      prominent
,
   
} = this.props;

   
return classnames('mdc-top-app-bar', Array.from(classList), className, {
     
'mdc-top-app-bar--fixed': fixed,
     
'mdc-top-app-bar--short': short,
     
'mdc-top-app-bar--short-collapsed': alwaysCollapsed,
     
'mdc-top-app-bar--prominent': prominent,
   
});
 
}

 
...
}

如果您前往 http://localhost:8080,现在“控件”复选框应会切换 DOM 中的类名称。

此代码使 TopAppBar 可供许多开发者使用。开发者可以与 TopAppBar API 互动,而无需担心 CSS 类的实现细节。

现在,您已成功实现 Adapter。下一部分将引导您实例化 Foundation

装载和卸载组件

Foundation 实例化发生在 componentDidMount 方法中。

首先,在 TopAppBar.js 中的现有 import 后面添加以下 import,以导入 MDC 顶部应用栏基础:

import {MDCTopAppBarFoundation, MDCFixedTopAppBarFoundation, MDCShortTopAppBarFoundation} from '@material/top-app-bar';

接下来,将以下 JavaScript 代码添加到 TopAppBar 类中:

export default class TopAppBar extends React.Component {
 
 
...

  foundation_
= null;

  componentDidMount
() {
   
this.initializeFoundation();
 
}

  componentWillUnmount
() {
   
this.foundation_.destroy();
 
}

  initializeFoundation
= () => {
   
if (this.props.short) {
     
this.foundation_ = new MDCShortTopAppBarFoundation(this.adapter);
   
} else if (this.props.fixed) {
     
this.foundation_ = new MDCFixedTopAppBarFoundation(this.adapter);
   
} else {
     
this.foundation_ = new MDCTopAppBarFoundation(this.adapter);
   
}

   
this.foundation_.init();
 
}
 
 
...

}

良好的 React 编码实践之一是定义 propTypes 和 defaultProps。在 TopAppBar.js 的现有导入之后添加以下导入:

import PropTypes from 'prop-types';

然后,将以下代码添加到 TopAppBar.js 的底部(位于 Component 类之后):

import PropTypes from 'prop-types';

TopAppBar.propTypes = {
  alwaysCollapsed
: PropTypes.bool,
 
short: PropTypes.bool,
 
fixed: PropTypes.bool,
  prominent
: PropTypes.bool,
  title
: PropTypes.string,
  actionItems
: PropTypes.arrayOf(PropTypes.element),
  navIcon
: PropTypes.element,
};

TopAppBar.defaultProps = {
  alwaysCollapsed
: false,
 
short: false,
 
fixed: false,
  prominent
: false,
  title
: '',
  actionItems
: null,
  navIcon
: null,
};

现在,您已成功实现顶部应用栏 React 组件。如果您导航到 http://localhost:8080,则可以体验演示页面。该演示页面的运作方式与 MDC Web 的演示页面相同。演示页面应如下所示:

3d983b98c2092e7a

6. 小结

在本教程中,我们介绍了如何封装 MDC Web 的 Foundation 以便在 React 应用中使用。GitHub 和 npm 上有一些库封装了 MDC Web 组件,如集成到框架中所述。我们建议您使用此处列出的列表。此列表还包含除 React 之外的其他框架,例如 Angular 和 Vue。

本教程重点介绍了我们决定将 MDC Web 代码拆分为 3 个部分:基础适配器组件。这种架构允许组件在与所有框架协同工作时共享通用代码。感谢您试用 Material Components React,请查看我们的新库 MDC React。希望您喜欢此 Codelab!

我能够用合理的时间和精力完成此 Codelab

我希望日后继续使用 Material Components