Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端组件库构建的权衡取舍 #12

Open
winixt opened this issue Jun 20, 2021 · 2 comments
Open

前端组件库构建的权衡取舍 #12

winixt opened this issue Jun 20, 2021 · 2 comments

Comments

@winixt
Copy link
Owner

winixt commented Jun 20, 2021

预备知识1:为什么要构建?

如果直接发布 .vue 或者 .jsx 文件,用户使用的时候需要跟着源码编译一遍,影响用户的开发以及发布的构建速度。但是不构建也有不构建的好处,看完下文关于构建的权衡取舍就明白了。

预备知识2: core-js 有哪几种用法?

core-js 有两个包,分别是 : core-jscore-js-purecore-js 以全局的方式引入 polyfill,如:import "core-js/stable/set";;而 core-js-pure 引入 polyfill 时不会污染全局,如:import Set from "core-js-pure/stable/set";

预备知识3:和 @babel/transform-runtime 都可以处理 core-js,有什么区别?

假设我们有这段代码需要编译:

export const p = new Promise();

使用 @babel/preset-env 处理 core-js,配置如下:

["@babel/preset-env", {
      useBuiltIns: "usage",
      corejs: 3
 }]

编译结果:

import 'core-js/modules/es.object.to-string.js';
import 'core-js/modules/es.promise.js';

var p = new Promise();

export { p };

使用 @babel/transform-runtime 处理 core-js,配置如下:

["@babel/plugin-transform-runtime", {
        corejs: 3
}]

编译结果:

import _Promise from '@babel/runtime-corejs3/core-js-stable/promise';

var p = new _Promise();

export { p };

从上述两个编译结果可以看出 @babel/preset-env 处理 core-js 采用的是污染全局的方式,而 @babel/transform-runtime 采用的是 core-js-pure 不会污染全局变量,两者不能建议同时使用,可能引发不可预知的问题。其中还有一个很重要的区别是:@babel/transform-runtime 不认 @babel/preset-envtargets 配置,不管设置的浏览器版本有多高,都会引入 polyfill没,还是上面的代码,修改配置如下:

{
       presets: [["@babel/preset-env", {
               useBuiltIns: false,
                    // corejs: 3
                    targets: {
                        browsers: [
                            'chrome >= 91'
                        ]
               },
      }]],
      plugins: [
            ["@babel/plugin-transform-runtime", {
                    corejs: 3
            }]
      ]
}

编译的结果还是会带 polyfill

import _Promise from '@babel/runtime-corejs3/core-js-stable/promise';

const p = new _Promise();

export { p };

预备知识4:如果使用 Babel 处理 js,库和应用的构建分别推荐什么样的配置?

APP: 仅仅使用 @babel/transform-runtime 处理 helpers,对于 core-js,因为不清楚有哪些第三方依赖包需要并且没有被引入的 polyfill,有两种选择:1)不介意最终包的大小:直接配置 useBuiltIns: "entry",根据 targets 全量引入 polyfill;2)介意包的大小,不介意生产构建的速度,配置 useBuiltIns: "usage",对所有依赖包用 babel 过一遍(如果不对所有依赖包处理,有可能会漏掉依赖包中需要 polyfill),根据targets按需引入 polyfill

Library: 配置useBuiltIns: false,避免全局的 polyfill 对应用造成影响,使用 @babel/transform-runtime 处理 helpers。对于 polyfill 有两种选择:1)不处理,交给使用方自行处理。2)使用 @babel/transform-runtime 处理 core-js,因为不能指定targets 会使最终构建的包比较大。对于这个问题,Babel 官方有还在实验阶段的方案 babel-polyfill

预备知识5:都 2021 了,是否能重新思考构建?我们是否还有必要把代码编译到 es5 ?

es6 2015年出道,现代浏览器在 2016-2017 年基本已支持:
Browser Support for ES6 (2015)

can i use 中也可以查到 es6 的支持度其实很高了:
Can luse

vue3 从高调宣称不支持IE11,到迷之激进构建,tsconfig.json直接上 esnext,直呼:好家伙。(花了69块钱给尤大发个私信咨询,没得到回复)

vue3 版本的 element-plus 也是直接上 es6,如果要用 vue3,支持的浏览器其实也老不到哪去,太老跑不了 Proxy

预备知识6:如何将 js 代码编译到 es6?

这时 Babel 就派不上大用场了,因为 Babeltargets 不能配置指定某个 es 版本,翻了下 github 的 issues,官方说:用户指定 targets 会是比较好的选择, 因为浏览器可能支持了大部分 es6 语法(不是全部)和部分 es7 语法,此时设置 targets 是更合理的。

可选的方案:
1)大名鼎鼎的 esbuild,现在已经有部分应用在生产使用了,element-plus的组件有一种构建类型也是用 esbuild,但是截至目前(2021年6月20日)作者依然不建议直接在生产使用。

2)小众一点点 swc,用 Rust 实现,主打也是快,但是有些语法不支持传送门,用的人也比较少。

3) 使用更大名鼎鼎的 tsc,因为 tsjs 的超集,所以是可以用 ts 的编译器直接编译 js 的,真香。

�开始组件库的构建

前面铺垫了这么多,大家因该知道怎么做的,直接进入 FAQ【逃】

FAQ1: 如何实现组件库按需记载?

用 webpack 或者 Rollup 将一个个的 .css .less .js .jsx .vue 编译成 .css .js。如非特殊需求,不建议自己封装 @vue/compiler-sfc,SFC 的写法太多了,templaterendersetupcss var。一步小心可能会踩坑。

FAQ2: 业务组件库构建需要特别注意什么?

业务组件库通常不太需要换肤等各种场景,但是需要避免避免全局样式污染。如果不是非常大的团队,交给应用构建其实也是没舍恩么问题的。要构建的尽量做到简单的按需加载,不需要使用 babel-plugin-import 啥的,使用插件实现 css 的反向注入,如:
image

之前写了 Rollup 插件,有需要的话后续发布出来(挺简单的,自己实现一个也不难)。

FAQ3:你们使用的什么构建方案?

我们构建的是 PC 端的 vue3 组件库,跟着尤大走,最终构建的目标版本是 es6,编译器用是 tsc,构建工具 Rollup

@winixt
Copy link
Owner Author

winixt commented Dec 1, 2021

将 esnext 编译到 es6,编译工具 esbuildtscswc 都不会处理 js api,只编译语法,根据作者们的描述,polyfill 不属于编译的范畴,可以通过其他方式实现。所以"使用这些工具依旧得考虑 polyfill 的问题"。

@winixt
Copy link
Owner Author

winixt commented Dec 1, 2021

顺带补几个点:

  • tsc 编译是真的慢~,为什么慢?还没研究清楚
  • 关于库的构建,建议不处理 polyfill,有以下几个理由
  1. 对于库的构建,目前没有特别优雅的方式处理 polyfill,要么存在全局污染问题,要么接受比较大的构建体积
  2. 如果所有的库都处理 polyfill,而技术体系又不一样,不能通过 tree-shaking 过滤,那么将会导致应用的构建体积非常大
  3. 库的使用者,比开发者更清楚应该兼容什么环境,用什么 polyfill
  4. Advice for library authors who use polyfills? w3ctag/polyfills#6 这里有一些大佬的讨论
  • 关于库编译的 helper 函数,例如 @babel/helpers、tslib、@swc/helpers 等,一个应用使用同一个生态的库,对于缩小构建体积有一丢丢好处,可以共用很多的 helper

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant