jjzjj

预测2024年之后的前端开发模式

卡颂 2023-03-30 原文

大家好,我卡颂。

最近AIGC​(AI Generated Content,利用AI​生成内容)非常热,技术圈也受到了很大冲击。目前来看,利用LLM​(Large Language Model,大语言模型)辅助开发还停留在非常早期的阶段,主要应用是「辅助编码」,即「用自然语言输入需求,模型输出代码」。更近一步的探索也仅仅是在此基础上的一层封装(比如copilot X​、cursor)。

但即使在如此早期阶段,也对开发者的心智产生极大震撼,「AI让程序员失业」这样的论调甚嚣尘上。

LLM的爆发对前端意味着什么?本文尝试预测一波2024年之后的前端开发模式,这个预测遵循如下原则:

  • 尊重技术客观发展规律。以当前已有技术为基础预测,而不是将预测建立在某种虚无缥缈的高端技术,或者假想某些技术突破重大瓶颈。
  • 尊重人性。程序员只是谋生的职业,新的开发模式即使再厉害,如果让程序员赚不到钱,那也是很难推广开的。

范式迁移的本质

为了预测未来,先看看我们是如何走到现在的。

在前端开发领域,我们经历了从jQuery为代表的「面向过程编程」向前端框架为代表的「状态驱动」模式的迁移。

当问到「该选Vue还是React开发?」,这样的问题会引起很大争议,但如果问到「该选jQuery还是框架开发?」,这样的问题就不会有太多争议。

为什么前端领域普遍接受了这种范式的迁移?在我看来,有两个原因:

1、开发效率提高

这一点毋需多言,相信前端同学都有体会。

2、门槛提高

​「面向过程编程」是非常浅显易懂的开发模式。君不见,曾经的前端靠一本「锋利的jQuery」就能打天下。相比之下,「状态驱动」就有一定学习门槛。

当一项有一定门槛的技术(这里指前端框架)变为行业事实上的标准时,行业门槛就提升了,这为从业者构筑了行业壁垒。

事实上,正是由于:

  1. web应用复杂度提高
  2. 前端框架的流行

才让后端工程师工作职责中的view层,分化出前端工程师这一职业。

对于前端领域来说,只有同时平衡了「提效」与「提高门槛」的技术,才会被市场(这里的消费者指前端工程师)接受。

举个反例,Angular全家桶的模式虽然提高了开发效率,但是同时,门槛提高太多了。

而且更糟的是,Angular​中的很多概念都是从「后端」迁移而来,作为一款前端框架,对后端更亲和且门槛高,这对本身就是从后端view层中分化出的前端工程师来说,是比较排斥的。

再举个反例 —— Vue​。有同学会说,Vue这么流行的前端框架,你说他是反例?

还是从「提效」与「提高门槛」的角度看,Vue​提效的同时,由于其模版语法、响应式更新等特性,他是降低了开发门槛的,这意味着使用Vue时:

  1. 同样是开发业务,老前端与新前端差距不大
  2. 必要时后端经过简单的学习,也能接手部分需求

重申一下,我并不是说Vue不好,相反,他是很优秀的前端框架。这里只是从人性的角度分析,并且这个分析很有可能是主观、带有偏见的。

再看个正面例子 —— React Hooks​。Hooks​对开发效率、组件复用性以及他对React未来发展的影响这里不赘述了。主要聊聊「提高门槛」:

  1. 一方面,什么时候封装自定义Hook,如何封装自定义Hook,如何规避Hook的坑,老前端与新前端有比较大的差异
  2. 更重要的是,后端改改JSX还行,要改基于Hooks的组件逻辑,是有一定难度的

既提效,又提高门槛,我认为这才是Hooks在前端领域火热的原因。

同样的原因,从人性的角度,我很看好Vue Composition API

所以,前端编程范式迁移的本质是:把握「提高效率」与「提高门槛」之间的平衡。

这个结论会成为后面预测未来开发模式的依据。

当范式无法再迁移时

当前端框架成为事实上的标准后很长一段时间,业界也在不断探索新的开发范式。

有一种开发模式每过几年都会被搬出来炒一遍,他就是「低代码」。用我们上面的结论来分析下:在市场选择的情况下,先抛开「低代码是否能提高效率」不谈,显然他的目的是「降低门槛」。

从人性的角度出发,他就很难在程序员群体中自发传播开。

那么,如果没有新的范式出现,会发生什么事情?会内卷。

我们会发现,这几年前端的发展轨迹,就是在重复一件事:

  1. 围绕前端框架周边,不断探索各细分领域的最佳实践
  2. 当探索出最佳实践后,就把他集成到框架中

