深度解读字节跳动 Web Infra 发起的 Modern.js 开源项⽬

7⽉,字节跳动 Web Infra 做过⼀次主题为 《迈⼊现代 Web 开发(字节跳动的现代 Web 开发实践)》 的分享,在分享中他们梳理了「传统前端技术栈」的典型组成部分,展示了其中每个部分都存在的瓶颈问题。也介绍了在这些问题的驱动下,业界正在发⽣从「传统 Web 开发范式」到「现代 Web 开发范式」的「范式转移」。在这个分享的最后预告了 Modern.js 开源项⽬。

10 ⽉ 27-28 ⽇举办的 稀⼟开发者⼤会 上,字节跳动 Web Infra 正式发起 Modern.js 开源项⽬。在专场分享《介绍 Modern.js——现代 Web ⼯程体系》中,⾸先介绍了业界和字节内部的前端开发、 Web 开发在发⽣哪些影响深远的变⾰,从这些变⾰的⻆度,展示了基于 Modern.js 的现代 Web 开发。

这些变⾰包括:

  • 更多「前端开发者」成为「应⽤开发者 / 产品开发者」

先讨论了什么根本因素在驱动这种转变,”Frontend Focused” 的意义,指出服务器端开发⻔槛不断降低的⻓期趋势、原有基建的缺陷,⽤ Modern.js 演示了「⼀体化、⽆服务器化的全栈开发」、「以客⼾端为中⼼的 Web 开发」

  • 从「前后端分离」到「前后端⼀体化」

分析了「前后端分离」产⽣的两种前端项⽬,为什么其中⼀种是「假分离」,另⼀种「不完整」,⽤ Modern.js 演示了「前后端⼀体化」在哪些地⽅带来改变

  • Meta Framework 取代传统「前端三剑客」

分析了四代「前端三剑客」,以及每⼀代都被下⼀代的成员「吞并」的规律,结合字节内部的 真实案例,讲解了 Meta Framework 的⻆⾊

  • 形成基于「前端技术」的成熟 GUI 软件研发体系

先明确了「前端技术」的定义, 结合 Modern.js 的功能和设计,讨论了如何实现「充分抽象」,才能 解决 DX 和 UX 的⽭盾

  • 智能化、平台化、低码化

接下来系统介绍了 Modern.js 的六⼤要素,包括:

  • 普及:现代 Web 开发范式

回顾了这种范式的 9 ⼤主要特征

  • 核⼼:现代 Web 应⽤(MWA)

从 Universal App、⼀体化、应⽤架构、Runtime API 这四个⻆度来了解 MWA。

在应⽤架构部分介绍了 Modern.js 中 Model 的设计和背景

  • 内置:前端⼯程最佳实践◦ 列举了⼏个典型的最佳实践,包括 Post-Webpack Era 的新⼯具趋势、Modern.js 的 Unbundled 开发,Modern.js 推荐的「CSS 三剑客」,Modern.js 微前端项⽬跟直接使⽤ Garfish 的微前端项⽬对⽐、模块⼯程⽅案和 Monorepo ⼯程⽅案中的最佳实践。

  • 包含:Web 开发全流程

演示了 Modern.js 在「编码」环节的微⽣成器功能、在「调试环节」的微前端调试

  • 提供:⼯程标准体系

  • ⿎励:定制⼯程⽅案

末尾介绍了除已经发布的开源项⽬,还有哪些对现代 Web 开发者有帮助的事情在发起和推进中。介绍了 Modern.js 当前⾼优的社区计划。

分享实录

⼤家好,我是来⾃字节跳动 Web Infra 的宋振伟,在字节跳动,我们部⻔负责打造和发展「Web 技术中台」和「前端研发体系」。


今年 7⽉,我们做过⼀次主题是 《字节跳动的现代 Web 开发实践》 的分享,在分享中我们梳理了 「传统前端技术栈」的典型组成部分,展示了其中每个部分都存在的瓶颈问题。


也介绍了在这些问题的驱动下,业界正在发⽣从「传统 Web 开发范式」到「现代 Web 开发范式」的 「范式转移」。

在这个分享的最后也预告了 Modern.js 开源项⽬。


昨天上午的主题演讲中,字节跳动正式发布了 Modern.js。今天的专场分享,我想结合字节内部的变⾰和实践,介绍基于 Modern.js 的现代 Web 开发,和所带来的实际效果。

议程

今天的分享可以分成三个部分。

昨天的主题演讲有说到,整个业界和字节内部的前端开发、Web 开发,都在发⽣着影响深远的变⾰,我们⾸先从这些变⾰的⻆度,看下基于 Modern.js 的现代 Web 开发是什么样⼦,有什么区别。

然后,我们整体看下 Modern.js 有哪些要素和收益。

最后再看下除了已经发布的开源项⽬,还有哪些对现代 Web 开发者有帮助的事情在发起和推进中。

我们先来看第⼀部分「现代 Web 开发」

⼀、基于 Modern.js 的现代 Web 开发

1. 更多「前端开发者」成为「应⽤开发者 / 产品开发者」


可以从这五个⽅⾯的变⾰,来展示「现代 Web 开发」是什么样⼦。

