本文将演示如何使用 React 全家桶搭建一个 SPA(单页面应用)相册,让你在学习 React 的同时有一个实践项目。我们将使用 React、React Router、Redux、Axios 等前端技术来构建我们的项目。
一、项目需求
在这个项目中,我们将建立一个相册,它有以下几个功能:
- 展示相册列表,并支持添加、删除相册;
- 展示相册内部的照片列表,并支持添加、删除照片;
- 展示具体的照片信息,如尺寸、拍摄日期等。
二、项目架构
项目将采用 MVC 架构:
- 数据模型:相册、照片
- 视图:相册列表、相册详情、照片列表、照片详情
- 控制器:路由、事件
三、环境搭建
我们需要先安装 Node.js 和 npm,然后再安装 React、React Router、Redux 和 Axios,具体如下:
// javascriptcn.com 代码示例 # 安装 create-react-app npm install -g create-react-app # 创建一个 React 项目 create-react-app album # 安装 React Router npm install react-router-dom --save # 安装 Redux 和 React-Redux npm install redux react-redux --save # 安装 Axios npm install axios --save
四、组件设计
我们将设计如下几个组件:
AlbumList
:相册列表Album
:相册详情PhotoList
:照片列表Photo
:照片详情
五、路由设置
我们将设置以下路由:
/
:相册列表/:name
:相册详情/:name/:id
:照片详情
六、数据模型
我们将设计如下两个数据模型:
// javascriptcn.com 代码示例 // 相册 { name: '相册名称', photos: [ // 照片 { id: '照片 ID', title: '照片标题', description: '照片描述', date: '拍摄日期', size: '尺寸', src: '照片 URL' } ] }
七、Redux 状态管理
我们将设计如下的 Redux 状态:
// javascriptcn.com 代码示例 { albumList: [ // 相册列表 { name: '相册名称', photoCount: 3 } ], currentAlbum: { // 当前相册 name: '相册名称', photos: [ // 照片列表 { id: '照片 ID', title: '照片标题', description: '照片描述', date: '拍摄日期', size: '尺寸', src: '照片 URL' } ] }, currentPhoto: { // 当前照片 id: '照片 ID', title: '照片标题', description: '照片描述', date: '拍摄日期', size: '尺寸', src: '照片 URL' } }
八、代码实现
在这里,我们将实现一个简单的相册。你可以在GitHub上查看完整代码。
1. AlbumList
:相册列表
// javascriptcn.com 代码示例 class AlbumList extends React.Component { // 初始化组件状态 state = { newName: '' } // 处理用户输入 handleInputChange = event => { this.setState({ newName: event.target.value }) } // 处理输入框按下 Enter 键 handleInputKeyDown = event => { // 如果是 Enter 键 if (event.keyCode === 13) { // 添加新相册 this.props.addAlbum(this.state.newName) this.setState({ newName: '' }) } } // 渲染相册列表 renderAlbumList() { const { albumList } = this.props if (albumList.length === 0) { // 没有相册 return <div>没有相册</div> } else { // 有相册 return albumList.map(album => ( <div className="album" key={album.name}> <Link to={`/${album.name}`}> <img src={album.photos[0].src} alt={album.name} /> <div className="title">{album.name}</div> <div className="count">{album.photoCount} 张照片</div> </Link> </div> )) } } // 渲染组件 render() { const { newName } = this.state return ( <div className="albumList"> <div className="header"> <div className="title">我的相册</div> <input type="text" placeholder="新建相册" value={newName} onChange={this.handleInputChange} onKeyDown={this.handleInputKeyDown} /> </div> <div className="list">{this.renderAlbumList()}</div> </div> ) } }
2. Album
:相册详情
// javascriptcn.com 代码示例 class Album extends React.Component { // 编辑相册名称 handleAlbumEdit = newName => { this.props.editAlbum(this.props.currentAlbum.name, newName) } // 添加照片 handlePhotoAdd = photo => { this.props.addPhoto(this.props.currentAlbum.name, photo) } // 删除照片 handlePhotoDelete = photoId => { this.props.deletePhoto(this.props.currentAlbum.name, photoId) } // 渲染照片列表 renderPhotoList() { const { photos } = this.props.currentAlbum if (photos.length === 0) { // 没有照片 return <div>没有照片</div> } else { // 有照片 return photos.map(photo => ( <div className="photo" key={photo.id}> <Link to={`/${this.props.currentAlbum.name}/${photo.id}`}> <img src={photo.src} alt={photo.title} /> </Link> <div className="title">{photo.title}</div> <div className="actions"> <Button onClick={() => this.handlePhotoDelete(photo.id)}>删除</Button> </div> </div> )) } } // 渲染组件 render() { const { currentAlbum } = this.props return ( <div className="album"> <div className="header"> <AlbumTitle name={currentAlbum.name} onEdit={this.handleAlbumEdit} /> <PhotoUploader onUpload={this.handlePhotoAdd} /> </div> <div className="list">{this.renderPhotoList()}</div> </div> ) } }
3. PhotoList
:照片列表
// javascriptcn.com 代码示例 class PhotoList extends React.Component { // 渲染照片列表 renderPhotoList() { const { photos } = this.props.currentAlbum return photos.map(photo => ( <div className="photo" key={photo.id}> <img src={photo.src} alt={photo.title} /> <div className="title">{photo.title}</div> <div className="meta"> <div className="date">拍摄日期:{photo.date}</div> <div className="size">尺寸:{photo.size}</div> </div> </div> )) } // 渲染组件 render() { return ( <div className="photoList"> {this.renderPhotoList()} </div> ) } }
4. Photo
:照片详情
// javascriptcn.com 代码示例 class Photo extends React.Component { // 渲染组件 render() { const { currentPhoto } = this.props return ( <div className="photo"> <img src={currentPhoto.src} alt={currentPhoto.title} /> <div className="title">{currentPhoto.title}</div> <div className="meta"> <div className="date">拍摄日期:{currentPhoto.date}</div> <div className="size">尺寸:{currentPhoto.size}</div> <div className="description">{currentPhoto.description}</div> </div> </div> ) } }
5. 路由设置
// javascriptcn.com 代码示例 class App extends React.Component { // 渲染组件 render() { return ( <div className="app"> <Route exact path="/" component={AlbumList} /> <Route exact path="/:name" component={Album} /> <Route exact path="/:name/:id" component={Photo} /> </div> ) } }
九、总结
通过这个项目,我们学习了 React 全家桶的使用,包括 React、React Router、Redux 和 Axios。本文所讲的只是入门级的应用,如果你想要深入学习,可以参考官方文档。
你可以在 这里 查看在线演示,或者在GitHub 上查看完整代码。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/654c86867d4982a6eb5fff8b