7月,字节跳动WebInfra做过一次主题为《迈入现代Web开发(字节跳动的现代Web开发实践)》[1]的分享,在分享中我们梳理了「传统前端技术栈」的典型组成部分,展示了其中每个部分都存在的瓶颈问题。并介绍了在这些问题的驱动下,业界正在发生从「传统Web开发范式」到「现代Web开发范式」的「范式转移」。在这个分享的最后预告了Modern.js的开源项目。
10月27-28日的稀土开发者大会[2]上,字节跳动WebInfra正式发起Modern.js[3]开源项目。在专场分享《介绍Modern.js——现代Web工程体系》中,第一部分先介绍了业界和字节内部的前端开发、Web开发在发生哪些影响深远的变革,从这些变革的角度,展示了基于Modern.js的现代Web开发。
这些变革包括:
「更多「前端开发者」成为「应用开发者/产品开发者」。」
先讨论了什么根本因素在驱动这种转变,"FrontendFocused"的意义,指出服务器端开发门槛不断降低的长期趋势、原有基建的缺陷,用Modern.js演示了「一体化、无服务器化的全栈开发」、「以客户端为中心的Web开发」。
「从「前后端分离」到「前后端一体化」。」
分析了「前后端分离」产生的两种前端项目,为什么其中一种是「假分离」,另一种「不完整」,用Modern.js演示了「前后端一体化」在哪些地方带来改变。
「Meta」「Framework取代传统「前端三剑客」。」
分析了四代「前端三剑客」,以及每一代都被下一代的成员「吞并」的规律,结合字节内部的真实案例,讲解了MetaFramework的角色。
「形成基于「前端技术」的成熟GUI软件研发体系。」
先明确了「前端技术」的定义,结合Modern.js的功能和设计,讨论了如何实现「充分抽象」,才能解决DX和UX的矛盾。
「智能化、平台化、低码化。」
接下来第二部分系统介绍Modern.js的六大要素,包括:
「普及:现代Web开发范式。」
回顾了这种范式的9大主要特征。
「核心:现代Web应用(「「MWA」」)。」
从UniversalApp、一体化、应用架构、RuntimeAPI这四个角度来了解MWA。
在应用架构部分介绍了Modern.js中Model的设计和背景。
「内置:前端工程最佳实践。」
列举了几个典型的最佳实践,包括Post-WebpackEra的新工具趋势、Modern.js的Unbundled开发,Modern.js推荐的「CSS三剑客」,Modern.js微前端项目跟直接使用Garfish的微前端项目对比、模块工程方案和Monorepo工程方案中的最佳实践。
「包含:Web开发全流程。」
演示了Modern.js在「编码」环节的微生成器功能、在「调试环节」的微前端调试。
「提供:工程标准体系。」
「鼓励:定制工程方案。」
末尾介绍了除已经发布的开源项目,还有哪些对现代Web开发者有帮助的事情在发起和推进中。也介绍了Modern.js当前高优的社区计划。
欢迎大家扫码入群,一起讨论交流
分享实录大家好,我是来自字节跳动WebInfra的宋振伟,在字节跳动,我们部门负责打造和发展「Web技术中台」和「前端研发体系」。
今年7月,我们做过一次主题是《字节跳动的现代Web开发实践》[1]的分享,在分享中我们梳理了「传统前端技术栈」的典型组成部分,展示了其中每个部分都存在的瓶颈问题。
也介绍了在这些问题的驱动下,业界正在发生从「传统Web开发范式」到「现代Web开发范式」的「范式转移」。
在这个分享的最后也预告了Modern.js开源项目。
昨天上午的主题演讲[6]中,字节跳动正式发布了Modern.js[7]。今天的专场分享,我想结合字节内部的变革和实践,介绍基于Modern.js的现代Web开发,和所带来的实际效果。
议程今天的分享可以分成三个部分。
昨天的主题演讲有说到,整个业界和字节内部的前端开发、Web开发,都在发生着影响深远的变革,我们首先从这些变革的角度,看下基于Modern.js的现代Web开发是什么样子,有什么区别。
然后,我们整体看下Modern.js有哪些要素和收益。
最后再看下除了已经发布的开源项目,还有哪些对现代Web开发者有帮助的事情在发起和推进中。
我们先来看第一部分「现代Web开发」
一、基于Modern.js的现代Web开发1.1更多「前端开发者」成为「应用开发者/产品开发者」可以从这五个方面的变革,来展示「现代Web开发」是什么样子。
这五个变革之间是承前启后的关系。首先最根本的推动力,不是来自技术侧,不是前端开发者一厢情愿的发展自己主观的技术偏好,而是在互联网和IT行业、市场需求、用户产品这一侧的大趋势,需要更多「前端开发者」成为「应用开发者」或「产品开发者」,鼓励和倒逼着技术领域,不断产生更有利于这种需求的技术形态和基础设施。
当传统技术范式遇到瓶颈,不再能进一步适应需求,就会发生「范式转移」,出现从一开始就针对这种需求重新设计的、新一代的技术范式。
这种转变推动前端技术领域出现了「从分离到一体化」、新一代「前端三剑客」的变革。
这种变革带来的新一代技术标准和基础设施,开始形成完全基于「前端技术」的成熟GUI软件研发体系,并且进一步朝着平台化、低码化的方向发展。
1.1.1"FrontendFocused"的意义我们刚才一直在说「前端」,「前端」这个概念似乎一直是只有开发者才关心的技术细节,但最近几年,却变成了商业领域、投资机构也都很关心的事情,全球市场上涌现出越来越多的新一代云平台和研发工具产品,多数都涉及前端研发特有的需求和模式,其中还有很多像Vercel这样明确「专注于前端」的产品。
就像幻灯片上这张图,云计算和研发产品最初是从最接近机器的底层开始发展,从虚拟化,到容器编排,到基于容器技术的各种平台化、服务化的研发工具形态,这个阶段是后端技术主导的,整个趋势是越来越向上层发展,越来越接近市场和商业价值最终所在的地方——也就是面向用户的产品,因此必然会发展到前端技术主导的抽象层,让应用开发和产品开发能更专注于用户需求,而越来越不需要关心服务器端的复杂性和专业技术细节。
所以市场需求会趋向于推动应用开发方式往「专注于前端」的方向的发展,专注于前端就是专注于用户,而专注于用户是多数企业、产品的根本利益所在
1.1.2最大的开发者群体另一方面,从进入移动互联网时代开始就不断大幅增加的应用开发需求,现在不但没减弱,反而还在加强,比如幻灯片上IDC的预测,要满足这么庞大的应用开发需求,传统开发方式和人才储备是很不够的,需要让尽可能多的开发者能独立、完整的开发这些应用,而前端技术栈的开发者,正是最大的开发者群体和技术社区。
所以在用户、产品、市场这一侧,一直有趋势和压力,需要更多「前端开发者」成为「应用开发者」或「产品开发者」,鼓励和倒逼着技术领域,不断产生更有利于这种需求的技术形态和基础设施。
1.1.3服务器端开发门槛不断降低在这种客观趋势的推动下,基于Web技术的应用开发中,服务器端的占比和门槛一直在不断下降,国内大厂的中台建设,提供了大量不跟特定客户端捆绑、专注于数据需求和底层业务逻辑的API,让产品开发更聚焦在上层的客户端业务逻辑。还有BaaS和基于云函数的后端云Serverless,也进一步降低服务器端的门槛,让前端开发者能更独立的、端到端的完成产品开发。
但要进一步降低门槛,提高效率,这些基础设施的一个缺陷就暴露出来,就是他们都把应用依赖的API,放在应用项目之外维护,跟前端研发是割裂的。
还有一个缺陷是它们不解决API之外的服务器端需求,比如路由、SSR等。
有一个字节内部的典型例子,前端开发者在自己实现的SSR项目中,始终用HTTP方式请求外网域名的API来获取数据,导致SSR频繁超时,HTML响应慢严重影响用户体验。可以给前端开发者做培训,让他们具备足够的服务器端开发思维和知识,知道要在SSR环节切换成内网的请求方式,还要考虑缓存机制等,但更根本的解决方法是屏蔽这种服务器端问题和实现细节,自动处理这些问题。
1.1.4一体化、无服务器化的全栈开发因此服务器端门槛不断降低的趋势,自然会发展到「一体化、无服务器化的做全栈开发」的阶段,让前端开发者直接开发「接近纯前端的项目」而不是「Node.js框架项目」,感觉就像没有服务器一样。
幻灯片上是这次分享中第一个Modern.js的demo,左侧是一个Modern.js应用项目的完整目录结构,src/目录下的应用主体,可以像调用普通函数文件一样访问api/目录下的BFF函数,不需要了解网络细节,Modern.js会自动基于BFF函数的路径、参数等自动生成RESTAPI,在CSR过程中自动请求。
接下来我们在package.json的modernConfig配置里启用SSR、「差异化分发」和「自动Polyfill」,可以看到这些功能不用增加代码逻辑,只需要静态开关配置。
构建后,幻灯片左侧可以看到产物里的HTML和JS都有es6和es5两个版本,用户访问时应用的时候,Modern.js的WebServer会根据浏览器UA选择分发es6版本还是es5版本,也就是「差异化分发」。
右边上面的图是现代浏览器的访问结果,不会返回任何polyfill代码,下面的图是低版本浏览器的访问结果,WebServer会自动提供这个UA需要的polyfill代码。
可以看到Modern.js不但支持一体化的开发BFF,也满足BFF之外的服务器端需求,尽可能自动利用自带的webserver去做性能优化和提供产品级的兼容性,同时开发体验仍然是无服务器化的
之前我们启用了SSR,左侧图上高亮的HTML片段,已经包含了通过BFF请求到的数据,会根据应用运行的方式自动选择最高效的请求方式。
这种自动优化不会阻碍开发者对技术细节的掌控,右边这张图展示了BFF函数也会生成标准的RESTAPI,可以手动调用。
1.1.5以客户端为中心的Web开发这种一体化、无服务器化的全栈开发进一步发展,自然会得到一种客户端为中心的Web开发方式。
比如在传统Web开发中,要实现常见的权限识别和重定向,除了前端页面的逻辑,还需要在服务器端的路由中,添加实现跳转的业务逻辑。比如图上,在访问home页面的时候根据cookie的值决定要不要重定向到登陆页面。
同样的需求,在以客户端为中心的Web开发方式中,可以一体化的在客户端代码里实现,比如前面已经启用SSR的Modern.js项目,只需要添加Redirect组件,就可以实现和刚才完全相同的权限识别和重定向效果,访问页面时会根据cookie决定要不要返回状态码。整个实现过程是客户端思维的。
以客户端为中心,不代表不能掌控服务器端,不能直接写服务器端业务逻辑。
如果已经习惯Node.js框架的开发方式,可以server目录的钩子文件里,对框架自带的WebServer添加自定义逻辑,比如自由添加中间件,可以在这个局部,用自己熟悉的传统Web开发方式实现权限识别和重定向。
1.2从「前后端分离」到「前后端一体化」Web项目的技术栈也在转变,相当于是先发展出「前后端分离」,然后又用新方式回归了「前后端一体化」
1.2.1「前后端分离」以前的Web开发就像图上这个RubyonRails项目,图中粉色的前端代码,「寄居」在图中绿色的后端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搭建的项目,多数属于这个类型。
1.2.2「前后端一体化」在前面说的需要更多「前端开发者」成为「应用开发者」的背景下,新一代的JAMstack项目用「客户端为中心」的「前后端一体化」方式,解决了上面说的问题
新一代JAMstack的三大组成部分虽然没变,对应的内容却有很大变化,JS部分更加函数化和组件化、以JS为中心,HTML可以完全不在项目中出现,自动生成。BFFAPI变成项目自包含。和之前简单的静态托管相比,基于前端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函数的文件路径是有约定的,可以实现任意设计的RESTAPI。
构建产物会针对BFF、Web、SSR分别生成独立可运行的Server,这是对前端Serverless平台更友好的,Serverless平台可以自主选择让BFF、Web、SSR用不同方式在独立进程中运行,不会互相干扰。比如在SSR环节遇到app代码的的内存泄露导致SSR超时,WebServer不受影响,可以自动降级到CSR模式,返回静态的HTML给用户作为兜底,用户的HTML请求始终不会超时或挂掉。
对于SSR,同样可以前后端一体化的开发,图里高亮的useLoader函数中的代码,同时适用于SSR和CSR,如果这个Loader在SSR中已经预加载,CSR就会自动跳过,否则会执行。
SPR相当于有缓存机制的SSR,在Modern.js里也可以一体化的开发,只要使用这个预渲染组件。
SSG实际上就是在编译时运行的SSR,在Modern.js里只要配置SSG路由,就会自动启用这种编译逻辑,给路由生成静态HTML。CSR、SSR、SSG都是用同一份代码。
1.3新一代「前端三剑客」和MetaFramework除了在技术栈层面向「前后端一体化」转变,在工程层面,传统的「前端三剑客」也在转变成元框架这种新的工程基建。
1.3.1传统「前端三剑客」先来回顾下传统的「前端三剑客」,第1代和第2代如图上所示,也被大家熟知。而第3代「前端三剑客」由视图框架、Node.js命令行、Node.js框架三个方向组成。
其中Node.js命令行代表了工程化,其中最典型的是像Webpack这样的打包工具,以及Babel、PostCSS这样的编译工具。
视图框架和Node.js框架很好理解,就是之前讨论的MERN项目中的前端和后端部分
1.3.2第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的能力和收益
1.4基于「前端技术」的成熟GUI软件研发体系在「前端开发者」成为「应用开发者」的大背景下,技术栈、工程基建的发展,开始形成基于「前端技术」的成熟GUI软件研发体系。
1.4.1什么是「前端技术」先明确一下我们一直说的前端技术,不是指做UI的技术,而是由Web原生语言、WebRuntime、Web技术生态组成的技术栈,不是只在浏览器里才有前端技术,而是有WebRuntime、有Web语言的地方,就有前端技术
1.4.2DX和UX同样重要传统前端开发不是成熟的软件研发体系,缺乏足够的抽象和基建,导致DX和UX始终存在矛盾,此消彼涨。以往的产品开发中,习惯更重视UX,这有两方面的原因,一来是因为产品是由产品主导,因此更重视UX,不会过多