这五个变⾰之间是承前启后的关系。⾸先最根本的推动⼒,不是来⾃技术侧,不是前端开发者⼀厢情愿的发展⾃⼰主观的技术偏好,⽽是在互联⽹和 IT ⾏业、市场需求、⽤⼾产品这⼀侧的⼤趋势,需要更多「前端开发者」成为「应⽤开发者」或「产品开发者」,⿎励和倒逼着技术领域,不断产⽣更有利于这种需求的技术形态和基础设施。

当传统技术范式遇到瓶颈,不再能进⼀步适应需求,就会发⽣「范式转移」,出现从⼀开始就针对这种需求重新设计的、新⼀代的技术范式。

这种转变推动前端技术领域出现了「从分离到⼀体化」、新⼀代「前端三剑客」的变⾰。

这种变⾰带来的新⼀代技术标准和基础设施,开始形成完全基于「前端技术」的成熟 GUI 软件研发体系,并且进⼀步朝着平台化、低码化的⽅向发展。

“Frontend Focused” 的意义


我们刚才⼀直在说「前端」,「前端」这个概念似乎⼀直是只有开发者才关⼼的技术细节,但最近⼏年,却变成了商业领域、投资机构也都很关⼼的事情,全球市场上涌现出越来越多的新⼀代云平台和研发⼯具产品,多数都涉及前端研发特有的需求和模式,其中还有很多像 Vercel 这样明确「专注于前端」的产品。

就像幻灯⽚上这张图,云计算和研发产品最初是从最接近机器的底层开始发展,从虚拟化,到容器编排,到基于容器技术的各种平台化、服务化的研发⼯具形态,这个阶段是后端技术主导的,整个趋势 是越来越向上层发展,越来越接近市场和商业价值最终所在的地⽅——也就是⾯向⽤⼾的产品,因此 必然会发展到前端技术主导的抽象层,让应⽤开发和产品开发能更专注于⽤⼾需求,⽽越来越不需要关⼼服务器端的复杂性和专业技术细节。


所以市场需求会趋向于推动应⽤开发⽅式往「专注于前端」的⽅向的发展,专注于前端就是专注于用户,⽽专注于⽤⼾是多数企业、产品的根本利益所在。

最⼤的开发者群体


另⼀⽅⾯,从进⼊移动互联⽹时代开始就不断⼤幅增加的应⽤开发需求,现在不但没减弱,反⽽还在加强,⽐如幻灯⽚上 IDC 的预测,要满⾜这么庞⼤的应⽤开发需求,传统开发⽅式和⼈才储备是很不够的,需要让尽可能多的开发者能独⽴、完整的开发这些应⽤,⽽前端技术栈的开发者,正是最⼤的开发者群体和技术社区。

所以在用户、产品、市场这⼀侧,⼀直有趋势和压⼒,需要更多「前端开发者」成为「应⽤开发者」 或「产品开发者」,⿎励和倒逼着技术领域,不断产⽣更有利于这种需求的技术形态和基础设施。

服务器端开发⻔槛不断降低


在这种客观趋势的推动下,基于 Web 技术的应⽤开发中,服务器端的占⽐和⻔槛⼀直在不断下降, 国内⼤⼚的中台建设,提供了⼤量不跟特定客⼾端捆绑、专注于数据需求和底层业务逻辑的 API,让 产品开发更聚焦在上层的客⼾端业务逻辑。还有 BaaS 和基于云函数的后端云 Serverless,也进⼀步降低服务器端的⻔槛,让前端开发者能更独⽴的、端到端的完成产品开发。

但要进⼀步降低⻔槛,提⾼效率,这些基础设施的⼀个缺陷就暴露出来,就是他们都把应⽤依赖的 API,放在应⽤项⽬之外维护,跟前端研发是割裂的。

还有⼀个缺陷是它们不解决 API 之外的服务器端需求,⽐如路由、SSR 等。

有⼀个字节内部的典型例⼦,前端开发者在⾃⼰实现的 SSR 项⽬中,始终⽤ HTTP ⽅式请求外⽹域 名的 API 来获取数据,导致 SSR 频繁超时,HTML 响应慢严重影响⽤⼾体验。可以给前端开发者做培训,让他们具备⾜够的服务器端开发思维和知识,知道要在 SSR 环节切换成内⽹的请求⽅式,还要考虑缓存机制等,但更根本的解决⽅法是屏蔽这种服务器端问题和实现细节,⾃动处理这些问题。

⼀体化、⽆服务器化的全栈开发


因此服务器端⻔槛不断降低的趋势,⾃然会发展到「⼀体化、⽆服务器化的做全栈开发」的阶段,让前端开发者直接开发 「接近纯前端的项⽬」⽽不是 「Node.js 框架项⽬」,感觉就像没有服务器⼀样。

幻灯⽚上是这次分享中第⼀个 Modern.js 的 demo, 左侧是⼀个 Modern.js 应⽤项⽬的完整⽬录结构,src/ ⽬录下的应⽤主体,可以像调⽤普通函数⽂件⼀样访问 api/ ⽬录下的 BFF 函数,不需要了解网络细节,Modern.js 会⾃动基于 BFF 函数的路径、参数等⾃动⽣成 REST API,在 CSR 过程中⾃动请求。


接下来我们在 package.json 的 modernConfig 配置⾥启⽤ SSR、「差异分发」和「⾃动 Polyfill」,可以看到这些功能不要增加代码逻辑,只需要静态开关配置。


构建后,幻灯⽚左侧可以看到产物⾥的 HTML 和 JS 都有 es6 和 es5 两个版本,⽤⼾访问时应⽤的时候,Modern.js 的 Web Server 会根据浏览器 UA 选择分发 es6 版本还是 es5 版本,也就是「差异化分发」。

右边上⾯的图是现代浏览器的访问结果,不会返回任何 polyfill 代码, 下⾯的图是低版本浏览器的访问结果, Web Server 会⾃动提供这个 UA 需要的 polyfill 代码。

可以看到 Modern.js 不但⽀持⼀体化的开发 BFF,也满⾜ BFF 之外的服务器端需求,尽可能⾃动利⽤⾃带的 web server 去做性能优化和提供产品级的兼容性,同时开发体验仍然是⽆服务器化的。


之前我们启⽤了 SSR, 左侧图上⾼亮的 HTML ⽚段,已经包含了通过 BFF 请求到的数据,会根据应⽤运⾏的⽅式⾃动选择最⾼效的请求⽅式。

这种⾃动优化不会阻碍开发者对技术细节的掌控,右边这张图展示了 BFF 函数也会⽣成标准的 REST API,可以⼿动调⽤。

以客⼾端为中⼼的 Web 开发


这种⼀体化、⽆服务器化的全栈开发进⼀步发展,⾃然会得到⼀种客⼾端为中⼼的 Web 开发⽅式。

⽐如在传统 Web 开发中,要实现常⻅的权限识别和重定向,除了前端⻚⾯的逻辑,还需要在服务器端的路由中,添加实现跳转的业务逻辑。⽐如图上,在访问 home ⻚⾯的时候根据 cookie 的值决定 要不要重定向到登陆⻚⾯。


同样的需求,在以客⼾端为中⼼的 Web 开发⽅式中,可以⼀体化的在客⼾端代码⾥实现,⽐如前⾯已经启⽤ SSR 的 Modern.js 项⽬,只需要添加 Redirect 组件,就可以实现和刚才完全相同的权限识别和重定向效果,访问⻚⾯时会根据 cookie 决定要不要返回 302 状态码。整个实现过程是客⼾端思维的。


以客⼾端为中⼼,不代表不能掌控服务器端,不能直接写服务器端业务逻辑。

如果已经习惯 Node.js 框架的开发⽅式,可以 server ⽬录的钩⼦⽂件⾥,对框架⾃带的 Web Server 添加⾃定义逻辑,⽐如⾃由添加中间件,可以在这个局部,⽤⾃⼰熟悉的传统 Web 开发⽅式实现权限识别和重定向。

2. 从「前后端分离」到「前后端⼀体化」

Web 项⽬的技术栈也在转变,相当于是先发展出「前后端分离」,然后⼜⽤新⽅式回归了 「前后端⼀体化」。

「前后端分离」


以前的 Web 开发就像图上这个 Ruby on Rails 项⽬,粉⾊的前端代码,「寄居」在 绿⾊的 后端 Web 框架项⽬中的,前端和后端会互相⼲扰互相拖累,做⼯程建设也⽐较⿇烦。


之后 Web 开发普遍转变到「前后端分离」的模式,分离后的前端项⽬和后端项⽬,都倾向特定的类型。

后端项⽬不倾向包含 Web 的功能,⽽这时的前端项⽬可以归纳为两种类型。


MERN 这种项⽬类型相当于⼜回到了分离前的状态,整个项⽬是基于 Node.js 框架的,前端被嵌在⾥ ⾯。这种结果其实反映出「前后端分离」实现的更多是分⼯上的分离,⽽不是技术架构上的分离,在技术架构上仍然没有摆脱以服务器端框架为中⼼的 Web 开发。


从 MERN 项⽬的结构可以看到,它不但是假分离,⽽且也不算⼀体化,React 代表的前端部分和 Node.js 框架代表的后端部分,在项⽬⾥是泾渭分明的,没有真正融合到⼀起去。

使⽤ Node.js 框架的项⽬,多数属于这个类型。


「前后端分离」模式中另⼀种前端项⽬类型,我们称作「⽼⼀代 JAMstack」,这种类型没有假分离问题,就是纯粹的前端项⽬。可以实现 SPA 和 MPA,也能基于编译⼯具实现 SSG(静态⽹站⽣成)。靠静态托管来运⾏,⿎励在 CSR 中调⽤ API 满⾜动态的应⽤需求。


「⽼⼀代 JAMstack」最⼤的问题是,虽然分离成了独⽴的项⽬,却不⾜以承担完整的应⽤开发,只能产出静态⽂件,依靠外部的 Web 服务器去运⾏,⽆法实现 SSR,三⼤组成部分⾥的 API,也需要 在项⽬之外,⽤云函数、独⽴后端项⽬等⽅式来实现,不能跟着项⽬⼀起迭代。

⽤ CRA 或直接⽤ webpack 搭建的项⽬,多数属于这个类型。

「前后端⼀体化」


在前⾯说的需要更多「前端开发者」成为「应⽤开发者」的背景下,新⼀代的 JAMstack 项⽬⽤「客⼾端为中⼼」的「前后端⼀体化」⽅式,解决了上⾯说的问题。

新⼀代 JAMstack 的三⼤组成部分虽然没变,对应的内容却有很⼤变化,JS 部分更加函数化和组件化、以 JS 为中⼼,HTML 可以完全不在项⽬中出现,⾃动⽣成。BFF API 变成项⽬⾃包含。和之前简单的静态托管相⽐,基于前端 Serverless 平台可以实现 SSR、SPR 等动态能⼒,即使是静态⻚⾯,也可以获得很多好处,⽐如前⾯展示的 「差异化分发」。


图上是⽤ Modern.js 的 demo 来展示新⼀代 JAMstack 项⽬。在开发中只需要聚焦在 JS 代码上,不 论是 SPA 还是 MPA,HTML 都是⾃动⽣成的。不论是 SSR 渲染的代码,还是 API 逻辑,构建之后按照规范输出到 dist 下的不同⽬录,构建产物规范是 Serverless 友好的,⽀持把 Web、SSR、BFF 等 拆分成不同服务器。


前⾯提到 Modern.js 倾向于 JS 为中⼼、⾃动⽣成 HTML。但不阻碍开发者⾃⼰掌控 HTML。图上是 Modern.js 渲染 HTML 的默认模板。


⼀体化 BFF 的调⽤,在前⾯的例⼦演示过,这⾥可以看到 BFF 函数的⽂件路径是有约定的,可以实 现任意设计的 REST API。


构建产物会针对 BFF、Web、SSR 分别⽣成独⽴可运⾏的 Server,这是对前端 Serverless 平台更友好的,Serverless 平台可以⾃主选择让 BFF、Web、SSR ⽤不同⽅式在独⽴进程中运⾏,不会互相⼲扰。⽐如在 SSR 环节遇到 app 代码的的内存泄露导致 SSR 超时,Web Server 不受影响,可以⾃动降级到 CSR 模式,返回静态的 HTML 给⽤⼾作为兜底,⽤⼾的 HTML 请求始终不会超时或挂掉。


对于 SSR,同样可以前后端⼀体化的开发,图⾥⾼亮的 useLoader 函数中的代码,同时适⽤于 SSR 和 CSR,如果这个 Loader 在 SSR 中已经预加载,CSR 就会⾃动跳过,否则会执⾏。


SPR 相当于有缓存机制的 SSR,在 Modern.js ⾥也可以⼀体化的开发,只要使⽤这个预渲染组件。


SSG 实际上就是在编译时运⾏的 SSR,在 Modern.js ⾥只要配置 SSG 路由,就会⾃动启⽤这种编译逻辑,给路由⽣成静态 HTML。CSR、SSR、SSG 都是⽤同⼀份代码。

3. 新⼀代「前端三剑客」和 Meta Framework

除了在技术栈层⾯向「前后端⼀体化」转变,在⼯程层⾯,传统的「前端三剑客」也在转变成元框架这种新的⼯程基建。

传统「前端三剑客」


先来回顾下传统的「前端三剑客」,第 1 代和第 2 代如图上所示,也被⼤家熟知。⽽第 3 代「前端 三剑客」由视图框架、Node.js 命令⾏、Node.js 框架三个⽅向组成。


其中 Node.js 命令⾏代表了⼯程化,其中最典型的是像 Webpack 这样的打包⼯具,以及 Babel、 PostCSS 这样的编译⼯具。

视图框架和 Node.js 框架很好理解,就是之前讨论的 MERN 项⽬中的前端和后端部分。

第 4 代「前端三剑客」


随着现代 Web 开发范式的发展,第 4 代「前端三剑客」的轮廓已经越来越明显,由元框架、前端 PaaS、低代码三个⽅向组成。其中低代码⽅向在昨天晚上稀⼟⼤会的低码专场已经介绍过,⽽ Modern.js 就属于元框架这个⽅向。


从这张图可以清楚的看到,每⼀代前端三剑客中,都有⼀个⽅向,把上⼀代前端三剑客完整包含在⾃⼰⾥⾯,变成不需要太关⼼的底层,让⾃⼰取代他们成为前端开发的新地基。

第 3 代中的视图框架,就扮演这样的⻆⾊,把第⼆代的 HTML、CSS、JS 封装在⾃⼰⾥⾯,⽽第 4 代中的元框架,⼜对视图框架、Node.js 框架、Node.js 命令⾏做了整合和抽象,成为前端开发和⼯程建设性起点,元框架扮演了过去 Webpack、React 扮演的⻆⾊。


这张 JS 框架的 S 曲线图,也能体现这种转变。在左边这个时期,发展的前沿、开发的起点,都是 React、Vue、Svelte 这样的视图框架,新的视图框架项⽬也层出不穷。现在已进⼊右边这个时期, 前沿收敛到基于 React 发展更上层的元框架。


Modern.js 作为现代 Web ⼯程体系,是由元框架组成的,提供三⼤⼯程类型,⿎励开发者基于⼯程类型建设⾃⼰的业务⼯程⽅案。

以字节内部的「⽕⼭引擎⼦应⽤⼯程⽅案」为例,初始化的⽬录结构没有什么变化,只在配置中默认加载了⾃⼰的框架插件,插件中通过 server 提供的 hook 修改渲染后的 HTML ,在原来的 HTML 上套了层壳,也就是右下⻆截图中⽕⼭引擎统⼀的顶栏和左侧导航栏。

这样建设出来的⼯程⽅案,既能满⾜垂直场景的需求或⾃⼰的偏好,⼜能保持跟三⼤⼯程类型的兼容,⾃动获得 Modern.js 的能⼒和收益

4. 基于「前端技术」的成熟 GUI 软件研发体系

在「前端开发者」成为「应⽤开发者」的⼤背景下,技术栈、⼯程基建的发展,开始形成基于「前端技术」的成熟 GUI 软件研发体系。

