[项目实战,源码完整]手把手教你怎么封装功能,React 重写学成在线 IV
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[项目实战,源码完整]手把手教你怎么封装功能,React 重写学成在线 IV相关的知识,希望对你有一定的参考价值。
[项目实战,源码完整]手把手教你怎么封装功能,React 重写学成在线 IV
学完这篇教程,你应该能够掌握以下知识点:
- 二维数组的使用
- 动态修改 CSS(行内 CSS)
- 了解到封装的优势
前情回顾
依旧,目前的项目进度可以在: https://goldenaarcher.com/xczx-react/#/ 上看到。
在上一篇 [项目实战,源码完整]手把手教你怎么封装组件,React 重写学成在线 III 中,首页的内容可以说是完成一半了,此时的效果如下:
这里的实现方法是通过调用两个循环完成的,即首先建立一个伪数据数组:
export const courseSuggestion1 = () => {
const list = [];
for (let i = 0; i < 5; i++) {
const modVal = i % 5;
let course = null;
switch (modVal) {
case 0:
course = newphpCourse(i);
break;
case 1:
course = newandroidCourse(i);
break;
case 2:
course = newAngularCourse(i);
break;
case 3:
case 4:
course = newAndroidHybridCourse(i);
break;
default:
break;
}
if (i === 0) {
course.isHot = true;
} else if (i === 1) {
course.isNew = true;
}
list.push(course);
}
return list;
};
然后再通过两次循环调用去实现:
const CourseSuggestion = (props) => {
// 省略其他内容
return (
<div className="flex">
{courseSuggestion1().map((course) => (
<CourseItem {...course} key={course.id} />
))}
{courseSuggestion1().map((course) => (
<CourseItem {...course} key={course.id} />
))}
</div>
);
};
export default CourseSuggestion;
这其实出现了 2 个问题:
-
代码的重复性
同样的代码出现了两次,并且在复刻以下组件的时候还会出现多复制黏贴:
还有课程页面:
-
细节上的问题——course 的右边距没有处理:
最右边能看到多出了一部分空间:
所以,本节教程的目的就是以下两个:
-
实现课程列表的二次封装,一劳永逸的解决这个问题
-
完成课程以及领域推荐:
回顾一下,领域推荐的是这样的:
课程推荐长的是这样的:
对课程列表的二次封装
进行封装后,对课程列表的渲染应该可以满足本页面内所有需求。
修改数组长度
首先,需要将 courseSuggestion1
的长度由 5 改为 10,去渲染一个完整的课程列表:
export const courseSuggestion1 = () => {
const list = [];
// 其余部分没变,只改了循环体的长度 ↓
for (let i = 0; i < 10; i++) {
// 其余部分没变,只改了循环体的长度 ↑
// 省略
}
return list;
};
二次封装 map 函数
其次,将 array.map()
抽离出来,再进行二次封装。
对于这一步的设计,需要考虑以下 3 点需求:
-
怎么样进行合适的断行?
不断行,所有的课程列表就会挤在同一行:
显然这不是想要看到的结果
-
断行后,怎么解决多余的边距问题?
-
怎么样能够应对其他的需求?
例如说领域榜需要 4 个课程一行,而课程榜需要 5 个课程一行:
领域 课程
请先自己想想看能不能拿出解决方案,开篇就给了提示:
二维数组的使用
解决方案就是通过 二维数组 来解决这个难题:
已知数组的长度,只需要一个参数决定一行渲染多少课程,就可以动态生成一个二维数组,就可以解决 1 和 3 这两个需求。
对于第 2 点,可以通过动态添加行内样式去解决——将一行最后一个课程列表的 margin-right 设置为 0 即可。
具体的封装函数如下:
export const renderCourseInRow = (courseList, numPerRow) => {
const style = {
marginRight: 0,
};
// 二维数组
const tableOfCourse = [];
// 这个变量不是必须的,可以通过 行*列+现在所处的列 得出
// 不过我个人喜欢加一个参数,这样看起来直观一些
let currCourseIndex = 0;
// 第一个循环体,决定多少行数据
// 使用 Math.ceil() 还是 floor() 就根据业务需求决定了
for (let row = 0; row < Math.ceil(courseList.length / numPerRow); row++) {
// 这是每一行的课程
const rowOfCourse = [];
// 第二个循环体,将 对应列 的数据放入 rowOfCourse 中
for (let col = 0; col < numPerRow; col++) {
const currentCourse = courseList[currCourseIndex++];
// 这里修改了一下 CourseItem,让它额外接受一个 style 的参数
rowOfCourse.push(
<CourseItem
{...currentCourse}
style={col === numPerRow - 1 ? style : null}
key={currentCourse.id}
/>
);
}
tableOfCourse.push(
<div className="flex" key={`row${row}`}>
{rowOfCourse}
</div>
);
}
return tableOfCourse;
};
// 新增引用
import { renderCourseInRow } from '../../../common/courseItem/courseItemUtil';
// 主体内容只需要调用 renderCourseInRow,并且传入合适的参数即可
const CourseSuggestion = (props) => {
return (
<div>
<SubHeader
subHeaderName="编程入门"
midConent={subHeaderOl}
checkMore={checkMore}
/>
{renderCourseInRow(courseSuggestion1(), 5)}
</div>
);
};
export default CourseSuggestion;
实现的效果也还不错,每行最后一个的 margin-right 也被清除了:
完成三个课程列表推荐
这部分其实就没什么难度了,只需要创建合适的伪数据,然后调用 3 遍 CourseSuggestion 这个组件即可。
如之前所分析的,除了传入课程的多寡,以及 sub-header 中参数的不同之外,这两个组件从结构上而言是完全一致的。
主页内容如下:
// 省略其他的引用内容
const Home = () => {
return (
<div className="homepage relative">
<HomeBanner />
<div className="container">
<FieldSuggestion />
<div className="homepage-main">
<CourseSuggestion
subHeaderName="精品推荐"
main={courseSuggestion1()}
/>
<CourseSuggestion
subHeaderName="机器学习工程师"
midConent={subHeaderOl}
checkMore={checkMore}
main={mearchineLearning()}
/>
<CourseSuggestion
subHeaderName="前端开发工程师"
midConent={subHeaderOl}
checkMore={checkMore}
main={frontend()}
/>
</div>
</div>
</div>
);
};
export default Home;
效果如下:
封装并完成领域列表
领域列表的结构和课程列表还不太一样,它额外多了两张图片,因此也可以将它单独拆分成一个组件。
对这个组件来说,除了 sub-header 所需要的的参数之外,它自身也许要 3 个参数:
- 左侧的图片
- 右上的图片
- 渲染的 4 个课程
页面的布局依旧可以使用 flex 来实现,而课程的渲染,上面已经封装好了函数,直接调用即可。
这部分的内容实现如下:
import React from 'react';
import SubHeader from '../subHeader';
import { renderCourseInRow } from '../../../common/courseItem/courseItemUtil';
const CourseSuggestionWithBanner = (props) => {
const { subHeaderName, midConent, checkMore, main, topBanner, sideBanner } =
props;
return (
<div className="homepage-suggestion-list">
<SubHeader
subHeaderName={subHeaderName}
midConent={midConent}
checkMore={checkMore}
/>
<div className="flex">
<div className="course-suggestion-side-banner">
<img src={sideBanner} alt="homepage-side-banner" />
</div>
<div>
<div className="course-suggestion-top-banner">
<img src={topBanner} alt="homepage-side-banner" />
</div>
{renderCourseInRow(main, 4)}
</div>
</div>
</div>
);
};
export default CourseSuggestionWithBanner;
封装后的效果:
只需要再微调一下 CSS,这部分的封装就完成了。
最终,这部分的完整展示内容如下:
总结
源码部分在这里:学成在线-react-part3
这一期主要的内容还是在组件的封装上。
二维数组的设计也好,使用行内样式的也用也罢,都是为了能够更进一步的对组件进行更加通用的封装。
封装的过程,也被称之为组件化,其优势除了可以减少代码量之外,也可以让结构变得更加清晰。以完整的 Home 组件为例:
import React from 'react';
import HomeBanner from './homeCarousel';
import './Home.css';
import FieldSuggestion from './fieldSuggestion/fieldSuggestion';
import CourseSuggestion from './courseSuggestion';
import CourseSuggestionWithBanner from './courseSuggestionWithBanner';
import { subHeaderOl, checkMore } from '../../constants/home';
import {
courseSuggestion1,
dataAnalyst,
frontend,
happyPython,
mearchineLearning,
} from '../../constants/courseList';
import topBanner from '../../asset/img/home/happy-python.jpg';
import topBanner2 from '../../asset/img/home/python-ai.png';
import sideBanner from '../../asset/img/home/side-banner.png';
import sideBanner2 from '../../asset/img/home/side-banner2.png';
const Home = () => {
return (
<div className="homepage relative">
<HomeBanner />
<div className="container">
<FieldSuggestion />
<div className="homepage-main">
<CourseSuggestion
subHeaderName="精品推荐"
main={courseSuggestion1()}
/>
<CourseSuggestionWithBanner
subHeaderName="编程入门"
midConent={subHeaderOl}
checkMore={checkMore}
main={happyPython()}
topBanner={topBanner}
sideBanner={sideBanner}
/>
<CourseSuggestionWithBanner
subHeaderName="数据分析师"
midConent={subHeaderOl}
checkMore={checkMore}
main={dataAnalyst()}
topBanner={topBanner2}
sideBanner={sideBanner2}
/>
<CourseSuggestion
subHeaderName="机器学习工程师"
midConent={subHeaderOl}
checkMore={checkMore}
main={mearchineLearning()}
/>
<CourseSuggestion
subHeaderName="前端开发工程师"
midConent={subHeaderOl}
checkMore={checkMore}
main={frontend()}
/>
</div>
</div>
</div>
);
};
export default Home;
它的结构,也是比较简单直接的:
|- Home
| |- HomeBanner
| |- container
| | |- FieldSuggestion
| | |- CourseSuggestion
| | |- CourseSuggestionWithBanner
| | |- CourseSuggestionWithBanner
| | |- CourseSuggestion
| | |- CourseSuggestion
从日常开发的角度而言,这样拆分的结构也算是比较合理。虽然说 subHeaderName
值都是写死的,但是如之前所言,大多数情况下这里可以来自于 constant
中的数据,十有八九都会从其他的地方动态地传送过来。
也就是说,但凡结构不会发生任何的变化,课程推荐中课程的变化,或是明天领域推荐从 编程入门 换成了 人工智能,这都不会涉及到代码层的变动。
这也就意味着不需要停止服务器去重新部署,从而增强了服务层的稳定性与可靠性。
最后,首页还剩下最后两个模块就写完了,一个是老师列表的渲染,另一个是左侧滑动部分的渲染。感兴趣的可以试试看自己做一下,应该说不是很难的。
老师列表的逻辑和课程列表的渲染很像,多出来的一点内容就是在鼠标悬浮时会显示出完整的信息。
如果能够完成首页的内容的话,也就说可以相对完成比较复杂的业务逻辑了——当然,这里指的只是 PC 端。
以上是关于[项目实战,源码完整]手把手教你怎么封装功能,React 重写学成在线 IV的主要内容,如果未能解决你的问题,请参考以下文章
游戏开发实战手把手教你在Unity中使用lua实现红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含工程源码)
游戏开发实战手把手教你在Unity中使用lua实现红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含工程源码)
Pytthon实战------黑白老照片上色,手把手教你用Python怎么玩儿!
Linux——Linux驱动之设备树下platform总线驱动编写实战(手把手教你设备树下platform总线利用GPIO控制蜂鸣器完整实现过程)