2024年大型前端项目降低复杂度新思路,2024年最新阿里前端高频面试真题库

总结

根据路线图上的重点去进行有针对性的学习,在学习过程中,学会写笔记,做总结。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

这里分享一些前端学习笔记:

  • html5 / css3 学习笔记

  • JavaScript 学习笔记

  • Vue 学习笔记

1.3. 状态机 VS 传统编码 示例


下面采用一个小需求来对比一下区别。

1.3.1. 需求描述

根据输入的关键字进行搜索,并将搜索结果显示出来。如下图所示:

ce4db4ae3f7b4e189683e44459ff6fe0.png

1.3.2. 基于传统编码

根据关键字拿到请求结果,再将结果塞回去就行了,代码如下:

function onSearch(keyword) {

fetch(SEARCH_URL + “?keyword=” + keyword).then((data) => {

this.setState({ data });

});

}

看似几行代码就把这个需求搞定了,但其实还有一些其他问题要处理。如果接口响应比较慢,则需要给一个用户预期的交互,如 Loading 效果:

function onSearch(keyword) {

this.setState({

isLoading: true,

});

fetch(SEARCH_URL + “?keyword=” + keyword).then((data) => {

this.setState({ data, isLoading: false });

});

}

还会发生出请求出错的情况:

function onSearch(keyword) {

this.setState({

isLoading: true,

});

fetch(SEARCH_URL + “?keyword=” + keyword)

.then((data) => {

this.setState({ data, isLoading: false });

})

.catch((e) => {

this.setState({

isError: true,

});

});

}

当然,不能忘记把 Loading 关掉:

function onSearch(keyword) {

this.setState({

isLoading: true,

});

fetch(SEARCH_URL + “?keyword=” + keyword)

.then((data) => {

this.setState({ data, isLoading: false });

})

.catch((e) => {

this.setState({

isError: true,

isLoading: false,

});

});

}

我们每次搜索时,还需要把错误清除:

function onSearch(keyword) {

this.setState({

isLoading: true,

isError: false,

});

fetch(SEARCH_URL + “?keyword=” + keyword)

.then((data) => {

this.setState({ data, isLoading: false });

})

.catch((e) => {

this.setState({

isError: true,

isLoading: false,

});

});

}

这就结束了么,是不是我们把所有的 Bug 都考虑进去了?并没有。当用户在等待搜素请求的时候,不应该再去搜索,所以搜索结果返回前,禁止再次发送请求:

function onSearch(keyword) {

if (this.state.isLoading) {

return;

}

this.setState({

isLoading: true,

isError: false,

});

fetch(SEARCH_URL + “?keyword=” + keyword)

.then((data) => {

this.setState({ data, isLoading: false });

})

.catch((e) => {

this.setState({

isError: true,

isLoading: false,

});

});

}

可以看到,应用的复杂度在不断变大,可能你经历的场景比这个小示例还要复杂的多的多。如果因为搜索接口特别慢,用户希望有一个中断搜索的功能,那么新的需求又来了:

function onSearch(keyword) {

if (this.state.isLoading) {

return;

}

this.fetchAbort = new AbortController();

this.setState({

isLoading: true,

isError: false,

});

fetch(SEARCH_URL + “?keyword=” + keyword, {

signal: this.fetchAbort.signal,

})

.then((data) => {

this.setState({ data, isLoading: false });

})

.catch((e) => {

this.setState({

isError: true,

isLoading: false,

});

});

}

function onCancel() {

this.fetchAbort.abort();

}

不能落下对 catch 的特殊处理,因为中断请求会触发 catch:

function onSearch(keyword) {

if (this.state.isLoading) {

return;

}

this.fetchAbort = new AbortController();

this.setState({

isLoading: true,

isError: false,

});

fetch(SEARCH_URL + “?keyword=” + keyword, {

signal: this.fetchAbort.signal,

})

.then((data) => {

this.setState({ data, isLoading: false });

})

.catch((e) => {

if (e.name == “AbortError”) {

this.setState({

isLoading: false,

});

} else {

this.setState({

isError: true,

isLoading: false,

});

}

});

}

function onCancel() {

this.fetchAbort.abort();

}

最后还要处理没有值的情况:

function onSearch(keyword) {

if (this.state.isLoading) {

return;

}

this.fetchAbort = new AbortController();

this.setState({

isLoading: true,

isError: false,

});

fetch(SEARCH_URL + “?keyword=” + keyword, {

signal: this.fetchAbort.signal,

})

.then((data) => {

this.setState({ data, isLoading: false });

})

.catch((e) => {

if (

e &&

e.name == “AbortError”

) {

this.setState({

isLoading: false,

});

} else {

this.setState({

isError: true,

isLoading: false,

});

}

});

}

function onCancel() {

if (

this.fetchAbort.abort &&

typeof this.fetchAbort.abort == “function”

) {

this.fetchAbort.abort();

}

}

仅仅这么简单的一个小需求,从开始几行代码就可以完成,到最终判断各种边界完成的代码,对比一下,如下图所示:

7faac2c19cec0fde4801d918c2132464.png

可以看到,这种包含各种 flag 变量和嵌套着各种 if/else 的代码,会越来越难维护,所有的逻辑只存在于你的脑子里。当你写测试的时候必须从头再梳理一遍代码逻辑,才能写出来。

由于业务的高频变化,很多业务开发人员是不写单元测试的,因为成本太高太高,这也导致了交接代码时,别人去理解你的代码是一件很困难的事。写久了,你自己都可能读不懂代码里面的逻辑了。