什么是「前端技术」


先明确⼀下我们⼀直说的前端技术,不是指做 UI 的技术,⽽是由 Web 原⽣语⾔、Web Runtime、 Web 技术⽣态组成的技术栈,不是只在浏览器⾥才有前端技术,⽽是有 Web Runtime、有 Web 语⾔的地⽅,就有前端技术。

DX 和 UX 同样重要


传统前端开发不是成熟的软件研发体系,缺乏⾜够的抽象和基建,导致 DX 和 UX 始终存在⽭盾,此消彼涨。以往的产品开发中,习惯更重视 UX,这有两⽅⾯的原因,⼀来是因为产品是由产品主导,因此更重视 UX,不会过多关注开发者体验,再就是缺乏⾜够的抽象和基建,导致 DX 和 UX 之间必须牺牲⼀个。

在新⼀代更成熟的研发体系的⽀持下,已经可以实现 DX 和 UX 的同时最⼤化了,也从「更重视 UX」 转变为「DX 优先」的⽅式。


要实现 DX 和 UX 的同时最⼤化,需要充分的抽象。⽐如前⾯提到的 Modern.js 的这个例⼦,项⽬⾥只有三个⽂件,就具备全⾯的能⼒,包括⾃动 Polyfill、差异化分发、SSR 等,既具备产品级的 UX,有保持了 DX 的简单、开箱即⽤等。

充分抽象


要实现充分抽象,需要让项⽬从基于「库、⼯具」发展成基于「框架」,这两者的区别在图上表现的 ⽐较好。蓝底⽩边部分是项⽬开发者⾃⼰写的代码。左边是传统前端项⽬,由开发者⼿写整个应⽤, 把库和⼯具当做积⽊来组装,填补项⽬⾥的空⽩。⽽右边是 Modern.js 项⽬,整个应⽤是框架本⾝,开发者⼿写的代码,是按照框架的要求填充到框架预留的位置上。


要实现充分抽象,也需要在尽可能多的环节实现最⼤化的抽象,图上体现了 Modern.js 除了像常规 的框架⼀样,在运⾏时和编译时做抽象,也会在 IDE 编写代码的环节,和部署产物的环节,引⼊最⼤化的抽象。


要实现充分抽象,也需要解决前端模板的问题,Modern.js 把各种研发场景、项⽬类型,收敛和标准 化成了始终固定的三个⼯程类型,其中「应⽤」⼯程⽅案,也就是 MWA,⽀持所有需要部署和运⾏的项⽬,「模块」⼯程⽅案⽀持所有需要实现代码复⽤的项⽬。

5. 智能化、平台化、低码化

Modern.js 代表的现代 Web 开发,也在继续朝着智能化、平台化、低码化的⽅向发展。


智能化⽅⾯,当前可⽤的功能,是⽤ Modern.js 的初始化⼯具创建的项⽬,会开箱即⽤的在 VSCode ⾥做好配置,启⽤⼏千条规则组成的 ESLint 全量规则集,加上按最佳实践内置在 ESLint ⾥的 Prettier,期望尽可能多⾃动修正问题,⽽不是仅仅提示问题。也追求尽可能多的让 IDE 负责⽣成真正的源码,让开发者⼿写的代码变成跟 IDE 沟通的语⾔。


在平台化⽅⾯,Modern.js 的⽬标之⼀就是形成「⼯程标准」,让各种前端 PaaS 平台可以围绕标准实现⾼级能⼒,⽐如图上粉⾊部分列出的产品级 Web Server、差异化分发、SSR 兜底、ESR、微前 端等,都需要结合代码层⾯的⼯程标准。


除了部署运⾏环节⽅⾯的平台,有了⼯程标准之后,研发环节也可以引⼊更多低码提效。

⽬前我们内部使⽤的研发平台,可以直接在图形界⾯上简单操作完成项⽬的创建、开发、部署。图上右侧可以看到图形界⾯上展示了当前项⽬的的状态: ⼊⼝数量、项⽬配置等,蓝⾊框内添加应⽤的⼊⼝,⼀键从 「单⼊⼝」 转变为 「多⼊⼝」 。右侧在 Web IDE 中也能看到 src ⽬录结构下的变化。


低码化有两个⽅向,⼀个是刚才说的跟研发⼯具结合,另⼀个就是研发从某些⼯作中解脱出来的低码搭建,昨天的低代码专题中有介绍。

总结


到⽬前为⽌,我们从这些变⾰的⻆度,展⽰了很多 Modern.js 的 demo 和效果。

⼆、Modern.js 的六⼤要素

接下来我们系统的看⼀下 Modern.js 是什么,Modern.js 提供了什么。

1. 普及:现代 Web 开发范式


可以⽤这六⼤要素来说明 Modern.js 。

⾸先这个项⽬是希望能推动现代 Web 开发范式的普及,发展完整的现代 Web ⼯程体系,突破应⽤开发效率的瓶颈。


前⾯讨论「现代 Web 开发」的时候,已经展示过这种范式的 9 ⼤主要特征。

其中 Serverless 范式、平台化、低码化这三个特征,在当前版本的 Modern.js ⾥还没什么体现,需要后续会跟⼀些平台配合提供。

2. 核⼼:现代 Web 应⽤(MWA)

继续看第⼆个要素。Modern.js 三⼤⼯程类型中最核⼼的就是 「现代 Web 应⽤」,简称 MWA,或 直接叫「应⽤」。

