如何使用 React 构建图片轮播图
大家好!
在这篇文章中,我决定使用我目前非常热衷的技术——React。
最近,我有很多机会接触 React,随着它的优势越来越清晰,我越来越愿意深入学习使用这个库来构建用户界面。
如您所知,React是一个 JavaScript 库,它采用组件化的方式构建复杂的交互式用户界面。React 使用的一些技术和概念旨在提高界面交互的效率。通过使用虚拟 DOM(真实 DOM 的轻量级表示),React 可以显著提升界面交互的速度。因为每次更改发生时,React 都会将虚拟 DOM 与真实 DOM 进行比较,并仅更新已更改的部分。
好了,这只是 React 在构建 UI 时比其他库强大得多的原因之一。本文将重点介绍 React 的实际应用示例,如果您在阅读代码时遇到困难,或者对文中提到的任何概念不了解,可以查阅 React文档。
基于组件的思维
创建用户界面时,首先要考虑的就是将其视为一组组件,这些组件将封装界面的各个部分,然后协同工作以创造良好的用户体验。这就是 React 的作用之一,它改变了你组织应用程序的方式。
如果你看看这篇文章的标题图片,就能明白我们要构建的是什么了。看起来很简单,对吧?事实上,因为我们用的是 React,所以它确实可以很简单 :)
你应该知道,在 React 中实现图片轮播图的方法有很多,包括组件的组织方式,甚至组件的数量。在这个例子中,我决定创建三个基本组件:第一个组件(`<image>` Carousel)将作为整个布局和逻辑的包装器;第二个ImageSlide组件(`<image>`)将简单地渲染轮播图的图片;第三个Arrow组件(`<array>`)将作为轮播图过渡的左右箭头。这样,对于ImageSlide你作为轮播图输入的每张图片,你都会有一个 `<image>` 组件的实例;而Arrow对于每个箭头,你都会有两个 `<array>` 组件的实例。
首先,让我们来构建Carousel组件。
该Carousel组件
首先,你必须告诉 React 你想让它在 HTML 文件的哪个部分渲染你的组件。
在您的 HTML 文件中添加以下内容:
<div id="container">
<!-- Your component will be rendered here. -->
</div>
在你的 JavaScript 文件中,输入以下内容:
ReactDOM.render(
<Carousel />,
document.getElementById('container')
);
如您所见,我们将 div 容器绑定到 Carousel 组件,因此 React 将使用该占位符来渲染您的整个组件。
请注意,您的脚本中必须包含所需的React库。如果您在CodePen或JsFiddleReactDOM等代码测试平台进行测试,只需在设置部分添加相关脚本即可。但是,如果您已经在本地计算机上配置了 React 开发环境,那么您应该知道该怎么做了 :)
现在是时候设置我们的Carousel组件了。
class Carousel extends React.Component {
render () {
return (
<div className="carousel"></div>
);
}
}
这里我们创建一个简单的包装类,它将负责处理界面中的所有逻辑。该类的 render 方法将返回整个布局的标记,因此我们将所有其他组件添加到div返回的块中。
你应该注意到,这里我们扩展了React.Component类,以声明这个特定的类将是一个 React 组件。需要指出的是,这是 ES6 中对应React.createClass方法的等效实现。前者只是 ES6 特性集中提供的“语法糖”。因此,在实现组件方法时,你会遇到一些差异。可以参考 Todd Motto 的这篇精彩文章,了解每种选项的详细信息。
既然我们已经完成了类的设置Carousel,就应该开始考虑要在类内部渲染什么内容了。
该ImageSlide组件
这将是一个非常简单的组件,没有任何逻辑。这是 React 组件构建中常见的模式,这类组件被称为“无状态组件”(stateless或“无functional状态组件”)。原因在于,这类组件没有声明状态,因此缺乏控制状态的逻辑。它们最终只是简单的 JavaScript 函数,接收(或不接收)参数,并返回基于这些输入值构建的(或不返回)标记。
我们来看看这个ImageSlide组件长什么样。
const ImageSlide = ({ url }) => {
const styles = {
backgroundImage: `url(${url})`,
backgroundSize: 'cover',
backgroundPosition: 'center'
};
return (
<div className="image-slide" style={styles}></div>
);
}
您可以看到,这个组件的功能并不多。它基本上只需要接收图片 URL,并生成所需的标记,使其作为轮播图中的一张幻灯片即可。
ImageSlide这是一个接收字符串(即图片 URL)的函数,它会创建一个对象来描述组件的样式,并返回填充了我们提供的信息的标记。如前所述,对于您在图片数组中提供的每个 URL,都会有一个这样的组件实例。
关于这个组件的样式,您可以看到图片被设置为背景。不过,您可以根据自己的喜好设置组件的样式,这不会影响本文的目的。
因此,在创建完图像幻灯片的标记后,我们应该将其添加到Carousel组件的渲染方法中。
class Carousel extends React.Component {
render () {
return (
<div className="carousel">
<ImageSlide url={ imgUrl } />
</div>
);
}
}
ImageSlideURL作为对象属性传递给组件props。本文稍后将介绍如何从用作引用的图像数组中正确获取图像 URL。
该Arrow组件
const Arrow = ({ direction, clickFunction, glyph }) => (
<div
className={ `slide-arrow ${direction}` }
onClick={ clickFunction }>
{ glyph }
</div>
);
这个组件更加简洁,因为我们无需在返回其标记之前进行任何设置。我决定在这里使用三个属性,因为这个组件同时用于左箭头和右箭头。因此,它的实现应该更加通用。第一个direction属性会告诉组件在每个实例(left或right)中使用哪个类。第二个属性clickFunction描述了点击每个箭头时应该发生什么,最后一个glyph属性则指向该组件的内容,也就是要渲染的内容。
接下来,我们将这两个箭头添加到Carousel组件中。
class Carousel extends React.Component {
render () {
return (
<div className="carousel">
<Arrow
direction="left"
clickFunction={ this.previousSlide }
glyph="◀" />
<ImageSlide url={ imgUrl } />
<Arrow
direction="right"
clickFunction={ this.nextSlide }
glyph="▶" />
</div>
);
}
}
由此我们可以更好地了解轮播图的最终定价。
接下来,我们应该回到Carousel组件部分,因为还有几件事需要完成。
你会注意到我们向Arrows 组件传递了两个不同的函数。它们负责处理幻灯片的过渡效果。但它们是如何实现的呢?
首先,我们需要了解需要创建哪些状态,才能告诉包装组件每次应该渲染哪张图片。所以,让我们来设置一个constructor函数,以便创建Carousel组件的初始状态。
class Carousel extends React.Component {
constructor (props) {
super(props);
this.state = {
currentImageIndex: 0
};
}
render () {...}
}
函数内部首先要做的constructor就是调用super()它,并将 props 作为参数传递,以便this在此上下文中通过关键字访问属性。目前,props由于构造函数中未使用 props,因此传递对象是可选的。
首先,我们设置一个名为currentImageIndex`set` 的状态0。该状态将保存每次需要在屏幕上渲染的图像的当前索引。这里我们从图像数组中的第一张图像开始。
我们将使用此状态来获取传递给ImageSlide组件的正确 URL。让我们看看如何实现。
class Carousel extends React.Component {
constructor (props) {...}
render () {
return (
<div className="carousel">
<Arrow .../>
<ImageSlide url={ imgUrls[this.state.currentImageIndex] } />
<Arrow .../>
</div>
);
}
}
之后,我们只需要告诉组件如何根据用户交互更新状态。这项工作与我们的Arrow组件相关,由于我们已经传递了两个函数(` previousSlideand` 和 `or` nextSlide)作为属性,现在我们需要实现它们。
你会注意到这两个函数类似。它们的作用都是将currentImageIndex状态更新为 1,要么加 1,要么减 1。这里需要指出一点:有时我们需要重置当前索引的值,因为它最终会达到图像数组的最大或最小索引。因此,检查数组的长度非常重要,以便判断是否需要重置索引。
class Carousel extends React.Component {
constructor (props) {...}
previousSlide () {
const lastIndex = imgUrls.length - 1;
const { currentImageIndex } = this.state;
const shouldResetIndex = currentImageIndex === 0;
const index = shouldResetIndex ? lastIndex : currentImageIndex - 1;
this.setState({
currentImageIndex: index
});
}
nextSlide () {
const lastIndex = imgUrls.length - 1;
const { currentImageIndex } = this.state;
const shouldResetIndex = currentImageIndex === lastIndex;
const index = shouldResetIndex ? 0 : currentImageIndex + 1;
this.setState({
currentImageIndex: index
});
}
render () {...}
}
对于该previousSlide函数,您可以注意到重置条件设置为状态为的情况currentImageIndex,0这意味着如果索引指向数组中的第一张图像,然后用户单击左箭头,则索引应指向数组中的最后一张图像(imgUrls.length - 1)。
其nextSlide作用基本相同,区别在于如果索引当前指向最后一张图片,则应将其重置为指向第一张图片(index = 0)。
在其余所有情况下,这两种方法都只是将上述状态改变 1,以获取上一张图片或下一张图片。
最后,还有一点需要注意。要更新 React 组件的状态,我们需要使用 `updateState`setState方法。这个方法负责告诉 React 它应该更新该组件及其子组件。这是更新用户界面的主要方式。
因此,每当您点击左箭头或右箭头时,您实际上就是在更新状态currentImageIndex,从而用新的图像幻灯片更新您的界面。
但是,为了this在这两个函数中访问关键字,您必须正确地将上下文绑定到它们。一种常见的方法是在组件的constructor方法中。
class Carousel extends React.Component {
constructor (props) {
...
this.nextSlide = this.nextSlide.bind(this);
this.previousSlide = this.previousSlide.bind(this);
}
previousSlide () {...}
nextSlide () {...}
render () {...}
}
最后,我们已经实现了轮播组件的所有逻辑。您可以在这个 CodePen 示例中查看运行演示和完整代码。
PS1:由于这不是本文的目的,我省略了组件使用的样式,因此如果您对此感兴趣,应该查看上面提到的 codepen。
PS2:我仍然想给幻灯片过渡添加一些动画效果,但我希望用 React 的方式来正确地实现,所以这可能会另写一篇帖子 :)
感谢各位阅读!
文章来源:https://dev.to/willamesoares/how-to-build-an-image-carousel-with-react--24na
