A. 无障碍辅助功能
WAI-ARIA
是一个为残疾人士等提供无障碍访问动态、可交互Web内容的技术规范。主要针对的是视觉缺陷,失聪,行动不便的残疾人以及假装残疾的测试人员。尤其像盲人,眼睛看不到,其浏览网页则需要借助辅助设备,如屏幕阅读器,屏幕阅读机可以大声朗读或者输出盲文。
在标签内使用aria-*
属性【aria属性表】,它描述了元素的属性和状态。
通常与其配合使用的标签是role
,它的值表示一个元素的实际意义和作用。
JSX
支持所有 aria-*
HTML
属性。虽然大多数 React
的 DOM
变量和属性命名都使用驼峰命名(camelCased
),但 aria-*
应该像其在 HTML
中一样使用带连字符的命名法
<input type="text" name="name" value={inputValue}
aria-label={labelText} aria-required="true"
onChange={onchangeHandler} />
A2. 语义化的HTML -- React Fragments
语义化的 HTML 是无障碍辅助功能网络应用的基础。 利用多种 HTML 元素
来强化您网站中的信息通常可以使您直接获得无障碍辅助功能
。
有时,语义化的 HTML 会被破坏。比如当在 JSX 中使用 <div>
元素来实现 React 代码功能的时候,又或是在使用列表(<ol>
, <ul>
和 <dl>
)和 HTML <table>
时。 在这种情况下,我们应该使用 React Fragments 来组合各个组件。
A3. 无障碍表单
所有的 HTML 表单
控制,例如 <input>
和 <textarea>
,都需要被标注来实现无障碍辅助功能。我们需要提供屏幕朗读器以解释性标注。
以下资源向我们展示了如何写标注:
尽管这些标准 HTML 实践可以被直接用在 React 中,请注意 for
在 JSX 中应该被写作 htmlFor
:
<label htmlFor="namedInput">Name:</label>
<input id="namedInput" type="text" name="name"/>
还有很多其他无障碍相关的知识点,本人暂时不准备深入了解无障碍这块相关知识点,所以本文就先不深入学习了。
B. Context
在一个典型的
React
应用中,数据是通过props
属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI
主题),这些属性是应用程序中许多组件都需要的。Context
提供了一种在组件之间共享此类值
的方式,而不必显式地通过组件树的逐层传递props
。
B.1请谨慎使用 Context
会使得组件的复用性变差。如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。
B.2 render props
这种模式足够覆盖很多场景了,在这些场景下你需要将子组件和直接关联的父组件解耦。如果子组件需要在渲染前和父组件进行一些交流,你可以进一步使用 render props。
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
用render
渲染子组件,就不需要在主组件中写死。看案例:
重要的是要记住,render prop
是因为模式才被称为 render prop
,你不一定要用名为 render
的 prop
来使用这种模式
Render Props
与 React.PureComponent
(区别于React.Component
) 一起使用时要小心
B.3 Context API
Class.contextType:挂载在 class
上的 contextType
属性会被重赋值为一个由 React.createContext()
创建的 Context
对象。这能让你使用 this.context
来消费最近 Context
上的那个值。你可以在任何生命周期中访问到它,包括 render
函数中。
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;
Context.displayName:context
对象接受一个名为 displayName
的 property
,类型为字符串。React DevTools
使用该字符串来确定 context
要显示的内容:
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
// "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Provider>
// "MyDisplayName.Consumer" 在 DevTools 中
<MyContext.Consumer>
B.4 消费多个 Context
// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');
// 用户登录 context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends React.Component {
render() {
const {signedInUser, theme} = this.props;
// 提供初始 context 值的 App 组件
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Layout() {
return (
<div>
<Sidebar />
<Content />
</div>
);
}
// 一个组件可能会消费多个 context
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
C. 错误边界
部分
UI
的JavaScript
错误不应该导致整个应用崩溃,为了解决这个问题,React 16
引入了一个新的概念 —— 错误边界。
错误边界的工作方式类似于 JavaScript 的 catch {}
,不过只有React
class
组件才可以成为错误边界组件。你可以将它作为一个常规组件去使用:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
D. Refs转发
非受控组件中也有ref
相关内容:
E. Fragments
最开始A2. 语义化的HTML
中已经提到了该知识点:
有时,语义化的 HTML 会被破坏。比如当在 JSX 中使用 <div>
元素来实现 React 代码功能的时候,又或是在使用列表(<ol>
, <ul>
和 <dl>
)和 HTML <table>
时。 在这种情况下,我们应该使用 React Fragments 来组合各个组件。
F. 高阶组件HOC
具体而言,高阶组件是参数为组件
,返回值为新组件
的函数。
const EnhancedComponent =
higherOrderComponent(WrappedComponent);
HOC
在 React 的第三方库
中很常见,例如 Redux
的 connect
和 Relay
的 createFragmentContainer
。
HOC
一些相关内容这里就先不展开学习了,就稍微提下一些注意事项:
- ①不要在
render
方法中使用HOC
render() {
每次调用 render 函数都会创建一个新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
const EnhancedComponent = enhance(MyComponent);
return <EnhancedComponent />;
}
这不仅仅是性能问题 - 重新挂载组件会导致该组件及其所有子组件的状态丢失。
- ② 务必复制静态方法
有时在React
组件上定义静态方法很有用。例如,Relay
容器暴露了一个静态方法getFragment
以方便组合GraphQL
片段。
当你将HOC
应用于组件时,原始组件将使用容器组件进行包装。这意味着新组件没有原始组件的任何静态方法
。 - ③
Refs
不会被传递
G. Portals
Portal
提供了一种将子节点
渲染到存在于父组件以外的 DOM 节点
的优秀的方案。
ReactDOM.createPortal(child, container)
第一个参数(child
)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container
)是一个 DOM 元素。
通常来讲,当你从组件的 render 方法返回一个元素时,
该元素将被挂载到 DOM 节点中离其最近的父节点:
render() {
// React 挂载了一个新的 div,并且把子元素渲染其中
return (
<div>
{this.props.children}
</div>
);
}
然而,有时候将子元素插入到 DOM 节点中的不同位置也是有好处的:
render() {
// React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
// `domNode` 是一个可以在任何位置的有效 DOM 节点。
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
H. 其他
与第三方库协同、深入JSX、性能优化:这里就先不深入学习了,边用边掌握吧!当然,以上讲的各个知识点都需要通过实践来不断深入理解。