从 Universal JS 到 Universal App

前⾯提到过,「应⽤」⼯程⽅案⽀持所有需要部署和运⾏的项⽬,把这些项⽬收敛成⽤同⼀套框架、 同⼀套约定、同⼀套模板、同⼀套架构、⼀套 API 来开发。

反过来,我们也可以从 Universal App、⼀体化、应⽤架构、Runtime API 这四个⻆度来了解 MWA。


Universal JS 指同⼀份 JS 代码,既能在浏览器端运⾏,也能在服务器端运⾏,Universal App 是它 的进⼀步发展,同⼀份 App 代码可以在不同环节运⾏,也可以⽤不同的模式来运⾏。


⾸先是常⻅的 MPA 和 SPA 的需求,本质上是「服务器端路由」和「客⼾端路由」的需求。

在 Modern.js ⾥它们可以随意组合。

我们之前的例⼦都是单⼊⼝应⽤,只需要把 App.tsx 组件、pages ⽬录这样的⼊⼝标识放到 src 的⼦ ⽬录⾥,就能变成多⼊⼝的 MPA,会⽤⼊⼝名⾃动⽣成服务器端路由,⽐如图上的 admin-app 和 landing-page 两个⼊⼝。

这两个⼊⼝也分别都是 SPA,根据⼊⼝标识不同,⼀个使⽤基于组件的客⼾端路由,⼀个使⽤基于⽂件系统的客⼾端路由。


然后是⽀持「动静⼀体」。

前⾯展示过⼀个静态的、CSR 的项⽬如何直接开启 SSR、SPR、SSG 功能。

Modern.js 也⽀持 CSR 和 SSR/SSG 混⽤,⽐如图上右侧红⾊⾼亮部分会在服务端渲染到 html 中, 蓝⾊区域的⽇期时间,在 CSR 阶段动态展示在⻚⾯上。也就是整体 SSR + 局部 CSR。

整体 CSR + 局部 SSR 的能⼒后续会加⼊。


在 BFF ⽀持⽅⾯,Modern.js 中还提供了类型友好的⽅式,可以通过 Type Schema 实现运⾏时⾃动 校验接⼝的参数和返回值。⽐如右下⻆请求时,参数 text 类型为 number 时,response 中会⾃动提示相应的错误。


最后 Modern.js 还⽀持不同类型的应⽤开发,上图就是之前例⼦中介绍 Web 开发模式。


最后是不同的运⾏模式。

微前端在 Modern.js 中是原⽣⽀持的,底层解决⽅案是 Web Infra 之前开源的 Garfish 微前端解决⽅案。⼀个 MWA 可以随时变成微前端主应⽤,在配置中指定⼦应⽤列表的加载地址,Modern.js 就会 ⾃动在 Web Server 中预加载⼦应⽤数据,注⼊到运⾏时,在 Runtime API 的帮助下,可以像普通 React 组件⼀样使⽤⼦应⽤。

MWA 也可以分别作为独⽴的 Web 和微前端⼦应⽤的运⾏和部署。


MWA 在启用 Electron 支持之后,能作为桌⾯应⽤来运⾏,项目里会新增 electron ⽬录用于写主进程相关代码。除了开箱即⽤的 Electron 构建等能力,也提供运⾏时 API ⽀持 Electron 的常⻅需求和最佳实践,进⼀步提升开发效率。

前后端⼀体化

第⼆个看待 MWA 的⻆度是 「前后端⼀体化」。


之前的例⼦只展示了⽤ BFF 函数,api/ ⽬录下每个⽂件就是 BFF 路由,当服务器端逻辑更重的时候,可以增加 Node.js 框架元素,⽬前⽀持了 4 种不同的框架,还可以⾃⼰开发 Modern.js 插件⽀持更多框架。


前⾯提到过,以客⼾端为中⼼,不代表不能掌控服务器端,不能直接写服务器端业务逻辑。

⽐如前⾯提到的⽕⼭引擎⼯程⽅案的例⼦,除了⽤框架插件,也可以在项⽬⾥创建 server ⽬录和钩 ⼦⽂件,添加修改 HTML 渲染结果的逻辑。在框架插件⾥和直接在项⽬下定制 Server,能⼒是对等的。


在只有 src ⽬录,或有 api ⽬录的情况下,MWA 类似 JAMstack 项⽬。

如果增加了 server 等钩⼦⽂件的情况下,MWA 像传统 Node.js App ⼀样能直接写服务器端业务逻 辑,使⽤ Node.js 框架插件、中间件等。

如果删掉 src ⽬录,MWA 就是⼀个纯 REST API 的项⽬。

我们把这三种模式之间的随意迁移,称作「三位⼀体」。

应⽤架构

接下来看下应⽤架构⻆度。


传统 Web 开发中的应⽤架构,等同于服务器端应⽤架构,前端部分的架构要么缺失,要么需要项开发者⾃⼰摸索、搭建,缺乏 API ⽀持和⼀致的抽象,难以跨项⽬复⽤业务逻辑。


MWA 提供的开箱即⽤的、客⼾端为中⼼的应⽤架构。类似图上那样,可以⽤标准化的 Runtime API,轻易实现 React 开发中⽋缺的 Model 层和 Controller 层。Model 作为封装 UI ⽆关业务逻辑的积⽊,跟 UI 组件⼀样可以复⽤和组装。


