前两天看到一个他人写的面试经验一次羞愧难当的阿里前端口试经验,本次换工作不会再寻求任何阿里工作机会,看了下题目本人试着写了下,短短续续差不多写了 1 天终于完后能力了。
实现一个红绿灯的需要
要求如下:
/** 1. 信号灯控制器
用 React 实现一个信号灯(交通灯)控制器,要求:
- 默认状况下,
1.1. 红灯亮 20 秒,并且最初 5 秒闪动
1.2. 绿灯亮 20 秒,并且最初 5 秒闪动
1.3. 黄灯亮 10 秒
1.4. 秩序为 红 - 绿 - 黄 - 红 - 绿 - 黄 - 灯的个数、色彩、持续时间、闪动工夫、灯光秩序都可配置,如:
lights=[{color: ‘#fff’, duration: 10000, twinkleDuration: 5000}, … ]
*/
剖析
js 业务逻辑局部
先遍历一轮即先亮一组灯(默认红绿黄), 再循环遍历灯
- 先要实现一个期待函数
首先有一个期待函数, 即 sleep 函数
罕用的 sleep 函数如下:
function sleep(time){return new Promise((resolve)=>{setTimeout(resolve,time)
})
}
2. 亮一颗灯逻辑
先亮 N
秒再闪 M
秒, 其中 M
可能为 0
async function Light(currentLight) {
const current = currentLight
console.log('current', current)
const currentColor = current.color // 灯的色彩
const currentTime = current.time // 常亮的工夫
const flashTime = current.flashingTime // 闪动工夫
console.log(Date.now(), '开始亮', currentColor, '色的灯')
await sleep(currentTime)
if (flashTime > 0) {console.log(Date.now(), '开始闪', currentColor, '色的灯')
await sleep(flashTime)
}
console.log(Date.now(), '敞开', currentColor, '色的灯')
}
3. 亮一组灯的逻辑
async function roundLight(lights) {for (let item of lights) {console.log('遍历灯:', item.color)
await Light(item)
}
return true // 增加返回值, 标记一轮已完结
}
4. 组与组间有限循环
首先想到的是 setInterval(fn,time)
, 其中time
为组内所有的亮灯加闪灯的工夫的和。
// 计算亮一轮灯须要的工夫和
function getARoundTime(Lights) {
let round = Lights
let totalTime = 0
for (let item of round) {totalTime += item.time + item.flashingTime}
console.log('所有色彩的灯都亮完一轮须要工夫为:', totalTime)
return totalTime
}
// 首次写法是这样的
async function lightInterval(Lights) {
// 获取亮一轮须要的工夫
let totalTime = getARoundTime(Lights)
// 先亮一轮
await roundLight(Lights)
// 每隔一轮从新执行下一轮
setInterval(async (Lights) => {await roundLight(Lights)
}, totalTime)
}
执行后发现逻辑有问题, 亮一轮后会等一轮工夫后才再开始亮,后续失常亮
调整逻辑为先放个一轮的定时器, 让第一轮开始时 setInterval
也开始执行
批改后变成了这样
async function lightInterval(Lights) {let totalTime = getARoundTime(Lights)
setTimeout(() => {setInterval(async () => {await roundLight(Lights)
}, totalTime)
})
await roundLight(Lights)
}
这样貌似能够,然而多看几轮会发现问题, 会呈现上一轮尚未齐全完结就开始执行下一轮的状况
因为每一轮执行的工夫并不齐全准确等于要求的工夫, 会有毫秒级的误差
累积多了误差就显著了, 所以不能应用setInterval
5. 另一种组与组间有限循环的办法
async function roundInterval(Lights) {
// 所有色彩的灯先亮一轮
const roundResult = await roundLight(Lights)
// 完结后调用本身, 继续执行
if (roundResult) {await roundInterval(Lights)
}
}
js 逻辑实现之后该写页面了
页面局部
- 如何在打 console 的中央执行页面内容的刷新?
通过 setState 让状态发生变化, 天然页面就会刷新
// 先在页面上展现灯的状态及色彩
function ShowLight(props) {
return (
<span>
以后展现的是{props.color}, 以后状态是{props.status}
</span>
)
}
应用 state 存储灯的色彩和状态, 先显示一轮逻辑
class LightTwo extends React.Component {constructor(props) {super(props)
this.state = {
currentColor: '',// 保留灯的色彩
currentStatus: '',// 保留灯的状态
}
}
async componentDidMount() {console.log('componentDidMount')
// 先亮一颗灯试一下(取数组里的第一颗灯)
const currentLight = this.props.options[0]
await this.LightState(currentLight)
}
async LightState(currentLight) {
const currentColor = currentLight.color
const currentTime = currentLight.time
const flashTime = currentLight.flashingTime
this.setState({currentColor, currentStatus: 'on'})
console.time('亮' + currentColor + '灯耗时')
await sleep(currentTime)
console.timeEnd('亮' + currentColor + '灯耗时')
if (flashTime > 0) {this.setState({ currentColor, currentStatus: 'flash'})
console.time('闪' + currentColor + '灯耗时')
await sleep(flashTime)
console.timeEnd('闪' + currentColor + '灯耗时')
}
}
render() {
return (
<div>
<ShowLight
status={this.state.currentStatus}
color={this.state.currentColor}
/>
</div>
)
}
}
而后显示多轮逻辑详见完整版
2. 灯的闪动成果实现
通过调整透明度的动画有限循环来实现闪动成果
@keyframes myAnimation {
0% {
opacity: 0;
filter: alpha(opacity=0);
}
100% {
opacity: 1;
filter: alpha(opacity=100);
}
}
.flash {
-webkit-animation: myAnimation 0.6s infinite;
animation: myAnimation 0.6s infinite;
}
3.react 管制款式的展现
通过增加和移除 className 的办法来实现管制款式
render() {
// 其余类名间接写死
// 多个类名用空格拼接
// 拼接以后色彩的类
let classNames = 'demo-1' + this.state.currentColor
if (this.state.currentStatus) {
// 拼接以后状态的类
classNames += ' ' + this.state.currentStatus
}
return (
<div>
<ShowLight
// 把类名传递给组件
classNames={classNames}
status={this.state.currentStatus}
color={this.state.currentColor}
/>
</div>
)
}
// 显示组件
function ShowLight(props) {
return (
<>
<span>
{' '}
以后展现的是{props.color}, 以后状态是{props.status}
</span>
<!-- 不能再这里写 class="foo"(该类间接被废除) 或者 className="foo"(若写在变量前面则变量不失效) 之类的写法 -->
<span className={props.classNames}></span>
</>
)
}
进行红绿灯
// 办法一: 清空函数
closeLight() {
// 设置组间循环为空
this.roundInterval = () => {}
// 设置组内循环为空
this.roundLight = () => {}
// 设置管制单个灯的函数为空
this.LightState = function () {}
this.setState({
currentColor: '',
currentStatus: '',
})
}
// 办法二:批改传入的灯数组为空数组 --- 感觉这种办法更好