编译内核无需源码-多重编译内核生态系统的快速开发经验

1. 背景——

跨端是后端开发过程中经久不衰的话题。 为了满足不同场景的跨端需求,许多不同的跨端解决方案将会诞生。 为了支持各端的开发需求,我们提供了跨端解决方案的多编译内核,并逐步扩展各编译内核的特性,为开发者提供更多可能,加速开发体验。 这就是所谓的多重编译内核。 生态。

多重编译内核生态系统

无论是在各种后端工程中,还是跨端解决方案中,毫无疑问Webpack频繁出现,在Taro中也是如此。 基于Webpack编译出来的内核能否用于包含各种小程序在内的终端项目提供了非常好的开发体验,并且实现了一整套完整的初步解决方案,实现所谓的“一份代码,多终端运行”的能力特征。

但事实上,在跨端解决方案不断迭代的过程中,一些端生态系统可能遇到的各种问题和困难已经逐渐显现出来。 比如我们基于ReactNative的App端就是这样的。

基于Webpack实现RN项目输出的能力,并使用Metro完成RNAApp的打包和调试等,是一个非常方便的解决方案,但在实际项目中,比如框架层面无法直接与Metro交互,等等问题很多,维护起来也有很多不便。

这就是为什么我们需要在框架内提供Metro编译内核,以统一RN侧的打包工具,为ReactNative生态提供更好的兼容性。

与Webpack相比,使用Vite来编译项目在Web端有很多优势,在社区中呼声还是很高的。 不少开发者希望支持使用Vite来编译跨端方案的项目。 那么如果多端项目的编译能力能够与基于Vite的Webpack编译内核基本一致的话,实际体验会如何呢?

正是因为有很多这样的激励因素的存在,我们才不断思考解决方案。 最后,在面向多终端的开发场景下,跨终端、跨框架之后,我们有了一个新的目标——支持多编译内核的开发生态。

专为多终端开发

在多端开发场景下,通过使用Webpack、Vite、Metro等编译工具,可以实现包括Web、App、小程序、鸿蒙等多端开发场景,开发者也可以根据需要进行选择以满足项目的需要。 获得更好的开发体验。

虽然我们在编译过程中主动选择使用多编译内核,但这样的架构不仅有优势,而且还有很多需要解决的问题。

例如,需要提供一致的生命周期支持,以平滑各个插件在支持不同编译内核时的差异; 框架配置不会因为编译内核切换而造成额外的学习成本……编译内核切换可以用于开发、生态等,各个层面都能流畅无缝,这也是我们正在努力实现的目标。

2. 极快的建立——

多重编译内核其实是一个很好的架构方案,可以支持各个开发场景特性的实现,给开发者更多的选择自由度。 但作为跨端解决方案,不能仅限于此。 在多终端的开发场景和多编译核心的开发生态中,如果想给开发者提供极致、快速的开发体验,应该怎么做?

以各个编译核心及其生态为起点,在框架内提供针对不同开发场景的快速搭建能力,结合框架内的插件系统,最终获得基于多编译的全新架构核心解决方案。

Webpack编译内核

在这类编译工具中,相信大家都很熟悉Wepback。 只有将开发者的代码输入到Wepback内核中并编译,才能得到Web或者小程序等终端应用

在Webpack编译核心中,我们提供了项目配置处理、不同模块的加载规则等通用能力,通过组合进行多端编译; 小程序端也有插件来处理小程序模块和原生模块的加载; Web端还需要按照Patterns封装插件来处理应用、页面入口等。结合框架的插件机制,开发者还可以干预编译过程,为项目提供定制化的扩展满足个性化需求。

如果你想在Webpack核心中实现更好的构建体验,你不仅可以选择自定义插件,还可以选择更多选项。 例如Webpack5的持久化缓存就是其中之一。 考虑到编译安全比速度更重要,该功能默认会关闭。 事实上,大多数情况下,持久化缓存还可以帮助开发者以更稳定的方式提升开发体验。 如果项目开发过程中编译速度有限,这不失为一个不错的选择。

依托ESBuild等不同工具,我们提供各种可配置模块供开发者选择。 他们可以根据自己的需求选择更适合项目的工具。 依赖预编译也是该设计的重要特点之一。 通过提前编译依赖,在实际项目中可以得到很大的提升。 极大地提高了我们的研发效率,受到了社会各界的广泛好评。

依赖预编译,即PreBundle功能通过ESBuild扫描项目依赖关系并提前编译,并利用Webpack的模块联合功能分别构建Host和Remote应用程序。 这个有趣的功能可以实现,并帮助开发者改善多终端开发的编译体验。

原理看似并不复杂,但不同终端实际执行的操作会有些不同。 该小程序终端包括以下步骤。

首先,需要扫描项目中的依赖关系。 通过ESBuild可以轻松识别项目中使用的JS模块。 不过也有很多细节需要注意和处理,比如识别Vue中的设置句型,防止误判和遗漏,这就需要预编译。 模块; 同时,其他终端需要的依赖也必须编译,避免误判、导出终端不支持的依赖等。

获取到项目需要的依赖之后,再次通过ESBuild进行打包,得到项目需要的Bundle,但是这里我们还需要解析对应依赖的入口方式,同时使用自定义的SWC插件优化代码编译能力,将基于CJS模式输出​​的依赖转换为ESM编译结果以实现兼容性。

编译内核无需源码-多重编译内核生态系统的快速开发经验

想要在ESBuild输出中保留原来的路径,会让最终的结果变得不可预测,所以我们需要将其压平,只通过虚拟模块保留原来的路径。 最后,将编译后的内容进行缓存,防止多次编译带来的性能损失。

接下来,我们需要使用Webpack通过MF插件来打包Remote应用程序。 在这个过程中,我们还需要传入编译所需的各种插件。 例如ProviderPlugin用于提供小程序所依赖的运行环境并缓存编译结果。 同时会生成Host应用程序的必要配置,并将Host应用程序及其资产注入到小程序目录中以进行最终的项目运行。

与小程序相比,Web端的很多步骤都非常相似,比如后依赖扫描和打包等,虽然具体配置上会有一定的差异。 例如Web端APIShaking的自定义Babel插件需要SWC插件。 重新实现,以防止 PreBundle 功能不支持基于 Taro 的组件库。 同时,由于小程序和Web本身的差异,后续的步骤也会有很大的差异,比如需要为Remote应用配置代理。

虽然在Web端编译Remote应用程序需要通过ContainerPlugin创建额外的容器条目,但就像小程序一样,不需要使用ProviderPlugin来注入运行时和其他依赖,也不需要将远程依赖更改为同步模式和其他操作。

配置Host应用插件时,需要通过ContainerReferencePlugin更改Remote提供的所有对远程模块的依赖,并通过特定的引用将容器添加为外部资源,让其加载远程模块,同时注入必要的Webpack公共运行时的工具。 功能。 最后,在使用之前,我们还需要为Remote应用程序设置一个代理,以便Host应用程序可以加载应用程序依赖项。

事实上,当我们直接在Host应用程序中使用卸载的Remote依赖项时,会导致应用程序报错。 在业界一些类似的解决方案中,往往需要通过 Babel 来改变引用依赖相关的代码逻辑,但实际上,如果在应用入口处嵌套了一层动态加载,Webpack 会预加载这个 Remote 依赖编译内核无需源码,以防止相关问题。 这也是MF官方例子中推荐的方式。 在这个功能方案中,为了支持户外使用、多页面应用创建等功能,我们选择基于virtual-module来实现这一操作,以最大限度地利用Webpack自身的能力和生态。

通过NutUI的示例工程测试编译和预编译时,我们可以发现通过PreBundle功能可以实现很好的优化。 在社区开发者的经验中,我们也收到过类似的反馈。 使用前小程序编译速度约为*。 *20%**,网页申请时长也将增加至原计划的**30%**左右。

这样的性能提升结果非常令人满意,但是在跨端环境,尤其是小程序环境下,模块联邦能力真的那么好用吗? 受小程序本身的限制,利用网络请求来实现该功能将大大增加小程序的用户体验。 牺牲用户体验不应该是一个成熟框架应该做的事情。

模块联合功能实际上是基于ModuleFederationPlugin包提供给开发人员的。 如果我们根据它的两个外部插件进行逻辑调整来优化这个实现呢?

这两个外部插件中,ContainerPlugin创建小程序使用的Remote应用时,需要在插件中为小程序额外创建一个容器入口,并提供手动模块注册相关逻辑。 通过劫持ContainerEntryDependency,它将依赖于异步逻辑。 改为同步; 同时,需要收集使用过的Webpack工具函数,并删除多余的条目、运行时等块。