举个例子,React Router​作为React技术栈中「路由」这一细分领域的一个开源库,经过长期迭代,逐渐成为主流路由方案之一。

React Router​团队基于React Router​开发出Remix​这一React框架。

这么做,在没有新的范式出现前,也能基于当前范式(前端框架),达到上述2个目的:

  • 提高效率:框架集成了最佳实践,开发效率更高
  • 提高门槛:除了学习React,还得学习新的上层框架

类似的,各种CSS​解决方案(比如tailwind css)也是同样的道理:

  • 提高效率:提高CSS编写效率
  • 提高门槛:新的概念、语法需要学习

那么,未来围绕「提高效率」与「提高门槛」的平衡,前端开发模式会如何发展呢?

从考虑范式到考虑流程

首先,我认为,在有限的未来,不会出现新的更先进的范式能让前端领域普遍认可并大规模迁移(就像从jQuery到前端框架的迁移)。

那么,为了提高效率,除了「改变范式」与「范式内 内卷」两个选择外,还有个选择 —— 让整个开发流程提效。

从需求文档到最终代码,存在4级抽象:

  1. PM用自然语言编写的需求文档
  2. 需求评审时,PM给开发描述需求后,开发脑海里形成的业务逻辑
  3. 开发根据业务逻辑划分各个模块或组件
  4. 开发实现各个模块或组件的具体代码

当前我们使用LLM​辅助编程时(比如以chatGPT为例),主要是用自然语言输入模块或组件业务逻辑,再让模型输出具体代码。也就是借助模型自动完成从3到4级抽象的转变。

比如说下图我们让chatGPT实现一个计时器:

这个计时器可能是我们需求中的某个模块,在此chatGPT帮我们完成了从抽象3(实现一个计时器组件)到抽象4(计时器组件的代码)。

如果仅仅到这一步,只能说这是个更高效的辅助工具,并不能达到「整个开发流程提效」的程度。为了达到这种程度,我们需要让LLM帮我们完成从抽象1到4的整个过程。

LLM如何完成4级抽象转换

接下来我们来看,基于当前已有的模型,如何完成抽象1到抽象4的自动转换。

首先,来看抽象1(PM用自然语言编写的需求文档)。chatGPT当前已经掌握基础的理解能力,所以他是能够理解需求文档的含义的。

下图是我从网上找的某需求文档中的登录功能流程图:

以当前主流的GPT-3.5​举例,虽然GPT-3.5​不能理解图片(不能理解需求文档中的流程图),但我们可以将流程图用文字描述出来(最新的GPT-4已经拥有「理解图片含义」的能力)。

上述登录功能流程图可以用文字概括为:

  1. 打开App后有3个选项,分别是“账号密码登录”、“快捷登录”、“第三方登录”。
  2. 选择“第三方登录”,进入第三方,同意授权后登录成功。
  3. 选择“快捷登录”,输入手机号和验证码并选择身份,点击登录后登录成功。
  4. 选择“账号密码登录”,输入手机号,如果已注册,输入密码,点击登录后登录成功。
  5. 选择“账号密码登录”,输入手机号,如果未注册,进入注册页,输入手机号,如果手机号已注册,回到“账号密码登录”。
  6. 选择“账号密码登录”,输入手机号,如果未注册,进入注册页,输入手机号,如果手机号未注册,填写手机号、验证码、密码、姓名、选择身份,点击注册,完毕。

抽象1到抽象2

如何完成从抽象1到抽象2(业务逻辑)的转变呢?换句话说,如何用一种介于「自然语言与实际代码」之间的规范描述业务逻辑?

这种规范应该拥有完备的数据结构(类似JSON​、XML),因为这样会带来很多好处:

  • 相比于自然语言,用规范的数据结构表示的业务逻辑能够传达更准确的意图。
  • 业务需求的不断增多,仅仅对应数据结构体积的增大,即使再复杂的业务,只需要分批将业务逻辑代表的数据结构投喂给模型,模型就能完全理解我们的业务。
  • 数据结构可以保存在变量中,通过变量名就能指代业务逻辑,无需再用自然语言大段的向模型描述业务逻辑。

我们可以利用SCXML​(State Chart XML)格式。SCXML​是由W3C​定义的一种「表示状态机」的XML格式,他能够表示状态之间的变化。

前端应用的本质其实就包括两部分:

  • 状态的变化。
  • 状态到视图的映射。

其中「状态到视图的映射」框架已经帮我们做了。所以,只要能表示「状态的变化」,其实就能表示业务逻辑。

现在,我们让chatGPT​将流程图翻译为SCXML格式:

得到如下结构(你不用细看,了解个大概就行):

