致我们学前端的小时光—corejs与env、runtime的不解之缘
栏目: JavaScript · 发布时间: 5年前
内容简介:最近在看致我们暖暖的小时光,有点不可自拔。文章具有翻译向,具体的可以看文末的链接。随着ES6的正式发布,以及ES2016、ES2017...每年的稳定更新,还有新提案的不断出现,使得JavaScript越来越成熟。但是对于一些新的语法和API,老版本的浏览器无法全面兼容。babel的出现,解决了在老版本浏览器使用新语法的问题,它现在不仅仅是一个ES6 to ES5单纯的语法转换工具,更是一个规范和生态,帮我们去更高效的处理JavaScript。
前言
最近在看致我们暖暖的小时光,有点不可自拔。
文章具有翻译向,具体的可以看文末的链接。
随着ES6的正式发布,以及ES2016、ES2017...每年的稳定更新,还有新提案的不断出现,使得JavaScript越来越成熟。但是对于一些新的语法和API,老版本的浏览器无法全面兼容。babel的出现,解决了在老版本浏览器使用新语法的问题,它现在不仅仅是一个ES6 to ES5单纯的语法转换工具,更是一个规范和生态,帮我们去更高效的处理JavaScript。
@babel/preset-env登场
@babel/preset-env是作为babel-preset-es2015的替代品出现的,主要的作用是用来转换那些已经被正式纳入TC39中的语法。所以它无法对那些还在提案中的语法进行处理,对于处在stage中的语法,需要安装对应的plugin进行处理。
除了语法转换,@babel/preset-env另一个重要的功能是对polyfill的处理。新加入标准库的,可能是一些语法特性,比如箭头函数等,还有可能是一些新的API,比如promise、set、inclues等。
对于语法,babel可以通过生成静态语法树,去做一些转换,生成对应的ES5的代码。
但是对于新的API,需要浏览器去原生支持,或者使用大量的代码去进行API的模拟。@babel/polyfill就是API的垫片,通过引入这些垫片,使得低版本的浏览器能模拟实现那些新的API。
而今天的主角core-js就是@babel/polyfill的核心依赖,它现在已经发布了3.0的版本,而且@babel/preset-env在7.4.0的版本已经支持这个最新的版本。大版本的升级,会带来一些破坏性,但是相应的也会带来很多优势。
core-js 被遗忘的包
core-js是什么
- 它是JavaScript标准库的polyfill
- 它尽可能的进行模块化,让你能选择你需要的功能
- 它可以不污染全局空间
- 它和babel高度集成,可以对core-js的引入进行最大程度的优化
core-js升级的动机
- core-js中的破坏性变更只能在主版本的升级中进行
- core-js@2.0的版本已经在一年半之前冻结,所有的新特性只会添加到3.0的分支中
core-js@3的重要改变
- 对于ECMAScript中已经稳定的功能,core-js已经几乎完全支持,并在core-js@3中引入了一些新的功能
- 对于一些已经加入到ES2016-ES2019中的提案,现在已经被标记为稳定功能
- 增加了proposals配置项,对处在提案阶段的api提供支持,但是因为提案阶段并不稳定,在正式加入标准之前,可能会有大的改动,需要谨慎使用;对于一些改变巨大的提案,也进行了对应的更新
- 增加了对一些web标准的支持,比如URL 和 URLSearchParams
- 删除了一些过时的特性
monorepos 包的拆分
core-js@2一个最常见的问题就是包的体积太大(~2M),并且有很多重复的文件被引用。基于这个原因,core-js@3对包进行拆分,三个核心的包分别是
- core-js:定义全局的polyfill(~500k, 40k minified and gzipped)
- core-js-pure:提供不污染全局环境的polyfill,等价于core-js@2/library(~440k)
- core-js-compat :包含了core-js模块和API必要的数据,通过browserslist来生成所需要的core-js模块的列表
在以前的版本中,已进入ECMAScript标准的特性用 es6.
的前缀来表示,提案阶段的特性用 es7.
的前缀来表示,选择这个前缀的原因是在2014年的时候ES6以后的所有特性都考虑使用ES7来进行命名。
在cores-js@3的版本中,所以规范中的特性都使用 es.
这个前缀,而提案中的特性使用 esnext.
这个前缀。
几乎所有的CommonJS的入口文件都已经发生改变。在core-js@3中,包含了更多的模块入口。这使得对于目标浏览器的按需支持更加的具有灵活性,同时可以带来文件大小方面的优化。
在core-js@2中,@babel/preset-evn在插件内部有一个data-table,维护了不同浏览器对于特定API的支持,通过这个data-table来实现不同targets按需加载所需要的core-js模块。由于这个compat-table存在一些固有的问题,作者重新维护了一个包,即core-js-compat,用来提供不用目标引擎所需要的core-js的模块信息。
const { list, // array of required modules targets, // object with targets for each module } = require('core-js-compat')({ targets: '> 2.5%', // browserslist query filter: 'es.', // optional filter - string-prefix, regexp or list of modules }); console.log(targets); /* => { 'es.symbol.description': { ios: '12.0-12.1' }, 'es.array.reverse': { ios: '12.0-12.1' }, 'es.string.replace': { firefox: '63', ios: '12.0-12.1' }, 'es.string.trim': { ios: '12.0-12.1' }, 'es.promise': { firefox: '63' }, 'es.promise.finally': { firefox: '63' }, 'es.array-buffer.slice': { ios: '12.0-12.1' }, 'es.typed-array.int8-array': { ios: '12.0-12.1' }, 'es.typed-array.uint8-array': { ios: '12.0-12.1' }, 'es.typed-array.uint8-clamped-array': { ios: '12.0-12.1' }, 'es.typed-array.int16-array': { ios: '12.0-12.1' }, 'es.typed-array.uint16-array': { ios: '12.0-12.1' }, 'es.typed-array.int32-array': { ios: '12.0-12.1' }, 'es.typed-array.uint32-array': { ios: '12.0-12.1' }, 'es.typed-array.float32-array': { ios: '12.0-12.1' }, 'es.typed-array.float64-array': { ios: '12.0-12.1' }, 'es.typed-array.from': { ios: '12.0-12.1' }, 'es.typed-array.of': { ios: '12.0-12.1' } } */ 复制代码
对于core-js@3新的入口文件,下面有一些简单的例子
// polyfill all `core-js` features: import "core-js"; // polyfill only stable `core-js` features - ES and web standards: import "core-js/stable"; // polyfill only stable ES features: import "core-js/es"; // if you want to polyfill `Set`: // all `Set`-related features, with ES proposals: import "core-js/features/set"; // stable required for `Set` ES features and features from web standards // (DOM collections iterator in this case): import "core-js/stable/set"; // only stable ES features required for `Set`: import "core-js/es/set"; // the same without global namespace pollution: import Set from "core-js-pure/features/set"; import Set from "core-js-pure/stable/set"; import Set from "core-js-pure/es/set"; // if you want to polyfill just required methods: import "core-js/features/set/intersection"; import "core-js/stable/queue-microtask"; import "core-js/es/array/from"; // polyfill reflect metadata proposal: import "core-js/proposals/reflect-metadata"; // polyfill all stage 2+ proposals: import "core-js/stage/2"; 复制代码
core-js@3 与 babel
如上面提到的,core-js与babel是高度集成的,babel的集成给core-js的按需加载提供了可能。在babel7.4.0的版本中已经支持core-js@3的版本。
@babel/prest-env
在升级到7.4.0以上的版本以后,既支持core-js@2,也支持core-js@3。所以增加了 corejs
的配置,来控制所需的版本,默认是core-js@2并且会有文字输出提示升级到3的版本。
@babel/prest-env可以通过配置useBuiltIns来根据targets加载@babel/polyfill。
@babel/polyfill的改动
@babel/polyfill是一个简单的包,包含core-js和regenerator-runtime这两个包。当core-js升级到3.0的版本后,将放弃使用@babel/polyfill,因为它只包含core-js 2.0的版本。
所以在@babel/prest-env升级到7.4.0并且使用core-js@3,需要做如下的替换工作
// 安装core-js@3.0 和 regenerator-runtime yarn add core-js@3 yarn add regenerator-runtime // babel.config.js presets: [ ["@babel/preset-env", { useBuiltIns: "entry", // or "usage" corejs: 3, }] ] // 入口文件index.js // before import "@babel/polyfill"; // after import "core-js/stable"; import "regenerator-runtime/runtime"; 复制代码
@babel/runtime
当使用core-js@3的时候,@babel/transform-runtime会从core-js-pure这个包里去加载对应的polyfill代码,core-js-pure里面的代码不会污染全局变量,适合第三方库的开发。
在@babel/transform-runtime的最新版本中,已经支持core-js@3,需作如下操作。
yarn remove @babel/runtime-corejs2 yarn add @babel/runtime-corejs3 //babel.config.js plugins: [ ["@babel/transform-runtime", { corejs: 3, }] ] 复制代码
改变一
在之前的版本中,@babel/runtime最大的问题就是无法模拟实例上的方法,比如数组的includes方法就无法被polyfill。
但是在core-js@3的版本中,所有的示例方法都可以被polyfill了。
array.includes(something) ↓ ↓ ↓ ↓ ↓ ↓ import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes"; _includesInstanceProperty(array).call(array, something); 复制代码
改变二
core-js@3支持对ECMAScript提案的API进行模拟。
@babel/plugin-transform-runtime的默认配置中,是不会注入对提案的polyfill代码。如果想要支持提案中的API,只需要增加和@babel/preset-env类似的配置项。
plugins: [ ["@babel/transform-runtime", { corejs: { version: 3, proposals: true }, }] ] 复制代码
new Set([1, 2, 3, 2, 1]); string.matchAll(/something/g); ↓ ↓ ↓ ↓ ↓ ↓ // without proposals flag import _Set from "@babel/runtime-corejs3/core-js-stable/set"; new _Set([1, 2, 3, 2, 1]); string.matchAll(/something/g); // with proposals: true import _Set from "@babel/runtime-corejs3/core-js/set"; import _matchAllInstanceProperty from "@babel/runtime-corejs3/core-js/instance/match-all"; new _Set([1, 2, 3, 2, 1]); _matchAllInstanceProperty(string).call(string, /something/g); 复制代码
展望未来
对老版本浏览器的支持
core-js支持尽量多的浏览器和平台,甚至是IE8-和一些老版本的Firefox浏览器。但是支持如此多的低版本浏览器,必然会造成polyfill文件变大,增大包的体积。
最大的问题主要来自于一些只支持ES3的浏览器,比如IE8-。大多数ES的新特性,都是基于ES5的语法去实现的,这就导致为了使低版本的浏览器能够支持这些新的特性,需要用大量的填充代码去抹平ES5与ES3的差异。
虽然在某些地区IE8还是非常流行,但是为了语言的发展和进步,应该允许某些浏览器退出历史的舞台。core-js@3已经放弃支持IE6,在下个大版本中,core-js@将不再支持IE8,只支持那些基于ES5语法的浏览器。
ECMAScript 模块
core-js的模块都是基于CommonJS规范的。随着ECMAScript模块的发布和普及,core-js应该提供一个ECMAScript模块规范的版本以供选择。
更好的优化polyfill的加载问题
在使用@babel/preset-env的 useBuiltIns:usage
这个配置项是,还是会存在一些问题。比如当项目的文件无法进行静态分析时,需要提供一种方案来进行polyfill的加载。另一个问题是 useBuiltIns:usage
可能会在一个文件头注入数十个core-js的导入语句。当项目中有几百上千个文件的时候,这些注入的语法会占据数量客观的体积。我们需要一个机制来收集所有需要用到的模块,并进行去重操作,最后统一注入到项目里。
对于那些需要支持低版本浏览器的开发人员来说,为了支持IE11这种浏览器,polyfill文件的大小会急剧膨胀。一种解决方案是使用type = module / nomodules属性,生成两个不同的包,一个用来支持现代浏览器,一个用来支持低版本的浏览器,但这并不是一个完美的解决方案;另一种解决方式是提供一个polyfill的服务,根据请求中的UA来判断浏览器的型号,返回这个浏览器需要的polyfill文件, 类似的服务有polyfill.io 。但是polyfill.io的返回并不准确,可用性不是很高。
others
- 增加对web标准的支持,比如fetch
- @babel/runtime提供对目标环境的支持,类似@babel/preset-env中targets字段
链接
core-js@3, babel and a look into the future
@babel/prest-env 7.4.0 Released: core-js 3, static private methods and partial application
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 震惊,高中少年与 CDN 的不解之缘
- 前端时光机(经典技能)
- 关于beego-httpbib库的不解问题,请求大家支援
- 假如时光倒流,我要这样学编程
- 集8年之大成,这本书与经典畅销书有着不解之缘
- 带时光机的 Kubernetes Dashboard:Kubevious
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。