在ContainerReferencePlugin中,需要将预编译依赖设置为远程模块,注入收集到的Webpack工具函数,更改远程模块中的id映射对象,并插入手动注册模块相关的逻辑,并传入require依赖app.js 所有预编译块和remoteEntry; 同时,需要删除多余的文件,例如远程模块加载不再有用的模块,以减少不必要的代码输出。

通过提供基于ContainerPlugin和ContainerReferencePlugin的神奇插件TaroModuleFederationPlugin供框架使用,可以在不损失用户体验的情况下在小程序中使用该功能。 开发者也可以自己配置和使用,但如果你的项目使用的是跨端框架,那么直接使用PreBundle功能将是更好的选择。

编译内核无需源码-多重编译内核生态系统的快速开发经验

事实上,TaroModuleFederationPlugin 和 PreBundle 功能都可以在第三方项目中配置和使用。 以PreBundle为例,通过Chain传入Webpack配置,并在实例上执行run方法,就可以得到最终需要的Webpack项目配置文件。 ,然后启动项目。

目前,很多外部项目都接入了该功能,以获得更好的开发体验。

vite编译内核

实现Vite编译内核可以说是赶上了时尚,也是当前社区开发者的心声。 不过,也希望支持Vite编译能够提供更加多样化的开发场景,让开发者可以根据实际需求自由选择。 对应场景下项目所需的编译内核。

与传统的编译模式相比,在启动项目时,需要先扫描整个项目的依赖和代码,构建应用后即可提供服务; Vite使用ESBuild建立依赖关系,并在浏览器请求源代码时将相应模块转换为原生。 ESM按需提供源码,开发速度快很多。

通过Vite,我们可以为Web端提供更好的开发体验。 其实还需要兼容各种小程序的特性,比如路由、TabBar等,以及各种API和跨端组件库,这些也需要兼容配置。

同样,Webpack编译内核中已经支持的各种能力和特性也不能落下,包括对React、Preact等后端UI框架的支持、多路由模式等,这些也需要在Vite编译中支持核心。

与Web端能够实现的可观提升相比,适应小程序场景的实际利润相对要低很多。 因为缺少持久化缓存,所以在编译小项目时的实际体验是和Webpack进行对比的。 没有明显的提升,只有配置跳过更新的页面才能为所需的项目提供良好的开发体验。

同样由于小程序本身的限制,Vite无法在开发模式下完成小程序的编译。 我们在生产模式下提供应用程序和各个页面入口的编译,写入对应的小程序目录,并使用虚拟模块的方法更新页面,并使用适合小程序的Vite插件完成小程序的编译。

综上所述,在Vite编译核心中,我们为小程序和Web端提供了配置、入口、样式插件,以适应各个终端的能力。 事实上,小程序还需要页面插件来为页面提供额外的处理。 结合框架自带的插件机制,我们可以让Vite适应跨终端方案,同时生成与其他编译内核的互操作选项供开发者使用。

整个编译系统中,不仅是我们提供的Vite编译内核,CLI和配置工具也需要进行相应的调整。 结合内核提供的代码和样式编译能力,可以为大多数编译场景提供支持。 同时编译内核无需源码,我们也在逐步推动社区完成一些工作,比如小程序的总封装能力,以及Web上的多路由应用模式等,这些工作将在1年之内完成。在未来的版本中增加一。

事实上,您在使用Vite内核时可能还会遇到一些问题。 未来我们也会尝试优化它,比如将输出打包成CJS模式。 配置splitChunk后,可能会造成循环依赖,导致小程序报错; 另外就是刚才提到的编译缓存,在小程序中二次编译速度可能不如基于其他打包工具的编译内核。

Metro编译内核

从 Webpack 导出 RN 项目以使用 Metro 是一个自然的选择。 依托Metro,我们还可以对ReactNative的很多操作从编译时到运行时进行适配,为开发者提供更好的开发体验。

从CLI读取项目配置到编译Metro内核并启动调试服务,Metro比Webpack更好地兼容ReactNative现有的生态环境。 框架层面需要做的是为开发者提供合并和定义Metro配置的能力,让Metro在适配其他终端时能够理解我们在Webpack上的操作,并在一定程度上进行复制。

基于@tarojs/rn-transformer处理RN的入口文件,编译时注入页面的打包方法,将入口的全局样式注入到页面中; 同时,对运行时进行一定的改动,比如在路由基础上ReactNavigation封装提供了动态创建导航的方式,但是封装了导航对应的API内容; 在运行时处理应用程序和页面配置等。