之前 Web Infra 举办的 React 核⼼开发者在线访谈⾥,Redux 作者 Dan 提到,状态管理最重要的是理解状态的类型,根据需要处理的状态是什么种类,来选择对应的⽅案。


常⻅的状态管理⽅案,都有适合的状态类型和场景,很多时候需要混合使⽤,⽽不是⼀把锤⼦锤所有钉⼦,要么所有状态都放到全局应⽤状态⾥,要么所有状态都在局部状态⾥。


很多开发者不⽤ Redux,是因为 Redux 本⾝只能算底层 API,需要手动创建和维护 store,业务逻辑被 reducer,action 等分散在不同的地方,提高了维护成本。其实 Redux 社区⼀直有解决⽅案,比如 Ducks Modular 设计模式会把业务逻辑聚集在⼀起,Redux 官方支持的主流库 RTK 也为解决这样的问题而生。

Modern.js 的 Model 基于 Redux 进⼀步提⾼抽象程度,保留了 Redux 在不可变数据、数据流等⽅⾯ 的收益,对整个 Redux ⽣态兼容,让使⽤和不使⽤ Redux 的开发者都能受益。⽀持多种状态类型,也⽀持不同的 Model 写法。

Runtime API 标准库

最后看下 MWA 的 Runtime API 标准库。


这套 API 相当于对「应⽤」层级的基础 API,不⽌能在 MWA ⾥使⽤,在 Modern.js 的模块⼯程⽅案⾥,同样可以使⽤这些 API,开发可复⽤的业务组件,⽀持独⽴调试和测试。

图上最上⾯蓝⾊⽅块是业务开发中常⽤的 api,⽐如 useLoader,useModel 等 API。中间橙⾊部分 就是前⾯提到定制 server 需要⽤到的 API, 最底下就是我们的插件 API,可以扩展框架、定制⼯程⽅案。右侧也提供了⼀些⼯具函数 API, ⽐如⽤于代码分割的 loadable API。最右边 测试相关的⼯具 API 保证了 runtime 不论在应⽤、模块还是 API 服务中 都可以进⾏单元测试和集成测试。


当应⽤中的组件需要拆分成独⽴的模块复⽤时,使⽤的 Runtime API 怎么办,还能继续调试、测试嘛,答案是肯定的,这套 API 相当于「应⽤」领域的 API 标准库,不⽌能在 MWA ⾥使⽤,在 Modern.js 的模块⼯程⽅案⾥,同样可以使⽤这些 API,开发可复⽤的业务组件,⽀持独⽴调试和测试。上图右侧是模块⼯程的⽬录结构。左侧 TableList 组件中使⽤了 useLoader API,调试时只需要 提供对应的 story ⽂件,模块⼯程⽅案⽀持我们在 storybook 调试⽤到了 Runtime API 的组件。测试⽤例也是⼀样⽀持。

3. 内置:前端⼯程最佳实践

对于第三个要素,简单列举⼏个 Modernjs 内置的前端⼯程最佳实践。

Post-Webpack Era


传统前端⼯程建设都是基于 Webpack 的配置封装, Webpack 不论是配置还是速度的问题,⼤家应该都有⽐较深的感受, 但是从去年开始业界涌现很多新的⼯具,完全不涉及 webpack,⽐如 snowpack、 vite、wmr、等等,有⼈把它称为 JS 第三纪元。


从第三纪元开始, esbuild 、swc 这种编译打包⼯具使⽤⾮ JS 的系统编程语⾔开发,显著提⾼构建速度。编译时间的缩减也意味着不打包,按需编译的 ESM 场景可以实现, Snowpack、Vite 这样的⼯具就是在 esbuild 的基础上实现的开发者体验优先的不打包开发调试模式。在 Modern.js 中不打包的模式⽬前已经被⽤于公共库的构建、业务项⽬的开发调试等真实场景。


Modern.js 中也内置类似 Snowpack、Vite 的不打包开发调试模式,左侧启⽤该功能之后,运⾏效果就像图上右侧那样,开发服务器在秒级启动。


为什么可以做到速度这么快?主要是因为业务代码只有在请求时使⽤ esbuild 按需编译,第三⽅依赖⾃动从 Goofy PDN 加载已经预编译好的产物。

CSS 最佳实践


在 CSS 开发⽅⾯,Modern.js 默认推荐图上「CSS 三剑客」搭配使⽤,有需要也可以开启 LESS/SASS 等预处理器和 CSS Modules ⽀持。

默认零配置、样板⽂件最⼩化


和以前把功能作为样板⽂件塞到项⽬⾥相⽐, 现代 Web 开发范式下的最佳实践要求默认零配置的,同时样板⽂件尽可能简洁最⼩化。之前我们也通过例⼦看到 Modern.js 项⽬⼿动创建⾮常简单,只需要应⽤根组件和 package.json。

跟直接使⽤ Garfish 开发微前端⼦应⽤的项⽬做对⽐,上图可以看到直接使⽤ garfish 的项⽬,需要⼿动运⾏ Garfish 框架、处理公⽤模块、路由等逻辑。除了运⾏时,还需要编译环节⾃定义⼀⼤堆配置。直接使⽤还是有⼀定的成本。


在 Modern.js 中使⽤微前端,之前的例⼦已经说过,只需要在 web 应⽤的基础上启⽤微前端功能, 提供⼦应⽤列表即可,每个⼦应⽤加载后就是组件、路由可以⾃⼰灵活组织。

