组件为渲染结果添加状态,更是 Preact
的基石和构建复杂界面的基础。我们将在此教程中展示 Preact 中的两种组件。
函数组件
函数组件是接受 props 作为参数的函数,其名称必须以大写字母开头才能在 JSX 中使用。
function MyComponent(props) {
return <div>我的名字叫{props.name}。</div>;
}
// 用法
const App = <MyComponent name="张三" />;
// 渲染结果:<div>我的名字叫张三。</div>
render(App, document.body);
请注意,在先前的版本中我们将其称之为“无状态组件”,但有了钩子组件后就不是了。
类组件
类组件可以拥有状态及生命周期方法,后者指组件被加到 DOM /从 DOM 中删除时会调用的特殊方法。
下面是一个显示当前时间的简单类组件 <Clock>
:
class Clock extends Component {
constructor() {
super();
this.state = { time: Date.now() };
}
// 生命周期:在组件创建时调用
componentDidMount() {
// 每秒钟更新一次时间
this.timer = setInterval(() => {
this.setState({ time: Date.now() });
}, 1000);
}
// 生命周期:在组件被摧毁时调用
componentWillUnmount() {
// 在无法渲染时停止时钟
clearInterval(this.timer);
}
render() {
let time = new Date(this.state.time).toLocaleTimeString();
return <span>{time}</span>;
}
}
生命周期方法
为了让时钟能每秒钟更新一次事件,我们需要知道 <Clock>
什么时候会被挂载到 DOM
上。如果您用过 HTML5
自定义元素的话,您就会发现这和 attachedCallback
与 detachedCallback
生命周期方法很像。Preact
会自动为组件调用下列列表中存在的生命周期方法 :
生命周期方法 | 被调用时间 |
---|---|
componentWillMount() | (已弃用) 组件将被挂载到 DOM 前调用 |
componentDidMount() | 组件被挂载到 DOM 后调用 |
componentWillUnmount() | 组件将从 DOM 移除前调用 |
componentWillReceiveProps(nextProps, nextState) | (已弃用) 在传递进新属性前调用 |
getDerivedStateFromProps(nextProps) | 在 shouldComponentUpdate 前调用,请小心使用! |
shouldComponentUpdate(nextProps, nextState) | 在 render() 前调用,返回 false 来跳过渲染 |
componentWillUpdate(nextProps, nextState) | (已弃用) 在 render() 前调用 |
getSnapshotBeforeUpdate(prevProps, prevState) | 在 render() 前调用,返回值将传递进 componentDidUpdate |
componentDidUpdate(prevProps, prevState, snapshot) | 在 render() 后调用 |
异常捕获
有一个生命周期方法需要您特别注意,那就是 componentDidCatch
。其特别之处是您可以使用此方法处理渲染中的错误,包括生命周期钩子中的错误,但不包括如 fetch()
在内的异步调用所产生的错误。
当捕获到错误时,我们可以使用此生命周期方法处理错误、显示错误信息或其他备用内容。
class Catcher extends Component {
constructor() {
super();
this.state = { errored: false };
}
componentDidCatch(error) {
this.setState({ errored: true });
}
render(props, state) {
if (state.errored) {
return <p>出现严重错误</p>;
}
return props.children;
}
}
片段 (Fragment)
Fragment允许您同时返回多个元素。它们解决了JSX的限制,即每个“块”都必须有一个根元素。您经常会遇到它们与列表、表或CSS flexbox的组合,其中任何中间元素都会影响样式。
import { Fragment, render } from 'preact';
function TodoItems() {
return (
<Fragment>
<li>A</li>
<li>B</li>
<li>C</li>
</Fragment>
)
}
const App = (
<ul>
<TodoItems />
<li>D</li>
</ul>
);
render(App, container);
// Renders:
// <ul>
// <li>A</li>
// <li>B</li>
// <li>C</li>
// <li>D</li>
// </ul>
请注意,大多数现代transfiler允许您对Fragments使用较短的语法。较短的一种更常见,也是你通常会遇到的一种。
// This:
const Foo = <Fragment>foo</Fragment>;
// ...is the same as this:
const Bar = <>foo</>;
您还可以从组件返回数组:
function Columns() {
return [
<td>Hello</td>,
<td>World</td>
];
}
如果在循环中创建片段,请不要忘记将关键点添加到片段中:
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// Without a key, Preact has to guess which elements have
// changed when re-rendering.
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
))}
</dl>
);
}