与之前的解决方案相比,Metro可以提高编译速度,提供更好的SourceMap支持,提高开发效率和体验; 同时可以避免Webpack提供的多入口模式带来的问题,增加包大小等; 它还与 ReactNative 的默认配置保持一致,并支持更灵活的合并形式。

在App端,我们利用Metro对项目在Transformer和Runtime两个层面进行修改,使方案能够支持多终端,更流畅地支持使用RN完成App端的开发。

如果你想在框架外使用这个开发出来的RN能力,有一个相对简单的方法可以快速接入。 @tarojs/rn-supporter 可以提供编译、运行时配置、资源样式解析解决方案的支持。

具体用法如右侧代码所示。 需要说明的是,在这种外部应用方案中,路由相关的逻辑需要自己处理。

综上所述,在我们的编译系统方案中,通过CLI和插件系统将多个编译核心结合起来,为开发者提供跨端的解决方案。

除了编译内核之外,插件系统还可以作为一个非常好的帮助框架,为开发者提供基于编译平台的解决方案。

在通过CLI完成编译工作的同时,我们还可以通过加载不同平台插件来完成编译扩展和运行时能力注入,以提供最终的编译结果。

在插件体系中,我们从CLI、编译流程、编译平台三个方向提供能力扩展。 结合各种生命周期,如:onBuildStart、onCompilerMake、modifyWebpackChain等,每个编译核心可以为不同的端提供更加个性化的编译。 定制服务。 依托于此,我们可以为React、Vue、Flutter等不同的DSL提供插件; 我们还可以提供小程序、鸿蒙等多终端的终端平台插件,辅助完成编译。

编译内核无需源码-多重编译内核生态系统的快速开发经验

在这样的框架下,开发者还可以根据我们提供的生命周期纵向或横向扩展编译能力,扩展对各个终端的编译能力支持,比如飞书、钉钉、奇微等小程序和鸿蒙终端。 平台插件由 Taro 技术委员会或社区开发者通过这种方式提供。

同时,在其他方面,也有很多小的优化来提升构建体验。 比如SWC看似和Babel不一样,但是在很多特殊的应用场景下它已经足够了,比如在依赖预编译中提供代码编译,以及应用程序页面配置读取方面有很多改进等等。

ESBuild 也是如此。 不仅在PreBundle功能中使用,我们还在压缩代码和样式中提供了相关配置。 不过,与terser和csso相比,仍然存在压缩功效等问题,再加上对其稳定性的审查,我们并没有将其配置为默认选项,可以由开发者选择。

3. 总结与展望——

只有将跨端解决方案与多编译器内核生态相结合,才能在开放的跨端跨框架解决方案中为开发者提供更高的自由度和开发体验。

通过React、Vue、Svelte等DSL插件,结合Web端渲染规范、标准API和小程序组件库等,我们可以为不同终端提供可靠的跨端能力,同时当时,在Webpack、Vite、Metro等编译内核的帮助下,我们可以提供越来越高效的开发体验,为日常开发工作提供更多便利。

至于框架本身,未来我们还会提供对更多后端UI框架或者编译工具的支持,比如Solid等。

在性能方面,我们也希望为小程序提供更好的热重载、预渲染等能力,提升多端开发体验,提供适合更多场景的多端性能优化方案。

同时根据场景提示性能优化方案,以及性能指标评估工具,为项目优化提供指导。

对编译Flutter核心的支持也提上了日程,并将持续在社区中推广; 通过WebAssembly进行小程序的运行时优化等,也在Taro未来的道路规划中,敬请期待。

同时,我们也期望提供框架测试解决方案,通过TestRenderer为跨端方案测试提供更好的体验。

最后,关于Taro的开源工作,长期以来都是由Taro技术委员会主导的。 在 Taro 生态的 700 多名贡献者中,有 50 多名来自行业各公司的同事常年参加 TaroTOC 会议分享。 在终端解决方案的不断迭代中做出了大量的努力和贡献,这也是我们能够在多编译内核生态中获得越来越多极致开发体验的原因。

正如技术委员会成员@xueshuai在贡献v3.6版本代号时所说,“‘Reach’代表着不屈不挠和希望,为开发者带来更好的用户体验,是所有开发者的坚持。” 希望社区的积极氛围能够引导太郎的生态变得越来越好。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 编译内核无需源码-多重编译内核生态系统的快速开发经验 https://www.wkzy.net/game/193347.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务