本文预计阅读 25 min,建议先收藏后观看~
时光荏苒,这绝不平淡的 2022 年已经走上历史的黄页,新的一年也逐渐看不到故人回首的光影。感谢你对前端技术领域持续关注,我们一直在这里等你。
① 2022 年前端人偏爱什么?
数据来源 bestofjs (https://risingstars.js.org/2022/en)
② 你也许会有同样的疑问?
团队的新项目要不要试试用 Svelte 来构建?但不清楚好用吗?性能怎么样?
RSC(React Server Components) 和 SSR 傻傻分不清楚?
项目正在用的测试库(Jest、Cypress)要不要升级到最新版本?
听说了 Turborepo 的强大打包实力,但是我当前在哪里可以体验到,现有项目可以接入吗?
老项目想从 Babel 切换到 SWC ,有没有什么限制?收益大吗?
到底用 Tailwind 还是 styled-components ?团队内部已经吵起来啦!
手上有个业务复杂的大项目,有没有必要迁移成微前端架构?
Github Actions 是什么?我们可以跨团队共建可复用的 CI 节点吗?
ChatGPT 可以帮我写代码吗?什么是 web3.0?跨端方案选什么?
如何搞点炫酷的Web 3D?听说 Serverless 是前端开发的福音?
等等等等......问题太多啦,什么时候才能全部搞明白?
③ 往下看,马上给你解答
如果你对上面所叙述问题有同样疑惑的话,请一定要读完本文!相信我们的精心制作一定会给你带来丰富的收获,一起走入跌宕起伏风云变幻的前端世界吧!
“天道酬勤,我们与梦想总是双向奔赴”
数据来源 star-history (https://star-history.com/#sveltejs/svelte&facebook/react&vuejs/vue&lit/lit&solidjs/solid&Date)
从近年来的数据来看,React 和 Vue 是现如今前端入门必学的两大框架,各大技术论坛上也常常有人为两者孰优孰劣吵得不可开交,从此便可显现出两大框架的布道范围之广泛,但如果仅仅局限于让两个框架一较高下,那格局便小了。Vue 和 React 两者的差别说大也不算大,它们的主体思想都是数据驱动视图,直观表现都是响应式更新,实现载体都是 Virtual DOM(除了 Vue1 )。由于采用了 Virtual DOM ,React 和 Vue 在前端都天生携带一个体积不小的“运行时”,它将根据输入的数据生成 Virtual DOM,并执行调度和渲染流程。
根据 HTTP Archive 报告 (
https://httparchive.org/reports/state-of-the-web#bytesTotal) 显示,2015 年网页的中位数大小为 1280 KB ,到 2022 年已增长到了2000+ KB ,庞大的网页会占用大量网络带宽,使页面加载时长更长,一些网络资源不发达的地区更能体会到这种影响。为了改善这种趋势,Svelte 出现了。Svelte 旨在让开发者以最少量的代码,产生最轻量的响应式应用,并且在运行期间产生最小的性能开销。从原理上讲,Svelte 便与 React 和 Vue 两者有一点不同:它不依托 Virtual DOM 技术来实现响应式更新,取而代之的是直接利用自定义的语法规则来实现响应式数据的分析、绑定以及响应式过程代码的实现,大部分场景下最终编译生成的产物大小可以做到和原生实现媲美,且性能不逊色于 Vue 和 React。
Svelte 在 2021 年度 stackoverflow 网站上关于“最受欢迎的 Web 框架” (
https://insights.stackoverflow.com/survey/2021#
most-loved-dreaded-and-wanted-webframe-love-dread) 调研中位列第一,在 2022 年度的此项调研 (
https://survey.stackoverflow.co/2022/#
most-loved-dreaded-and-wanted-webframe-love-dread) 也位列第二,从好评比例来看使用者们对这个框架很满意。github stars 的增长速度也说明了 Svelte 是一个在开发者社区中十分有发展潜力的 Web 框架,在生态日趋完善之后,Svelte 很可能是一个比肩 React 和 Vue 的存在。
下面我们将从各方面来看看这个新星框架与 Vue 和 React 相比都有哪些区别,从而让大家对这个框架的潜力和局限性有更多的认识。
① 状态管理和更新机制
Svelte 是一个模版编译器,一个模版文件分为 Script 、CSS 、HTML 三个部分用以描述一个组件:demo (
https://codesandbox.io/s/frosty-worker-o66p2f?file=/App.svelte), HTML 部分采用 Mustache 模版引擎的语法动态地描述 DOM 树。Script 分为 Module Script 和 Instance Script ,分别用于外部库的引入和组件状态的维护。编译器工作流程大概分为四个步骤:生成 AST 、封装 Component(收集上下文数据)、渲染 Component(拼装代码)、生成最终代码(链接 runtime 库)。Svelte 通过 AST 的方式对代码进行静态分析,可以实现数据和视图的绑定,还可以根据上下文和配置文件,实现剪枝和依赖的按需引入,尽最大的努力优化产物大小和性能。Svelte 为了避免不必要的 DOM 操作,为产物实现了一套原理十分简单的基于 Dirty Mask 以及守卫判断的 DOM 更新机制,实现了细粒度的更新。
Virtual DOM 类的技术,则是在运行期间实现数据和视图的绑定,当组件递归式地执行渲染时,组件内部的状态便被动态地注册,当组件更新状态时,运行时将调用用户定义的渲染函数,重新计算 Virtual DOM ,并利用 DOM Diff 算法来决定哪些 DOM 需要更新,用户如果想优化 Virtual DOM 的计算流程,避免不必要的计算,则需要自行调用运行时提供的函数来实现剪枝操作。
② SSR渲染
Svelte 天生自带了 SSR 渲染的能力,用户如果有相关需求,便可通过简单的配置实现 SSR 产物的生成;Vue 如果要生成 SSR 产物则需要借助 Nuxt 、Quasar 等专门的呈现框架库的支持,React 与之对应的库则是 Next 、Remix 等。
③ 跨平台
React 、 Vue 、 Svelte 都具有良好的跨平台能力。由于 Virtual DOM 把渲染过程高度抽象化,Virtual DOM 不单单可以描述浏览器的 DOM 结构,还可以适配其他如 Android Layout 等渲染目标,抹平了浏览器应用和客户端应用的差异。
④ 产物大小
Svelte 最大的卖点之一便是其小而精的产物体积。Svelte 由于可以根据源代码上下文只编译出对功能实现有用的代码,并且对 Runtime 包的依赖做按需引入,另外其 Runtime 包的实现十分原子化和扁平化,编译器很容易对产物做剪枝处理,在这些机制的共同作用下保证了 Svelte 以最低的空间成本实现最终产物。不过由于需要实现组件的状态和生命周期管理等功能,单个组件的源码-产物大小转换放大倍数可不小,和 React 这种可以以一个函数直接描述一个组件的形式相比,Svelte 产物体积随源码体积增长的斜率要比 React 大。有人创建了一个项目尝试以图表的形式直观地表现出了这个效应。
数据来源 github (https://github.com/halfnelson/svelte-it-will-scale)
将两条体积增长线放一起可以直观地看出,当源码大概达到120 KB 规模时, Svelte 相较于 React 将不存在产物体积优势。 不过这种规模的项目在现实世界中很难达到,对于使用者来说,完全可以认为 Svelte 是一个轻量级的框架。
⑤ 性能
Svelte 的响应式更新机制较为简单,它会使用状态更新的 update 函数,这个函数内部只做了两件事:比较并更新 component 上下文信息、标记脏数据掩码(make_dirty) 。Svelte 的这套响应式更新机制远没有 React 和 Vue 的调度更新复杂,实现的更新粒度也十分细,不过在某些场景下这种机制存在弊端。
由于是基于静态代码分析, Svelte 难以从视图中收集到十分详细的订阅信息,这导致了 Svelte 对于对象引用的赋值操作可能会存在无用更新:
在这个 demo (
https://codesandbox.io/s/lingering-resonance-rjz5gh?file=/App.svelte) 中,虽然对于 person 状态视图中只订阅了其 name 引用,但是对于另一个引用 age 的修改操作,仍然会触发更新操作的执行。
这种情况在 Vue 的实现中并不会发生,Vue 状态的依赖是在运行时收集,因此可以实现在状态修改时更精准地判断视图是否需要执行更新操作,从这个 demo (
https://codesandbox.io/s/musing-dhawan-k8n2wn?file=/src/App.vue) 中我们可以得到验证。
Svelte 的性能表现和 Vue 、React 等框架相比究竟如何,我们可以通过一个项目来有更直观的认知(
https://github.com/krausest/js-framework-benchmark)
这个项目对大量的前端框架实现了表格数据的增加、删除、换位等操作,并通过无头浏览器测试各种操作的性能,我们可以通过跑分结果大概了解各种框架数据更新操作的性能,这里筛选了原生、Vue 、Svelte 、React 的结果。
从结果来看,Svelte 的性能与 Vue 不相上下,它们都比较接近原生实现的性能,并且大部分操作性能都优于 React 。不过,实际上在很多情况下,一个系统整体的性能并不取决于选择的框架,而是取决于开发者功能实现是否合理,Svelte 在很大程度上已经帮助其使用者完成一部分优化工作,这可以说是它的魅力所在。
作为一名优秀的前端工程师,当你做好了技术选型并确定了某个 Web 框架准备施展拳脚时,会发现要从头构建一个完整的 Web 应用程序还需要考虑许多重要的细节。
Web 框架如 React、Vue 为开发者提供了官方的脚手架,让我们无需深入学习配置构建工具的情况下就可以快速创建一个 Web 应用,使我们专注于编码,不用操心构建工具。
通过脚手架创建的项目可以解决部分工程化的问题,然而脚手架不是万能的。当我们想搭建一个 SSR Web 站点就需要用到更高阶的框架能力、更高级的构建配置、更复杂的项目部署。
呈现框架为开发者提供了更多便利,下图是近年来相关框架的使用度排名:
数据来源 stateofjs
(https://2022.stateofjs.com/zh-Hans/libraries/rendering-frameworks/)
① Next.js
Next.js 是一个灵活的 React 框架。 它每周有超过 300 多万次 npm 下载和 9 万多个 GitHub Star,足以说明了它是构建 Web 最流行的框架之一。2022 年10月 发布了 Next.js 13 ,带来了一个全新的版本:
Webpack 已被下载超过 30 亿次,它已然成为构建 Web 不可或缺的一部分。然而它也已经达到了使用基于 JavaScript 的工具可能达到的最大性能的极限。 所以 Next.js 另辟蹊径,选择了 Turbopack,它的架构吸取了 Turborepo 和 Google 的 Bazel 等工具的经验教训,这两个工具都专注于使用缓存来避免重复执行相同的工作。
Next.js 13 也引入了一个新的基于文件系统的路由器,该路由器构建在 React Server Components 之上,支持布局、嵌套路由、加载状态、错误处理等。
React Server Components
RSC 出现的背景
主要原因是因为大量的 React 组件依赖数据请求之后才能做渲染。如果是这样,那么每个组件自己去请求数据的话会出现子组件要等父组件数据请求完成,到渲染子组件的时候才会开始去请求子组件的数据,也就是官方所谓的 WaterFall 数据请求队列的问题。而将数据请求放在一起又非常不便于维护,出现了一个两难的问题。
RSC 是如何解决组件依赖数据渲染的问题?
既然组件是依赖数据做渲染的,那为什么接口不直接返回渲染后的组件呢?所以他们提出了 Server Components 的解决方案。方案的大体是将 React 组件拆分成 Server 组件(.server.tsx)和 Client 组件(.client.tsx)两种类型。 其中 Server 组件会在服务端直接渲染并返回。
与SSR的区别是什么?
SSR 和 RSC 可以结合使用,SSR 目前大部分被用在首屏加载的场景,之后的页面交互可以选择用 RSC
RSC 有哪些优势?
大家可以通过 React 官方的演讲和实例 (
https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html) 来了解 RSC 的更多内容。React Server Components 仍在研发中, 如果自己在项目使用 RSC 的成本还是很大的,所以推荐在 Next.js 中使用 RSC ,这样会轻松很多。
同时,如果使用 Next.js 中的 RSC 和 嵌套布局 (
https://beta.nextjs.org/docs/routing/pages-and-layouts) 可以立即呈现页面中不需要特定数据的部分,并显示正在获取数据的页面部分的加载状态 (
https://beta.nextjs.org/docs/routing/loading-ui) 使用这种方法,用户不必等待整个页面加载完毕就可以开始与之交互。
也可以与 React Suspense (
https://beta.reactjs.org/apis/react/Suspense) 结合使用,可以获得以下优势:
② Nuxt.js
Nuxt.js (https://nuxt.com/) 是一个构建于 Vue 生态系统之上的呈现框架,它为编写 Vue SSR 应用提供了丝滑的开发体验。
2022年11月,官方发布了 Nuxt 3.0 稳定版,Nuxt 3 是基于Vite (https://vitejs.dev/) Vue3 (https://vuejs.org/) 和 Nitro (https://nitro.unjs.io/) 的重写版本,具有一流的 Typescript 支持。
开发者可以基于它初始化新项目的基础结构代码,或者在已有 Node.js 项目中使用 Nuxt.js。
③ 对比
Next.js、Nuxt.js 这两个框架的重心都在 Web 部分,对 UI 呈现部分的代码组织方式、服务器端渲染功能等提供了完善的支持,让开发者开箱即用。
没有最好的框架,只有最合适的框架。 本质上 Next.js 是一个 React 框架,另一方面 Nuxt.js 是一个 Vue 框架,两者的目标都是来简化开发成本和提高开发体验的。
数据来源 bestofjs(https://risingstars.js.org/2022/en#section-test-framework)
Jest 和 Cypress 都是使用 Javascript 进行编写测试用例的老牌工具,也都有完整的文档和生态对各种测试情景进行支持,对于前端同学来说学习成本较低。同时,这两个工具也都支持 CI/CD 集成,在提交代码合并请求的时候可以自动运行测试用例,简化回归测试流程。
相较于2022年,Cypress 和 Jest 新增 Star 数较 2021年都有所下降,尤其是 Jest,从21年的第5名下降到了第7名。而微软团队开发维护的 Playwright 的新增 Star 数连续三年遥遥领先,总 Star 数也在22年首次超过了 Jest 和 Cypress,达到了46.3k。其优势在于多浏览器和多语言的支持。不过因为 Playwright 相较于其他测试框架还比较新,生态支持还不如其他的老牌测试框架。
当然测试是确保程序正确运行的保障,不是为了单纯提高测试覆盖率而写的,毕竟写测试也是要占用开发人力的。至于在工作当中如何设计实用的测试用例,把握好测试覆盖的粒度,就是一门更加深入的学问了。
① 单元测试:Jest
Jest 是基于 React 的应用程序的首选测试框架。 由 Meta 定期维护。除了 React,它还支持 Angular,VueJS,NodeJS 和其他基于 Babel 的项目。
在 2022 年4月发布的 Jest 28中,增加了 --shard 命令条选项,可以把测试分片,并在不同的机器上运行。在测试条目很多的情况下,大大降低了跑测试所需要的时间。与此同时,这次更新还解决了 Jest 最大的一个兼容性问题,完全支持了 package.json exports 。这次更新还完成了对 Node.js globals 的支持,以后就不需要手动复制全局变量到测试中了。
② E2E测试:Cypress
Cypress 是一个使用 Javascript 编写,可在浏览器或 CI / CD 集成中进行运行的工具。 有丰富灵活的 API 和插件对各种场景的测试用例进行支持。Cypress 在进行 E2E 测试时,会打开 Chrome 浏览器,然后根据测试代码对页面进行操作,就像一个正常的用户在操作页面一样。
Cypress 12 于 2022 年 12 月发布,其中最重量级的新功能就是对跨源的支持。 现如今,大部分网页都是用第三方授权系统的方式进行登录验证。之前,Cypress 只能对使用程序化授权验证的方式进行登录测试,却不能完全模拟用户使用时的操作。现在,我们可以通过 cy.session() 存储用户授权后的 session 信息 (如 cookies,storage 等),并在之后的测试中随时调取。这也降低了测试用例运行的时间,因为不需要每个测试单独运行一次授权登录流程。
数据来源 npmtrends
(https://npmtrends.com/esbuild-vs-grunt-vs-gulp-vs-parcel-vs-rollup-vs-turbo-vs-vite-vs-webpack)
从上图可以看出,目前前端世界主流的前 3 名模块打包器仍然是 Webpack 、 Rollup 和 Esbuild 。
① Webpack
本质上,Webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 Webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph) ,然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
② Esbuild
Esbuild 是一个用 Go 编写的模块打包工具。因为它大量利用了 Go 中并行的特性,所以 Esbuild 非常快。
③ Rollup
Rollup 是一个 JavaScript 模块打包工具,可以将多个小的代码片段编译为完整的库和应用。与传统的 CommonJS 和 AMD 这一类非标准化的解决方案不同,Rollup 使用的是 ES6 版本 Javascript 中的模块标准。
④ Parcel
Parcel 是一个众所周知的零配置 Web 应用程序打包器。
⑤ Turbopack
Turbopack 建立在 Turbo 之上:Turbo 是一种用于 Rust 的开源增量记忆框架。 Turbo 可以缓存程序中任何函数的结果。当程序被执行多次时,函数不会重新运行,除非它们的输入发生变化。
这种方法使 Turbopack 在计算增量更新方面非常快。这也优化了 Turbopack 使得开发过程中增量更新非常快,确保 dev server 能够快速响应代码变更。
Turbopack 还使用请求级编译方法来只编译请求的代码。如果浏览器请求某些 CSS,则只会编译该 CSS,而不编译引用的 images。采用请求级编译有助于减少请求数量并能够快速编译它们。
目前,Turbopack 比现有打包工具快 10 倍~700 倍。但它目前只可以在 Next.js v13 中使用。未来计划发布独立的 CLI、插件 API,并支持 Svelte 和 Vue 等其他框架。
⑥ Nobundle:Vitejs
Vite 通过在一开始将应用中的模块区分为依赖和源码两类,改进了开发服务的启动时间。
对于生产环境,Vitejs 使用 Rollup 进行打包。
编译工具(Compiler)可以理解为将一种语言编写的源代码转换为另一种语言的过程。在前端开发过程中,我们经常遇到如下两类编译场景:
盘点当前前端界的编译工具,如下四个占据了大部分市场:
名称描述Star底层语言Babel (https://babeljs.io/)JS 编译器,用于将新版本 JS 语法转换为向后兼容的 JS 语法。也可以通过插件(
https://babeljs.io/docs/en/babel-plugin-transform-typescript)支持 TS 语法编译,但不支持类型检查。41.8KJSTSC (
https://www.typescriptlang.org/)TS 官方编译器,用于将 TS 编译成 JS。同时也支持旧版本语法转换,但不能自动 polyfill。87.5KTSSWC (https://swc.rs/)新一代 JS 编译器,特点是速度非常快,宣称单核下比 Bable 快 20 倍。支持 TS 编译,但不会做类型检查。25.5KRustesbuild (
https://esbuild.github.io/)一款超级快且融合编译&打包的工具,宣称比现在生态(说的就是你:webpack + babel)快10-100倍。支持 JS/TS,编译 TS 不会做类型检查。34.3KGo
可以看出,由于 SWC 和 esbuild 的出现,编译工具生态正在进行巨大的转变过程中,底层引入 Rust、Go 等更高效的语言,让编译速度有了几十倍乃至上百倍的提升。SWC 和 esbuild 虽然能高效的编译 TS,但是都不支持类型检查,TS 项目中还需要配置 TSC 来进行额外的类型检查支持,不过 SWC 已经有计划做些事情了。
从下面 Github 的点赞量趋势图中可以看出,新一代的编译工具正在猛追老牌工具,esbuild 和 SWC 在 2020 年之后都获得了大量的关注。
数据来源 star-history (http://star-history.com)
不过,关注归关注,从使用量来看,却是另一番景色,根据 NPM 包下载量趋势分析来看:
数据来源 npmtrends (https://npmtrends.com/)
如果你准备开发新项目或者升级工程框架,不妨尝试一下 esbuild 和 SWC,毕竟,谁不想要更快一些呢!
① Babel
最早开始出现 JS 编译工具之一,也是目前使用最多的 JS 编译工具。随着 Babel 的出现,前端开发者可以直接编写现代 JS 代码,同时又不失去对旧浏览器的支持。
简单来说,Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。Babel 可以做如下事情:
Babel 官方提供一个 在线编译网站(https://babeljs.io/repl),大家可以使用它便捷的体验 Babel 的编译效果。
Babel 构建在插件之上,默认情况下,Babel 不做任何处理,需要借助插件来完成语法的解析、转换、输出。
② TSC
TSC(TypeScript Compiler) 是 TS 语言官方的编译器,最初 TS 语言只能使用 TSC 进行编译,随着 Babel 等工具也相继支持编译 TS,你可能有疑问,他们之间有什么区别吗?答案是有的, 相对于 Babel,TSC 有如下 优势:
原因在于 Babel 是单个文件编译的,不会解析其他文件的信息,而 TSC 的类型检查需要拿到整个工程的类型信息,需要做类型的引入、多文件 namespace 的合并等。而 Babel 编译 TS 只是去掉类型声明,保留原有 JS 功能。
通过配置 target 也可以让 TSC 编译出低版本浏览器支持的代码,但存在一些 缺点:
综上,现在的前端 TS 项目一般还是会使用 Babel 做编译,使用 TSC 做类型检查。
Babel for transpiling, TSC for types :
(https://www.typescriptlang.org/docs/handbook/babel-with-typescript.html#handbook-content)
③ SWC
一款基于 Rust 语言开发的编译工具,它相对于 Babel 的优势就是编译速度非常快,在它的官网上,写着如下一句话。
SWC is 20x faster than Babel on a single thread and 70x faster on four cores.
SWC 的成功得益于 Rust 语言,这是一门高效、性能更好且内存安全的语言。随着 SWC 的崛起,很快它就被 Next.js 采用,进而取代 Babel 作为 JS 编译工具。
优缺点:
SWC 最大的优势就是编译速度快,另外在可扩展性、WASM 支持、社区和生态系统方面也是做的非常不错的。
但是,如果从 Babel 切换到 SWC,也有一些问题需要考量:
SWC 并不满足只是一个编译工具,未来或将支持如下能力:
④ esbuild
esbuild 更多是作为打包工具被大众熟知,我们在打包工具章节也做了介绍。
其实 esbuild 也是支持编译能力的,且速度要比 Babel 快很多,但是它不兼容低版本浏览器,产物无法降级到 ES6 以下。esbuild 也原生支持编译 TS 语言,同样不支持类型检查,遗憾的是官方也没有计划去做这件事。
数据来源 star-history (https://star-history.com/#sass/sass&postcss/postcss&tailwindcss/tailwindcss&
styled-components/styled-components&
webpack-contrib/css-loader&Date)
如何更优雅、更舒适的书写样式是前端领域永远绕不开的话题,为了解决原生 CSS 存在的一些问题,社区中出现了各式各样的样式工具。有覆盖范围比较小,但是解决了最关键问题的 CSS module;也有以 Sass 为代表的,中规中矩的 CSS 预处理器;还有通过插件化形式,具备非常强大的可拓展性的 PostCSS;以及剑走偏锋的 tailwind 和 styled-components。他们各有自己的优势劣势,也不存在一个最好的工具,作为前端工程师,只能根据具体场景,选择最适合的工具。
① 原生 CSS
从前,车马很慢,书信很远,样式开发也很简单。我们只需要将样式放在 CSS 文件中,并直接引入即可。然而简单也是需要代价的:
② CSS module
CSS Modules 是这样一种效果:CSS 文件中的所有类名都默认具有局部作用域。即使是一个普通的 CSS 文件,只要开发者通过某种方式实现了这种效果。也可以称之为“使用了 CSS module”。
显著的不同是:CSS 文件居然导出了一个变量(style)。并且,html标签上的类名,并不是普通的字符串,而是从style变量导出的同名属性。
最主流、应用最广也是最早产生的实现方式,就是通过构建工具来实现。比如 webpack 生态下的 css-loader。这些工具会为 CSS 文件中的每个类名,生成一个哈希值。并在打包产物中,将哈希值作为类名使用。并且打包工具会保证 ,即使是不同的 CSS 文件中,存在同名的类,生成的哈希值也是不同的。从而保证了局部作用域。
③ CSS 预处理器
CSS 预处理器可以说是众多样式工具中比较主流的一类,有很多广受欢迎的工具:Less、Sass 以及 Stylus。
CSS 预处理器是一种独有的语法来生成 CSS 代码的程序。也就是说,每种 CSS 预处理器,都会引入一种新的语言,开发者需要使用这种新的语言来编写源码。而预处理器会负责将源码编译成 CSS 代码。并且,虽然说会引入一种新的语言,但为了方便开发者使用,这种新的语言一般不会和 CSS 相差太多,基本上是 CSS 的一个超集。 从这个意义上来说,CSS 预处理器其实和 Typescript 有些相似之处。
这些超集内容提供了很多的语法,比如支持定义变量和嵌套写法等, 为我们的开发带来了很大的助力。
④ PostCSS
PostCSS 的名称可能会让人误解,认为这个工具是进行 CSS 后处理(对用户编写的标准 CSS 代码进行处理)的。从而与一些 CSS 预处理工具(比如上面提到的 Sass)区分开。实际上,PostCSS 依靠其简单的核心功能以及丰富的插件体系,能够同时完成 CSS 后处理,以及 CSS 预处理的工作。
PostCSS 的插件体系是极其丰富的,对于常见的需求,我们都能找到对应的插件。比如 Autoprefixer 是一个十分常用的 PostCSS 插件。它能够在输出的 CSS 中,对于兼容性有问题的 CSS 规则,自动加上不同浏览器的前缀。免去了开发人员手动为每一个规则加上不同前缀的繁琐而无趣的工作。
⑤ Tailwind
Tailwind和上面提到的工具,采取了完全不同的思路。因此也有了完全不同的架构。具体来说,tailwind 的思路是:它提供非常多的原子化 CSS 类。用户可以通过组合这些类来实现想要的样式。
总的来说 tailwind 的优点和缺点都十分突出。其缺点显而易见:大多数人都不熟悉这种方式,并且学习起来比较困难。要想熟练地使用 tailwind,需要对其提供的大多数常用的类都比较熟悉,看到某个组件的设计图,就能大概知道需要使用哪些原子化 CSS 类。而在达到这个程度之前,使用 tailwind 编写样式是一个比较痛苦的过程(写两行代码需要看十次文档)。
而优点可能不太容易察觉:
1.在大多数情况下,几乎不用自己编写一行 CSS 代码
如果开发者能够对 tailwind 比较熟悉,就能够使用它提供的原子化 CSS 类完成全部样式。
当我们需要对第三方组件库(比如 antD)的类添加一些样式时,我们就必须自己编写 CSS 代码了
2.极大的提高开发效率
之前需要编写多行 CSS 规则,现在只需要组合几个原子化 CSS 类
3.几乎不需要考虑原子化 CSS 类之间的冲突
原子化这个称呼就表明了,每个类只完成一个孤立的功能。所以不同类的 CSS 规则之间,不会有覆盖或重叠
4.以一种非常独特的方式,解决了全局作用域问题
如果我们完全使用 tailwind 来编程,我们会发现,我们几乎不需要定义 CSS 类。如此,既然不存在自定义的CSS 类,就根本不会存在 CSS 类污染了全局作用域的问题。
但也有例外,比如上文提到的“为第三方组件库的类添加一些样式”时,还是会产生 CSS 类名,从而产生了污染全局作用域的风险
⑥ Styled-components
Jeff Atwood 在2007年提出 Atwood 定律:任何能够用 JavaScript 实现的应用系统,最终都必将用 JavaScript 实现。大家或许能想到 JS 在服务器、客户端领域的大放异彩。但大家想不到的是,JS 竟然也想要将样式也纳入统治范围之内。而这个趋势的产物,就是 styled-components。
使用者完全不需要引入 CSS 文件或者类 CSS 文件,也完全不需要定义和使用CSS类名。而是需要将样式代码,放在一个个样式组件中。而样式组件如何定义呢?需要使用暴露出来的styled api,并且将样式代码放在模板字符串中。
值得一提的是 antd v5 开始使用了 css in js 方案,不过部分开发者认为 styled-components 这种“依赖模板字符串构建样式组件”的方式并没有给他们带来多大收益,但是它让样式组件, 以及样式组件和逻辑组件分离的概念普及给了更多人,这本身就是一件很好的事情。
微前端是一种类似于微服务的架构,是一种由独立交付的多个前端应用组成整体的架构风格,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的应用,而在用户看来仍然是内聚的单个产品。
通俗来说,就是在一个 web 应用中可以独立地运行另一个 web 应用,这样的使用场景其实不少:
一个完整的微前端框架应该具备哪些能力呢?
页面需要从一个子应用切换到另一个子应用,框架必须具备加载、渲染、切换的能力
子应用运行会污染全局的 window 对象,样式会污染其他应用,必须有效的隔离起来
激活子应用后,浏览器刷新、前进、后退子应用的路由都应该可以正常工作
应用间可以方便、快捷的通信
数据来源 star-history(http://star-history.com)
可以看出 single-spa 和 qiankun 是微前端方案的不二之选,无论是从其业界认可程度还是生态的活跃程度,都属于第一梯队。下面我们来介绍这些业界流行的方案:
① Single-spa
从名字上就可以推断出,Single-spa (
https://single-spa.js.org/docs/getting-started-overview/) 是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架,其主要实现思路:
Single-spa 是微前端领域的先行者,下面介绍的几个框架也是借鉴了它不少设计思路,但由于它本身没有处理 CSS 样式隔离或是 JS 运行时隔离,因此只适合单应用的场景。
② Qiankun
Qiankun (
https://qiankun.umijs.org/zh) 是一个基于 single-spa 的微前端实现库,但对 single-spa 方案中的痛点进行了完善,主要的完善点:
优点:
缺点:
③ Garfish
Garfish (
https://www.garfishjs.org/) 是字节跳动团队提出的一种微前端解决方案,其原理跟 qiankun 比较近似。
④ MicroApp
MicroApp (https://zeroing.jd.com/) 是京东开发的一种基于 WebComponent 进行渲染的微前端架构,它没有沿袭 single-spa 的思路,而是通过 CustomElement 结合自定义的 ShadowDom,将微前端封装成一个类 WebComponent 组件,从而实现微前端的组件化渲染。
优点:
MicroApp 的实现方式其实就是2个概念:HTML Entry 和类 webComponent
⑤ 无界
无界 (
https://wujie-micro.github.io/doc/) 则是腾讯推出的一款微前端解决方案。与众不同的是,它是一种基于 iframe 的全新微前端方案,无界团队希望借助 iframe 的优点,努力补足 iframe 的缺点,让 iframe 焕发新生。
优点:
缺点:
以上这些缺陷归根结底都是使用 iframe 带来的限制,无界团队逐一提出了解决方案:
针对弹窗适配问题,无界采用 webcomponent 来实现页面的样式隔离,子应用的实例在 iframe 中运行,DOM 在主应用容器下的 webcomponent 内,通过代理 iframe 的 document 到 webcomponent,可以实现两者的互联,从而使得iframe无法全局展示的问题。
针对路由状态丢失问题,无界提出了子应用“保活”模式。具体来说,当子应用发生切换,子应用的容器可以销毁,但 webcomponent 和 iframe 依然可以选择保留,这样等应用切换回来将 webcomponent 再挂载回容器上,使得其获得类似 vue 的 keep-alive 能力。
而针对通信难问题,无界提供了三种通信方式:props 注入机制、window.parent 通信机制和去中心化的 EventBus 通信机制。
⑥ 用或不用?
微前端的适用场景
微前端的劝退场景
微前端是前端发展的必然阶段。目前微前端框架也是属于百花齐放的阶段,基本上每一个大厂都有结合自己业务推出的方案,每个框架都有自己的优缺点,大家可以根据自己的项目去选择合适好用的即可。
但需要注意的是,微前端并非是最好的。他只是解决项目复杂性、代码检查及管控等问题的一种方式,对于架构设计的合理性提出了更高的挑战。
数据来源 katalon (https://katalon.com/resources-center/blog/ci-cd-tools)
“Quality at Speed” is the new norm in software development.
CI 持续集成(Continuous Integration) ,是我们的开发工程流里非常重要的一环。
在进行多人协作的日常工作中,为了保证整个团队的代码质量维持一个比较高的水平,我们往往会在 pre-commit 钩子里配置 lint 校验或者在 CI 中执行校验,两者的最大不同就是本地运行的 git hooks 可以被手动跳过(--no-verify 大法),且校验未通过内容只有当前分支的开发者本人可见,并伴随着较长的校验运行等待时间,无法在根本上保障代码质量。
同时我们也希望在合码到 master 之前执行一些单元测试和 E2E 测试,这种时候 CI 就成为了最佳选择。
CD 持续交付(Continuous Delivery), 为我们带来流畅的部署体验和更好的产品更新速度。
我们的代码在通过 CI 校验流程后,还需要一些相对重复和繁琐的环节来进行部署才能真正被用户访问到最新的内容,把这些重复的的工作用类似流水线的形式一键完成便是 CD 流程的 使命。
① 历史
如果我们把 CI & CD工具分为两代来对比,可以划分为:
第一代工具:Jenkins、TeamCity、Bamboo 等
第二代工具:CircleCI、GitLab CI 等
在早期,第一代工具的底层思路大多是使用一条流水线将编译、测试、部署等重要环节链接起来,并且这些环节的配置基本是通过 UI 交互来实现的。Jenkins 的灵活性和可扩展性使其一直享有着最流行和使用最广泛的 CI & CD 工具的地位,后续慢慢发展出来具有类似思路的 TeamCity、Bamboo 等工具。
而随着互联网技术的逐渐发展,开发者们慢慢注意到之前灵活的 Jenkins 在面对复杂,重复的多个项目时,相关配置的工作量非常大。由于 Jenkins 的相关配置文件 Jenkins file 需要开发者在Jenkins server 中进行配置,这往往带来较多的重复操作,所以我们需要一种更简单的、可复用的 CI & CD 工具来帮我们维护项目。
CircleCI、 GitLab CI 可以代表第二代工具的出现,它们可以将 CI 流程部署在云服务器上,其中 CI 相关运行的配置都基于特定格式的 YAML 文件。由于我们的 YAML 文件和主代码仓库存储在一起,我们不需要手动配置云服务器,CI 云服务器可以根据不同仓库的不同配置文件来做特定的处理动作。
② Jenkins
Jenkins (https://www.jenkins.io/) 是一个开源的免费 CI & CD 工具,它支持的平台非常多,可以在安装了Java Runtime Environment(JRE)的任何机器上独立安装,同时有非常丰富的插件生态可供开发者使用,具有自动化构建、测试和部署等功能。
③ GitLab CI
GitLab CI (
https://docs.gitlab.com/ee/ci/) 对于在 GitLab 代码托管平台上的仓库来说是最容易实现 CI 的方式,如果你想使用其他软件提供的 CI 能力,就必须注册对应平台的账号和下载相关应用。而 GitLab CI 内部集成好了这一切,如果你用 GitLab 的公用 CI 集群部署 CI 脚本,那基本算是开箱即用,使用成本很低。
④ Github Actions
Github Actions 诞生于 2018 年,与诞生于 2004 年的行业大前辈 Jenkins 相差 14 年。
其中 Actions 可以算是 Github Actions 最有特色的功能,Github 所有的 Actions 都可以在 (
https://github.com/actions) 找到。
Actions 可以被理解为 CI 流程中可复用的 原子化 step 结点。 试想,如果我们想做实现一个代码行数检查的 CI 节点,整个脚本的设计大抵会很顺利,但是这种基础的功能可能有很多场景需要,其他业务线也有类似的能力想要实现,没有 Actions 的话只能做机械的脚本代码拷贝工作。
在使用 Github 的 CI 能力后,我们就可以把想要封装的能力设计为一个 Actions,发布后其他人也可以使用同一个 Actions 而避免重复开发。
我们可以把 Actions 抽象理解为一个活跃在 CI 流水线上的云函数,我们设定好固定的输入输出和执行环境后,用户只需要把云函数需要的参数传递过去,在脚本正常执行的情况下,云函数就能生成预期的计算结果。
⑤ 选型的考虑点
在 CI 工具选型时,云服务 VS 自建服务是一个永远也逃不过的话题,云服务的简单便捷有目共睹,自建服务往往对开发者的 docker 使用能力也有一定的要求,但是云服务就一定好于自建服务吗?答案也不是如此,当我们的的 CI 节点运行出现预期之外的故障时,自建服务可以很方便的通过 SSH 进入内部进行调试和检查,而云服务就很难进行问题定位。
但是不论是 Jenkins 丰富的插件生态还是 Github Actions 多样的 Actions 能力,功能 原子化 ,细粒度化和能力集成的复用性都是非常有意义的探索命题,它们为每一个开发者都带来很大的便利,也是跳脱出 CI & CD 领域也值得其他方向共同研究的软件工程演进方向。
除了主流的前端研发领域相关知识,还有一些衍生领域的前沿概念是我们不得不去探索学习的,下面就带大家深入浅出的了解一下这些能拓宽我们技术知识面的内容吧!
① Web 3.0
区块链
区块链是在计算机网络的节点之间共享的分布式数据库或分类帐。区块链以组(称为块 [
https://www.investopedia.com/terms/b/block-bitcoin-block.asp] ) 的形式收集信息,这些组包含信息集。块具有一定的存储容量,并且在填充时关闭并链接到先前填充的块,形成称为区块链的数据链。新添加的块之后的所有新信息都被编译成一个新形成的块,一旦填充,该块也将被添加到链中。区块链的创新之处在于它实现了数据记录的保真度和安全性,并在不需要接受第三方管理。
DeFi: Decentralized Finance - 去中心化金融
DeFi 旨在彻底改变金融部门,消除对银行、支付处理商和其他中介机构等中央机构的需求。取而代之的是一个基于区块链的点对点金融系统。
NFTs: Non-Fungible Tokens - 非同质化代币
NFT 是一类存在于 区块链 上的数字资产。
每个 NFT 都是唯一的(不可替代的),并且没有两个 NFT 是相同的。这与可替代的美元形成对比:一美元与任何其他一美元是完全相同的。
DAOs: Decentralized Autonomous Organization - 去中心化自治组织
DAO 是一个为了共同目的而形成的团体,其规则、计划和目标都编码在区块链上。在 DAO 中,每个人都可以发布提议并进行投票来做决策。
加密货币
通过 Web 3.0 应用程序(如比特币、以太坊等加密货币)正在创建一个新的货币世界,力求与传统的法定现金世界区分开来。
② 跨端
众所周知,很多平台都采用了跨端开发(跨平台开发)的方式。相对于原生开发,跨端开发有开发成本低、开发周期短、开发难度小等诸多优点。
那么,如何进行是跨端开发?
H5 Hybrid 方案
简单来说就是用网页来跨端。从开发成本低、标准统一、生态繁荣上来说,H5 Hybrid 方案优秀。但这种方案的劣势也非常明显,就是性能和体验存在显著的差距,同时 Web 的生态繁荣来自于其良好的历史兼容性,也意味着沉重的历史包袱。
React-Native/Weex 类方案
React-Native/Weex 这类方案通过尽可能的取长补短,综合了 Web 生态和 Native 组件,让 JS 执行代码后用 Native 的组件进行渲染,以解决抛弃 Web 历史包袱的问题。
方案同样存在一些缺陷:iOS/Android 双端本身不一致的组件和布局机制,让双端一致性难以得到保障。另外,这套方案也需要非常高的维护支持成本。
Flutter 方案
Flutter 不继续在 Web 生态上借力,从设计之初也并没有把 Web 生态考虑进去。相比于 RN 依赖 Native View 渲染,Flutter则是自绘组件,通过 Skia 绘制到屏幕上。
由于可以完全发挥 GPU 的能力,也不需要去 Native 绕一圈。Flutter 理论上能做到更好的性能和两端一致性,这一意味着理论上未来可能基于 Flutter 的 JS 动态化方案能够在样式上支持的比 Weex 更好。
目前最主要的问题是 Flutter for Web 从技术原理上来说离生产可用可能还非常遥远,动态化能力的确实也会让部分场景不适用。
小程序运行时方案
这个方案可以说是目前性价比最高的方案。
应用体验方面,小程序技术是前端容器技术的一种应用,其组件及UI都有明确的规范,开发者不用考虑兼容性及类似 H5 开发时复杂工具及框架的选择。同时,由于组件及 UI 都是预设的,展示体验也会更佳。
应用框架支持方面,某些运行时方案不仅支持纯 wxml 微信小程序运行,还支持包括 uniapp、 Taro、kbone 等第三方框架集成的小程序。
宿主环境结合方面,小程序是基于 App 端实现的应用,其获取系统(App)的权限也会多于 H5;随着微信小程序的潮流引领,各大主流互联网平台的追随,小程序技术的发展已经趋于成熟,市面上小程序运行时已经开始出现多智能终端设备的适配(基于 Andriod 系统的多终端屏幕适配)。
③ WebAssembly
优势
Javascript 的运行步骤
WebAssembly 的运行步骤
劣势
④ Web 3D
数据来源 star-history (http://star-history.com)
Web3D 是在Web浏览器实现 3D 模型绘制的一系列技术的统称,用来丰富网页用户的使用体验。目前 Web3D 技术已经在许多场景起着无可替代的作用,利用 3D 模型实现商品、房间的展示,既可以给客户带来身临其境的参观体验,还可以提供他们丰富的交互效果;将 3D 游戏、虚拟社交软件移植到网页上,由于无需本地安装客户端,有助于吸纳大量的用户;在地图和导航中引入实物模型,可以使用户直观地识别地理信息;在医疗和教育培训中引入网页 3D 教具,可以用于科普和仿真实验。未来随着 XR 设备的推广,Web3D 技术也将有更丰富的使用体验,产生更多元的交互场景。
OpenGL
是计算机图形编程的接口标准,为了将图形编程引入浏览器,OpenGL 分化出了一个特殊版本 OpenGL ES ,WebGL则是将 OpenGL ES 以 JavaScript 接口的形式暴露给用户使用。图形编程有一个重要的概念叫做“着色器”,在 OpenGL 里由 GLSL 实现,它是一种编译成 GPU 汇编程序以提供图形硬件运行的高级语言,在 WebGL 中,我们同样可以编写 GLSL 来实现着色器,最终将通过对应的 JavaScript 接口将其编译成二进制从而运行在浏览器上。
X3D
是一种声明式编程范式的用于表示 3D 模型的 ISO 标准,其前身是 VRML ,它可以提供用户以 xml 或 json 的形式渲染出 3D 图形,从而避免图形渲染中复杂的计算和实现。目前 HTML 标准暂时不直接支持此类技术,不过已经有一些 JavaScript 库实现了这个标准,例如 X3DOM 和 X_ite 。以 X3DOM 为例,由于浏览器不直接原生支持 X3D 特殊标签的渲染,X3DOM 底层便使用 JavaScript 通过 DOM 操作识别用户声明的 X3D 标签,并利用 WebGL 进行图形渲染。
ThreeJS
也是一套基于 WebGL 的技术封装,整合了 Audio 等 API ,并且抽象出场景、材质、动画、摄像机等应用层概念的 Web3D 引擎,与其类似的引擎工具有 BabylonJS 、SceneJS 等,它们在各类场景下各有千秋,需要使用者根据自己的需求选择合适的引擎进行开发。
⑤ Serverless
Serverless 这个词可以分为 server 和 less 两部分。server自然就是服务器,less 则是使减少的意思。但组合起来,Serverless 并不是指不需要服务器,而是开发者不需要关心服务器的运维的一种架构。
与之相对的,是 serverful 架构(这并不是一个被广泛使用的术语)。与 Serverless 架构相反,serverful 架构要求开发者细致地关心服务的运维。
Serverless 只是一种具有以上特点的架构,但是开发者实际使用的 Serverless 平台可能有很多种。其中具有代表性的两种是:BaaS 以及 FaaS:
BaaS
Backend as a Service, 后端即服务指的是开发者不再编写所有的后端逻辑,而是完全使用服务商提供的。这部分逻辑通常包含:数据库服务、登录注册服务、消息推送服务等。所以如果用一个词来代表 BaaS,可以使用“外包”。它代表了,软件开发团队将一些后端逻辑完全外包给 BaaS 平台,而不需要自己编写。
FaaS
Function as a Service,函数即服务指的是开发者以函数的形式编写、组织后端逻辑,并将其上传到 FaaS 平台上。而这些函数会在什么情况下被触发,则是开发者需要关心的另一件事情。所以,我们可以用“函数”这个词来代表 FaaS。开发者只需要将逻辑封装在一个个函数中,而不需要关心如何管理扩缩容以及可用性。
⑥ ChatGPT
ChatGPT 是 OpenAI 12 月 1 日官宣推出的一款对话式大规模语言模型,以对话的方式进行交互,采用基于 GPT-3.5 架构。
数据来源 ChatGPT-真格基金分享 (
https://daimajia.com/2022/12/05/chatgpt-zhenfund)
如果你尝试一下与它对话,你会发现它什么都能回答,而且能连续有理有据的回答,很难相信他是一个AI,反而更像是一个人工助理。
我们或许能用它?
看看大家怎么说
技术的进步往往会带来生产力的进一步解放。对于每一个人来说 ,如何用好更新颖的工具,都是眼下需要思考的重点。
小小预言一下,在 2023 这些方向可能会给人带来不小的惊喜,同时存在很大的探索价值:
看到这里,你一定收获了不少新知识吧,但这仍然是前端领域大潮中的冰山一角。不过也不必“哀吾生之须臾,羡长江之无穷”,作为一名信息行业的一朵小浪花,我们永远在路上,永远有无限的角度值得我们去挖掘学习。与其感叹知海浩瀚无从下手,不如争取每天都进步一点点,多了解一个新概念,在 2023 成为更好的自己!
RSC:
编译工具:
测试工具:
WebAssembly: