【深度好文】我在作前端构建过程当中的思考

做者:支付宝前端 玄寂
写在最前:欢迎来到「微贷前端技术」专栏,咱们将与你们分享前端各领域的高质量技术文章,包括但不限于移动端、小程序、互动技术/数据可视化、Node.js 全栈/中后台、基础架构、我的思考,不限于原创与翻译。咱们也欢迎优质文章的投稿。
编者按:本文做者曾负责过内部某产品(如下简称 X)的「构建」流程的重构工做,在重构过程当中深挖了许多知识点(为这种刨根问底的精神点赞👍),尽数浓缩在这篇五千字长文中,通篇读完绝对能带给你满满的干货!P.S.: 咱们假定你已经了解 Webpack 以及前端构建相关概念。如下为正文。

在个人理解中,对 X 的构建经历了如下两大过程:css

  1. Webpack 化
  2. 去 Webpack 化
乍一看就好像一开始走错了路,但其实,并无清晰的界限能够断定某一技术变动是对的仍是错的,由于全部的决定都取决于当下的状态。


Webpack 化

先来讲一说为何 Webpack 化。 我的以为 Webpack 最大的魅力是视万物为 module 的能力。它高阶的扩展能力,带来了极高的个性化能力;极其活跃的社区或多或少可让咱们少走路,也少走弯路;另外不得不着重提一下 CRA,CRA 在优化开发体验上下的功夫很是深,目前绝大多数基于 Webpack 的上层封装,都会有 CRA 的影子。 总而言之,Webpack :
  • 优异的 Add-on 设计
  • 成熟的社区配套
  • 活跃的社区氛围
  • CRA 提供了优异的开发体验模块
这些对于新生业务来讲有着致命的诱惑。

另外因为以前对 Ant tool 的维护,我成为了 Webpack 死忠粉。因此咱们天然而然地采用了 Webpack 作为构建的内核,也基于它的 Add-on 能力作了足够多的个性化能力输出。前端

一切看上去都没什么大问题,除了 Webpack 内置的缓存优化方案坑了咱们一次以外,没有任何的意外。

然而凡事都有个可是,随着 X 一步步发展,开发者的个性化需求也见涨。不出所料,愈来愈多的同窗但愿咱们能开放核心的构建能力、更多的参数配置,所以 webpack.config.js 又被搬上了台面。若是业务取向稳定可控,那么 webpack.config.js 绝对是滋生恶魔的来源,关于这一块探讨我在 2 年前写过一篇文章——构建工具的发展和将来的选择,这里就再也不展开了,我担心的根本问题是这给后续带来的维护、稳定性、安全性问题。 因为 Webpack 内置支持 pollyfill nodejs buildin 的模块,做为社区大库他们天然会使用一些 module,一般他们也会提供一个 dist 目录,以存储能够纯跑在浏览器端的文件。而做为开发者,在我所看到的上千的项目中,几乎 90% 的开发者都没有这个意识,他们习惯「拿来即用」。
node

这又牵出了另一个话题,颇有趣。我应该曾有 2 次在对外分享中问过你们一个问题:以下代码,涉及了几种模块规范?(你们能够在评论区发表本身的见解)

import a from 'a';

const hello = require('./hello');

const d = {
  x: 1,
};
module.exports.c = function c() {};
exports.b = {};
export { d };复制代码

让人惊讶的是,能回答上来的人寥寥无几。

不知道你们有没有想过元罪是什么?是 Webpack 这一类 universal 方案么? 仍是 NPM 把前端模块也引入了进来了?这是一个开放的命题,没有标准答案。react

回到正题,由于 Webpack 强大的包容性(固然这也怪咱们当初没限死),慢慢的咱们发如今业务中,竟然出现了一类 cheerio 的依赖,看到愈来愈多的案例把传统 PC、Node 开发思路用到了 X 之上。就我我的来讲,这是我不想看到的。webpack

另外,在调试环节,X 的研发流程中用户会设置编译模式(即只调试固定页),Webpack 贪婪式的编译模式(全量构建)是否真的契合 X 的调试模式?git

此外,随着 Web-IDE 的盛行,咱们也琢磨着把编译流程整合到浏览器端,Webpack 的厚重让可能性显得比较渺茫。

不过最最最重要的缘由仍是,Webpack 的效率仍略显局促,特别是在与友商的对比下。不过在这里要给 Webpack 抱不平的是,Webpack 的能力是远大于友商的,而这种能力,就像如今的手机,它的算力是过剩的,而这种过剩的算力致使了时间的上累。
github

调研之路,友商带来的启发

友商在安全的防御上作的是至关到位的,我很难经过 hack 的方式来窥视整个流程。
探究的过程当中留给我最深入的印象是对于克制的理解。产品层是克制的,功能是克制的,开放度是克制的。要真正在浮躁的互联网中作到如此「克制」仍是挺难的。

接下来咱们单纯从技术层面来讲一说。注意:如下都是我我的的理解,未必对。
web

友商的技术选型,按个人理解应该遵循四点:轻,快,可管控,够用就好。这几点在框架、DSL、构建服务等方面均有体现。

框架层面,友商相比蚂蚁,作的仍是很薄的,蚂蚁背靠 react 生态,但这颇有多是把双刃剑?咱们是否作好了开放所带来的的管控问题?我以为这些都是棘手的问题。而友商的轻薄,虽然在管控性上作的比较极致,可是面对开发者天马行空的需求,或许他们的问题更多的是如何来支持。典型的案例就是 NPM 支持,在蚂蚁这套技术体系下,咱们是纯自然就支持现有的前端研发链路的,因此在开发习惯的延续性上基本没有任何问题,但友商最初的作法是,让开发者在 X 的生态外本身建立一个 NPM 使用流程,这也就是为何在社区里面会有不少这类方案的缘由,然后续友商发布支持 NPM,但仔细琢磨,其实友商 NPM 更加偏向因而 component,而不是标准前端意义上的模块,好比不支持 nodejs module shim 就能够看出。反观咱们的 NPM 目前已被玩坏。
gulp

另外在梳理过程当中发现,友商在处理 *xml 文件和 *css 文件时,它是解耦的,贪婪的。稍微深刻就能够看到,他有专门的二进制编译工具负责此类文件的编译,利用编译工具能够批处理 *xml 和 *css 文件。这和咱们以前基于 Webpack 的方案有着巨大的差别,咱们的编译本质上是有上下文的,即好比 component 样式会有做用域提高,进而影响 page,另外这当中大量依赖了 Webpack 的语法分析 和 loader 机制,与此同时咱们还依赖了一个虽不属于构建却又能影响构建的外部过程,因此从根本上来讲,在现有的技术架构上咱们很难真正意义上超越友商。
另外,友商在框架层应该就考虑了模块加载,因此友商对于模块的加载模式优化或者内部模块间的管控,好比控制 exports 有着更加灵活的空间,但咱们在这一层依赖 Webpack 提供的 runtime,正由于这种模式,若是友商想要往好比 Web-IDE 靠拢,其实更加可行。对于这一层的认知主要是我接触到了 Stackblitz,以及以后慢慢了解到了 Systemjs。这部份内容我放到下节。

经过对友商的学习,看到他们的产品没有厚重的堆积感,我感觉到了小而美的力量。
小程序


CodeSandbox、Stackblitz 带来的启发

对于我来讲,个人命题就是尽量的让开发者以最快的速度来完成开发环境的初始化。我作过很是多基于 Webpack 的尝试,熟人常知的 happypack,cache-loader,thread-loader,hardsource,以及如何尽量的让缓存增长有效性,以及甚至基于 Webpack 的 memory fs hack 了一套本身的逻辑等等等等,但效果并不让人满意,甚至不少优化反而会致使很奇怪的问题。

很神奇的是,我有一次无心看到了友商在 worker 端代码的加载过程,它并无真正意义上的 bundle 过程,而是对全部的 worker 进行了全量的 http 请求,个人第一个反应是 http 有同源策略,为何他们还敢这样作,难道并发量不会成为瓶颈吗?这种方式我见不到相对 Webpack 的优点究竟是什么?!

这困扰了我很长时间,直至 Stackblitz 走到了个人眼前——最初就是那篇 turboCDN 文章。我基本上挖坟了全部有关 Stackblitz 的 Issue Twitter。实际上 CodeSandbox 在这一块上也用着相似的方案,但 CodeSandbox 的问题是,他在这块的实如今当时与其业务实现深度耦合,因此我更加倾选择 systemjs,至少它是独立的,且有本身的生态。随着了解的深刻,我逐渐对他如何在浏览器内实现伪 bundle 和 NPM 如何跑在浏览器端有了必定的了解。当时个人最大想法就是,我能够基于 systemjs 来实现一套动态和按需的加载方案,在本地开发阶段省去全部 bundle 过程,文件只有在真正用到时再进行编译,甚至经过一些方式,好比在浏览器端实现 fs,那么这套方案就能够被移植到 Web-IDE 上。基于这样的构想,我开始了很长时间的尝试,从 18 年 10 月初我写下第一行代码,代号被取为 Gravity,更多 Gravity 设计的内容我会放在下一段 。但在浏览器端实现 fs 和利用 web worker 来实现 compile 上我碰了不少壁,由于浏览器端没有 nodejs 环境,我要解决全部的 pollyfill 的问题,以及文件 resolve 的差别性。然而时间上并不容许我作过多的技术性探究,因此第一阶段我退而求其次,把这部份内容架设在了本地,经过启动一个 koajs 实例来解决,即浏览器端发生资源请求时,经过中间件(所处 nodejs 环境)来实现真正的编译过程。仅仅只是这一步尝试,就把我原先在 mac 上花费的 40s+ 编译时间降到了 8s 左右,说实话我本身都无法相信。

学习 CodeSandbox、Stackblitz 带来的启发是,利用纯浏览器带来的架构上的变动或许是另一种出路。

再来谈谈 bundler 历史

最近有一篇文章出如今社区,题目是: A Future Without Webpack。如文中所说,这几年里,JavaScript 打包过程从一个仅仅针对生产环境的优化,发展到了如今 web 应用开发必要的一个步骤。你喜欢也好,厌恶也罢,你都很难否认一个事实,那就是它大大增长了 web 应用开发的复杂度。而 web 领域一直来它所引觉得傲的的点是,view-source 和 easy-to-get-started。这或许如今成为了一种讽刺。

为何咱们须要 bundler?

把时间倒退到六年前,那会儿咱们对于打包的概念应该仍是在 grunt 或 gulp 流式的任务处理,对资源文件的处理也仅仅是压缩和拼接。然后前端界兴起了模块化浪潮,模块化后的代码放在哪儿,又如何被引入,相信这个问题是那会儿前端们最为关注的事情,因此又要翻翻老帐 - seajs 有本身的源服务器来承载模块化后的模块也是理所固然的事情。但你们也都知道,世界上最大的代码源服务是 NPM,但如上文中提到的起始时 NPM = “Node.js Package Manager”,它并不真正意义上服务于前端浏览器。可是开发者对此的诉求实在是太大了,而众所周知 Node.js 使用的是 CJS 模块规范,因此不通过打包根本不可能运行于浏览器中,而诸多的模块定义,也给了 Browserify, Webpack 等发挥的空间,特别是 Webpack universal 的概念很是契合大众诉求。

固然做者观点更可能是当下已然 2019 了,咱们应该往前看,由于在浏览器端已经支持了 ESM。对我而言,我以为这种想要跳出困局寻求突破的精神是更加值得学习的。另外也带来一个问题,撇开做者提供的思路或实现,本地 bundle 基于现有的技术架构,可否有所破局。


新技术方案 Gravity

在谈新方案 Gravity 前,还要来回首下我在几年前写的一篇文章 - 支付宝前端构建工具的发展和将来的选择,那会儿咱们最大的困扰是配置带来的不可控性。因此那会儿我提出了构建因子以及 preset 的想法。出于对配置的敬畏,在 Gravity 中我把这一套想法彻底实现了出来(其实在那篇文章几个月后就有一版实现,但不幸的是没有继续深刻就流产了,另外也由于我工做内容而被动调整了)。

因此 Gravity 最 base 的架构思路是让 Gravity 变成构建工具的工厂,让各类业务形态的构建变成 Gravity 的一种上层实现。要实现如上这个想法也就意味着,Gravity 必需要有好的插件机制。这个时候 tapable 天然而然成为了个人最佳选择,对于 tapable 渊源还要从我解析 Webpack watch 实现提及,固然这不是咱们今天的重点。重点是 Webpack 就是基于 tapable 实现出来的,它的灵活性健壮性毋庸置疑,另外我不折不扣研究过 tapable,真的是喜欢到不行。还有很是重要的一点是,我用 tapable 来设计插件机制,能够对开发者很是友好,由于几乎不须要学习。

另外还有一个好处是基于 tapable 我能够很是轻松实现一种时序,好比说我如今要实现一个 css 文件的加载 loader,在上文中我大概说了由于在时间上的缘由我并未在一期就尝试把全部流程都丢到浏览器内完成,而是把一部分工做丢给了 koa 的中间件,因此在文件处理上(Webpack 中叫 loader),我实现了一套动态生成中间件的方案,缘由在于实现一个 css 文件的加载可能须要通过多个加载器,好比 post-css,css,style,这其中就有时序的问题,因此借由 tapable 我能够很方便来根据描述文件(类 Webpack rules 设计)动态建立一个时序,转而变成一个中间件。

在设计 Gravity loader 时,和 CodeSandbox 同样,咱们把 API 尽量的往 Webpack 靠了。缘由就在于我想要有复用 Webpack loader 的可能性。另外对于上层实现,也会更加友好,由于基本能够作到和 Webpack 长得同样,用的同样。

另外还有不少细节我这里就不在阐述了。

Gravity 会进一步维护,也会在合适的时候开源。它的目标也很是明确,成为真正意义上浏览器端方案,在上层实现层能够对接到更多的业务场景。最终经过一个 Web-IDE 把全部的事情都串联起来。

总结

抛弃偏见,我相信 Cloud IDE 必定是将来,而面向 Web 的架构必定是当务之急。


「微贷前端技术」致力于与你共享高质量的技术文章

欢迎关注咱们的专栏,将文章分享给你的好友,共同成长 :-)
相关文章
相关标签/搜索