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

4次阅读

共计 8032 个字符,预计需要花费 21 分钟才能阅读完成。

大家好,我卡颂。

最近 AIGC(AI Generated Content,利用AI 生成内容)十分热,技术圈也受到了很大冲击。目前来看,利用 LLM(Large Language Model,大语言模型)辅助开发还停留在十分晚期的阶段,次要利用是 辅助编码 ,即 用自然语言输出需要,模型输入代码。更近一步的摸索也仅仅是在此基础上的一层封装(比方copilot Xcursor)。

但即便在如此晚期阶段,也对开发者的心智产生极大震撼,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 HooksHooks对开发效率、组件复用性以及他对 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(业务逻辑)的转变呢?换句话说,如何用一种介于 自然语言与理论代码 之间的标准形容业务逻辑?

这种标准应该领有齐备的数据结构(相似JSONXML),因为这样会带来很多益处:

  • 相比于自然语言,用标准的数据结构示意的业务逻辑可能传播更精确的用意
  • 业务需要的一直增多,仅仅对应数据结构体积的增大,即便再简单的业务,只须要分批将业务逻辑代表的数据结构投喂给模型,模型就能齐全了解咱们的业务
  • 数据结构能够保留在变量中,通过变量名就能指代业务逻辑,无需再用自然语言大段的向模型形容业务逻辑

咱们能够利用 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 标准的库。xstateJS 中比拟风行的状态机开源库。

所以接下来咱们让 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 的可视化编辑器中查看:

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

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

每个状态接下来的变动逻辑都清晰可见。比方,当进入 ACCOUNT_LOGIN 状态后,后续会依据是否登录(UNREGISTEREDREGISTERED)进入不同逻辑:

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

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

形象 3 到形象 4

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

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

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

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

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

更妙的是,咱们能够让 chatGPTSCXML 格局转换而来的 xstate 数据结构 保留在一个变量中,在后续对话中,咱们用一个变量名就能指代他背地所示意的业务逻辑(这里保留在变量 m 中)。

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

对于理论场景下比较复杂的需要,通过从形象 1 到形象 3 的转换,咱们会失去 代表业务逻辑的不同变量,比方:

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

如果弹窗广告的逻辑和是否登录相干,那么要实现弹窗广告组件代码只须要通知chatGPT

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

如果你司应用其余框架,只需将其中 react 换成其余框架名即可。当大家还在争执哪个框架更优良时,LLM曾经轻轻帮开发者实现了 框架自在

新开发模式的劣势

让咱们从 提高效率 进步门槛 的角度剖析这种新开发模式的劣势。

提高效率

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

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

其次,他能解放局部测试同学的生产力(抢局部测试同学的活儿)。对于保护过屎山代码的同学,必定遇到过这样的场景:明明只是改变一个小需要,测试问你改变影响的范畴,你本人都不分明会有多大影响,为了稳当起见只能让测试笼罩更大的回归测试范畴。

在应用基于状态机的开发模式后,任何改变会造成的影响在状态图中都清晰可见。同时,因为代码逻辑的实现基于状态机,能够据此主动生成端到端的测试用例,模型也能依据状态机形容的逻辑本人补足其余单测。

进步门槛

接下来,咱们从 进步门槛 的角度剖析。

首先,可能对模型生成的代码进行查漏补缺自身就要求开发者有肯定前端开发程度。

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

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

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

因为当需要评审完结后,你会将这份需要文档投喂给模型间接生成业务代码(两头会经验 生成 SCXML生成 xstate 数据结构 保留 xstate 变量、应用变量生成组件代码)。

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

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

正文完
 0