<scxml version="1.0" xmlns="http://www.w3.org/2005/07/scxml">
<state id="mainMenu">
<transition event="THIRD_PARTY_LOGIN" target="thirdPartyLogin" />
<transition event="QUICK_LOGIN" target="quickLogin" />
<transition event="ACCOUNT_LOGIN" target="accountLogin" />
</state>

<state id="thirdPartyLogin">
<onentry>
<send event="AGREE_AUTHORIZATION" />
<send event="LOGIN_SUCCESS" />
</onentry>
</state>

<state id="quickLogin">
<onentry>
<send event="INPUT_PHONE_NUMBER" />
<send event="INPUT_VERIFICATION_CODE" />
<send event="SELECT_IDENTITY" />
<send event="LOGIN_SUCCESS" />
</onentry>
</state>

<state id="accountLogin">
<state id="inputPhoneNumber">
<transition event="PHONE_NUMBER_SUBMITTED" target="checkRegistered" />
</state>

<state id="checkRegistered">
<onentry>
<send event="CHECK_REGISTERED" />
</onentry>

<transition event="REGISTERED" target="inputPassword" />
<transition event="UNREGISTERED" target="register" />
</state>

<state id="inputPassword">
<onentry>
<send event="INPUT_PASSWORD" />
<send event="LOGIN_SUCCESS" />
</onentry>
</state>

<state id="register">
<state id="inputPhoneNumber">
<transition event="PHONE_NUMBER_SUBMITTED" target="checkRegistered" />
</state>

<state id="checkRegistered">
<onentry>
<send event="CHECK_REGISTERED" />
</onentry>

<transition event="REGISTERED" target="inputPassword" />
<transition event="UNREGISTERED" target="inputUserInfo" />
</state>

<state id="inputUserInfo">
<onentry>
<send event="INPUT_PHONE_NUMBER" />
<send event="INPUT_VERIFICATION_CODE" />
<send event="INPUT_PASSWORD" />
<send event="INPUT_NAME" />
<send event="SELECT_IDENTITY" />
<send event="REGISTER_SUCCESS" />
</onentry>
</state>
</state>
</state>
</scxml>

至此,我们完成了抽象1到抽象2的转变。

抽象2到抽象3

SCXML格式没法直接在JS代码中使用。为了用代码实现逻辑,我们需要使用遵循SCXML规范的库。xstate是JS中比较流行的状态机开源库。

所以接下来我们让chatGPT将上述SCXML格式转换为xstate语法:

得到结果(同样,具体代码你不用在意,了解我想表达的转换意思就行):

import { Machine, sendParent } from 'xstate';

const loginMachine = Machine({
id: 'login',
initial: 'mainMenu',
states: {
mainMenu: {
on: {
THIRD_PARTY_LOGIN: 'thirdPartyLogin',
QUICK_LOGIN: 'quickLogin',
ACCOUNT_LOGIN: 'accountLogin'
}
},
thirdPartyLogin: {
onEntry: [
sendParent('AGREE_AUTHORIZATION'),
sendParent('LOGIN_SUCCESS')
]
},
quickLogin: {
onEntry: [
sendParent('INPUT_PHONE_NUMBER'),
sendParent('INPUT_VERIFICATION_CODE'),
sendParent('SELECT_IDENTITY'),
sendParent('LOGIN_SUCCESS')
]
},
accountLogin: {
initial: 'inputPhoneNumber',
states: {
inputPhoneNumber: {
on: {
PHONE_NUMBER_SUBMITTED: 'checkRegistered'
}
},
checkRegistered: {
onEntry: sendParent('CHECK_REGISTERED'),
on: {
REGISTERED: 'inputPassword',
UNREGISTERED: 'register.inputPhoneNumber'
}
},
inputPassword: {
onEntry: [
sendParent('INPUT_PASSWORD'),
sendParent('LOGIN_SUCCESS')
]
},
register: {
initial: 'inputPhoneNumber',
states: {
inputPhoneNumber: {
on: {
PHONE_NUMBER_SUBMITTED: 'checkRegistered'
}
},
checkRegistered: {
onEntry: sendParent('CHECK_REGISTERED'),
on: {
REGISTERED: 'inputPassword',
UNREGISTERED: 'inputUserInfo'
}
},
inputPassword: {
onEntry: [
sendParent('INPUT_PASSWORD'),
sendParent('REGISTER_SUCCESS')
]
},
inputUserInfo: {
onEntry: [
sendParent('INPUT_PHONE_NUMBER'),
sendParent('INPUT_VERIFICATION_CODE'),
sendParent('INPUT_PASSWORD'),
sendParent('INPUT_NAME'),
sendParent('SELECT_IDENTITY'),
sendParent('REGISTER_SUCCESS')
]
}
}
}
}
}
}
});