构建产物规范


Modern.js 的模块⼯程⽅案,会并⾏编译出多种符合社区主流规范的构建产物。模块的编译也是不打包的,更容易引⼊速度更快的⼯具⽐如 esbuild、swc 等。

Monorepo ⼯程⽅案


应⽤和库如果分散在不同项⽬开发的话,通过 link 调试也⽐较⿇烦,业界的主流⽅案就是 monorepo, Modern.js 本⾝就是基于 pnpm monorepo 开发的,同时也将这部分最佳实践收敛到 monorepo ⼯程⽅案。初始化的 monorepo 默认使⽤pnpm,左侧是他的⽬录结构,apps 对应的是 MWA 应⽤ 、features/packages 对应是可复⽤的模块。

右侧内部模块指的是不会发布到 npm、仅在当前仓库下复⽤的库。它本⾝不需要构建,同仓库下的应⽤直接使⽤它的源码即可。monorepo 我们也提供了 new 命令,可以选择创建应⽤或者模块。

更多


除了上⾯提到的⼀些最佳实践,Modernjs 还提供了单元测试、集成测试、Visual Testing 等、ESlint 全量规则集等最佳实践。这⾥不⼀⼀展开介绍。可以在⽂档上了解和使⽤。

4. 包含:Web 开发全流程

Modernjs 不仅是上⾯介绍过的 运⾏、构建、调试等⽅⾯,本⾝包含了 Web 开发全流程。


在编码环节,也可以通过微⽣成器启⽤某个功能或者添加⼊⼝,从 SPA 到 MPA, 和前⾯提到的通过研发平台「低码提效」类似,还可以像图上那样在项⽬⽬录下执⾏ new 命令选择要启⽤的功能。这个命令会⾃动重构我们的代码。

通过微⽣成器按需⾃动启⽤的⽅式,可以放⼼的将⼀些功能作为插件提供,也可以控制 Modern.js 初始化项⽬的体积。


在微前端⼦应⽤开发时,通常情况下主应⽤已经部署上线了,这时候开发⼦应⽤就需要结合线上的主 应⽤⼀起调试,之前的⽅式通常是通过全局代理⼦应⽤ js 到本地,⽐较⿇烦。

Modern.js 中只需要主应⽤像右下⻆那样启⽤ DEBUG 模式,之后打开主应⽤线上链接,在 header 中设置需要开发的⼦应⽤信息,server 会⾃动替换注⼊到 html 中的⼦应⽤列表数据。这样也就可以让线上主应⽤加载本地⼦应⽤。


在运⾏环节,传统的 Web 开发模式,通常是没有提供⽣产环境运⾏项⽬的⽅式,MWA 项⽬本⾝⾃ 带产品级的 Server,⾃⼰就能产品级的运⾏⾃⼰,⽐如图上的⾃动 Polyfill 服务。之前也提过,结 合 serverless 平台,可以⾃动做⼀些优化。也可以在本地运⾏模拟⽣产环境的效果。

5. 提供:⼯程标准体系

Modern.js 不只是⼀个现代 Web 应⽤开发框架,⽽提供了整套现代 Web ⼯程体系。


前⾯已经介绍过,我们将前端开发中涉及的场景收敛到 3 种,就是图上应⽤、模块和 monorepo。

不仅解决了业务模板数量爆炸的问题。融合后的⼯程类型,⽐如 MWA 不是多个场景简单叠加,导致 ⼯程变的⼤⽽全,通过抽象可以做到很轻量,也能更容易交付⼀些之前不好实现的功能。

6. ⿎励:定制⼯程⽅案

Modern.js ⿎励业务结合⾃⾝场景定制垂直的⼯程⽅案。


就像前⾯提到的⽕⼭引擎例⼦⼀样,封装插件、微⽣成器、定制出⾃⼰的业务⼯程⽅案。


关于 Modern.js 六⼤要素的更多解释和例⼦,可以在官⽹上看到。

三、Modern.js 社区和现代 Web 研发体系

最后我们⼀起看下除了已经发布的开源项⽬,还有哪些对现代 Web 开发者有帮助的事情在发起和推进中。


Modern.js 开源项⽬现在是刚起步的状态,昨天上线的官⽹,以及最新发的 1.0 版,都是公测状态, 还需要更多意⻅、测试和实践,希望⼤家多参与社区建设。

Modern.js 的起点是字节内部的现代 Web ⼯程体系项⽬,现在⼤部分代码已经完全转到 Github 上开发,⼯作流还在建设中。

双⽉计划、每周计划、缺陷管理等,也都会全⾯转到 Github 上公开推进。

当前版本还没有包含 Roadmap 上⼀些重要功能,希望尽量以每周发版的节奏,把这些功能补上。


昨天的分享介绍了 「现代 Web 研发体系」中的其他部分,这些部分⼏乎也都算的上是 Modern.js 的 重要功能,后续会陆续对外开放,欢迎⼤家关注。


最后,屏幕上右下⻆⼆维码是 Modern.js 官⽹的地址,可以在官⽹上通过快速上⼿和实战教程了解 更多 Modernjs 的细节使⽤部分。

谢谢⼤家。

官⽹:https://modernjs.dev/

Github:https://github.com/modern-js-dev/modern.js

本文文字及图片出自 InfoQ

余下全文(1/3)
分享这篇文章:

请关注我们:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注