这样会导致:

  • 难以测试

  • 难以阅读

  • 可能含有隐藏的 Bug

  • 难以扩展

  • 新功能增加时还会使逻辑进一步混乱

1.3.3. 基于状态机

看一下我们用状态机的做法。记住流程:梳理出有哪些状态,每个状态有哪些事件,经历了这些事件又会转换到什么状态。

下面是用 XState 状态机工具的 JSON 描述:

{

“initial”: “空闲”,

“states”: {

“空闲”: {

“on”: {

“搜索”: “搜索中”

}

},

“搜索中”: {

“on”: {

“搜索成功”: “成功”,

“搜索失败”: “失败”,

“取消”: “空闲”

}

},

“成功”: {

“on”: {

“搜索”: “搜索中”

}

},

“失败”: {

“on”: {

“搜索”: “搜索中”

}

}

}

}

没错,就这几行代码就描述清楚所有的关系了。并且,可以把它可视化出来,如下图所示:

8e618bd853c663670cc4d18393267871.png

可以看到状态之间表达的非常清晰,结合到 View 中,也不需要再去编写复杂的 flag 及 if/else 了,View 中只需要知道当前是什么状态,已及将事件发送到状态机就可以了,其他什么都不需要做。在新增或者修改需求的情况下,只需要对状态进行新增或者编排就可以了。

而且可视化后,有以下变化:

  • 清晰的看到有哪些状态

  • 清晰的看到每个状态可以接受哪些事件

  • 清晰的看到接受到事件后会转移到什么状态

  • 清晰的看到到达某个状态的路径是怎么样的

2. 解决协作的问题

===========

另一个很大的问题是解决协作问题,主要包括:

  • 与测试开人员的协作沟通

  • 与 PD 人员的协作沟通

  • 与其他前端开发人员的协作沟通

  • 与用户的协作沟通

这里就需要引用一个可视化的概念了。「可视化,是利用人眼的感知能力对数据进行交互的可视表达以增强认知的技术」 。

所以很大程度上,可视化可以解决一大部分协作问题。当然,必须要确定把什么进行可视化才是有意义的。

要想可视化,状态工具就需要具备可序列化的能力。这也是 Redux 之类的状态管理工具缺乏的,主要有以下几方面问题:

  • 不具备可视化的能力

  • 状态和数据混在一起

  • 所有的状态都是平级的,无法描述状态之间的关系

2.1. 状态图


回到状态机。你单纯用状态机去写代码,需求数量上去了,状态多了,会面临 “状态爆炸” 问题,依然很难维护,且阅读成本巨大。

当然,这个场景其实很早之前就有人考虑到了,1987 年,Harel 就发表论文,解决复杂状态机可视化的问题,在状态机的基础上进一步增强,提出状态图的概念。随后,由微软、IBM、惠普等多家公司,从 2005 到 2015 年花了 10 年时间制定了规范,并推出了 W3C 的 State Chart XML (SCXML) 规范,至此基本稳定,各家编程语言也基于此规范进行了状态图的封装。

看一下,状态机、状态图和手写代码复杂度的对比,如下图所示:

0fe48216e2af037fb4305c023ebc97b2.png

从图中可以看到:

  • 传统编码方式,随着状态和逻辑的增加,复杂度是线性增长的。

  • 使用状态机,前期复杂度很底,但随着状态的增多,“状态爆炸”现象的出现,复杂度也急剧增长。

  • 使用状态图,虽然前期成本略高,但后期的状态和逻辑的增长,基本不太会影响它的复杂度。

前面给状态机画的图,就是状态图。

状态图大概长这样,如下图所示:

beb044bec8068a8a34b1674b366925de.png

主要包括:

  • 状态

  • 原子状态

  • 复合状态

  • 条件状态

  • 最终状态

  • 历史状态

  • 初始状态

  • 并行状态

  • 伪/瞬间状态

  • 转换

  • 自动转换

  • 延迟转换

  • 自身转换

  • 内部转换

  • 操作

  • 自定义操作

  • 进入操作

  • 退出操作

  • 数据操作

  • 日志操作

  • 事件

  • 生成事件

  • 延迟时间

  • 条件

  • 数据

  • 调用

即使状态非常复杂,也可以通过状态图的模式进行聚合、分组、细化,还可以通过 Actor 模型进行划分,不会发生 “状态爆炸” 现象。

2.2. 文档化


目前对项目需求的描述主要有:

最后

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

给大家分享一些关于HTML的面试题。


beb044bec8068a8a34b1674b366925de.png

主要包括:

  • 状态

  • 原子状态

  • 复合状态

  • 条件状态

  • 最终状态

  • 历史状态

  • 初始状态

  • 并行状态

  • 伪/瞬间状态

  • 转换

  • 自动转换

  • 延迟转换

  • 自身转换

  • 内部转换

  • 操作

  • 自定义操作

  • 进入操作

  • 退出操作

  • 数据操作

  • 日志操作

  • 事件

  • 生成事件

  • 延迟时间

  • 条件

  • 数据

  • 调用

即使状态非常复杂,也可以通过状态图的模式进行聚合、分组、细化,还可以通过 Actor 模型进行划分,不会发生 “状态爆炸” 现象。

2.2. 文档化


目前对项目需求的描述主要有:

最后

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

给大家分享一些关于HTML的面试题。

[外链图片转存中…(img-TTbiwwS4-1715633354945)]
[外链图片转存中…(img-dKWCtH02-1715633354946)]

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值