export default loginMachine;

这段代码我们可以直接粘贴到xstate的可视化编辑器[1]中查看:

图中初始状态可以转移到3个状态(这些状态都是chatGPT生成的),其中:

  • QUICK_LOGIN —— 快捷登录
  • ACCOUNT_LOGIN —— 账号密码登录
  • THIRD_PARTY_LOGIN —— 第三方登录

每个状态接下来的变化逻辑都清晰可见。比如,当进入ACCOUNT_LOGIN状态后,后续会根据是否登录(UNREGISTERED、REGISTERED)进入不同逻辑:

也就是说,chatGPT理解了需求文档想表达的业务逻辑后,将业务逻辑转换成代码表示。

读者可将上述xstate代码复制到可视化编辑器中看到效果。

抽象3到抽象4

接下来,我们只需要让chatGPT​根据上述xstate状态机生成组件代码即可。

这时有同学会问:chatGPT​对话有token限制,没法生成太多代码怎么办?

实际上,这可能并不是坏事。在我曾经供职的一家公司,前端团队有条不成文的规矩 —— 如果一个组件超过200行,那你就应该拆分他。

同样的,如果chatGPT​生成的组件超过了token限制,那么应该让他拆分新的组件。

拆分组件的前提是 —— chatGPT​需要懂业务逻辑。显然,他已经懂了xstate数据结构所代表的业务逻辑。

更妙的是,我们可以让chatGPT​将「SCXML格式转换而来的xstate数据结构」保存在一个变量中,在后续对话中,我们用一个变量名就能指代他背后所表示的业务逻辑(这里保存在变量m中)。

当我们要生成业务组件代码时,让chatGPT从模块中导出m实现组件逻辑:

对于实际场景下比较复杂的需求,经过从抽象1到抽象3的转换,我们会得到「代表业务逻辑的不同变量」,比如:

  • signin变量代表登录逻辑。
  • login变量代表注册逻辑。
  • PopupAD变量代表弹窗广告逻辑。

如果弹窗广告的逻辑和是否登录相关,那么要实现弹窗广告组件代码只需要告诉chatGPT:

根据signin​、PopupAD​实现弹窗广告的react​组件,其中signin​变量由xxx​模块导出,PopupAD​变量由yyy导出。

如果你司使用其他框架,只需将其中react换成其他框架名即可。当大家还在争论哪个框架更优秀时,LLM已经悄悄帮开发者实现了「框架自由」。

新开发模式的优势

让我们从「提高效率」与「提高门槛」的角度分析这种新开发模式的优势。

提高效率

首先,这种新模式能显著提高开发效率。本质来说,他将前端工程师从「实现需求」的角色转变为「review代码」的角色。

极端的讲,当需求评审会结束的那一刻,第一版前端代码就生成了。

其次,他能解放部分测试同学的生产力(抢部分测试同学的活儿)。对于维护过屎山代码的同学,肯定遇到过这样的场景:明明只是改动一个小需求,测试问你改动影响的范围,你自己都不清楚会有多大影响,为了稳妥起见只能让测试覆盖更大的回归测试范围。

在使用基于状态机的开发模式后,任何改动会造成的影响在状态图中都清晰可见。同时,由于代码逻辑的实现基于状态机,可以据此自动生成端到端的测试用例,模型也能根据状态机描述的逻辑自己补足其他单测。

提高门槛

接下来,我们从「提高门槛」的角度分析。

首先,能够对模型生成的代码进行查漏补缺本身就要求开发者有一定前端开发水平。

其次,这种开发模式引入了新的抽象层 —— 状态机,这无疑会增加上手门槛。

但这都不是最重要的,最重要的是 —— 这套模式强迫前端开发需要更懂业务。

以前,拿到产品的需求文档后,你可以在做的过程中遇到不懂的再问产品。使用新的开发模式后,你必须很懂业务,做到「在需求评审时就能指出需求文档中不合理的地方」。

因为当需求评审结束后,你会将这份需求文档投喂给模型直接生成业务代码(中间会经历「生成SCXML」、「生成xstate数据结构」、「保存xstate变量」、使用变量生成组件代码)。

当大家技术水平旗鼓相当时,「懂业务」才是前端的核心竞争力。

综上,这套开发模式在极大提高效率的同时提高了门槛,我认为在未来很有可能成为主流前端开发模式。

参考资料

[1]xstate的可视化编辑器:https://stately.ai/viz。

有关预测2024年之后的前端开发模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  5. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  6. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  7. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  8. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  9. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  10. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

随机推荐