babel 每次学习都有新的理解,哪怕是其配置都与咱们前端生态中的各类概念息息相关。近期再次复习babel知识从而更好的编写js类库,本文是学习过程所作的记录。前端
那么,咱们使用 webpack 来打包js代码时,是使用的什么呢?答案是 babel-loader。node
babel-lodaer 是适配 webpack 的一个模块,其内部就是调起 babel/core 来实现代码转译。webpack
记住:babel自身惟一能作的事情叫:代码转译。
至于新增api那些,那是须要引入polyfill垫片的事情(下文会讲)。es6
babel自己所作的事情,通常都叫他代码转译。那么所谓的转译都有哪些种类呢?以下:web
一、单纯的语法解析和转换。例如箭头函数转成function
二、须要一点辅助的语法解析和转换,例如 class语法,给你转成prototype原型语法chrome
解释下第二点:咱们知道,一种新的语法意味着新的一些关键字和符号等,那么对于一些特殊符号如箭头函数,那么他刚好有es5对应的 function
关键字,因此babel只需简单换一下字符。npm
然而有些却不是简单的换一下,例如 class 语法,babel给你转成 es5 以后,他必须改形成函数和prototype的写法,在这种状况下,babel编译后的代码中会加入一些辅助函数(也就是下文说的babel/runtime所干的事儿)以协助来用es5的方式实现 class。api
举个栗子:promise
看到了吗,babel生成的原型式写法中,须要在构造函数中对调用者的实例化方式进行检测,这些都封装成了 helper函数 _classCallCheck。浏览器
除此以外,还有更复杂的场景 babel转译以后甚至须要依赖polyfill垫片:例如 async 转换。 babel 会把 async 语法代码转成 generator 辅助函数,而 generator 辅助函数的功能须要依赖 regenerator-runtime
这个polyfill垫片。以下图,babel给你把async语法转换后,多了不少辅助函数;甚至其中有一个 regeneratorRuntime 的函数是没有找到定义的,而这个函数其实就是依赖全局须要引入 regenerator-runtime
这个 polyfill 才行。
首先,咱们要理解什么是 polyfill 什么是辅助函数。
像上文所说的2种babel转译,其中转换以后所出现的那些辅助函数,叫作运行时所须要的helper,他们其实就是 runtime。 而这些运行时函数,有一个单独的 npm 包去实现他们,叫作 babel/runtime。
而像那些 es6 新增的 API,例如 Promise、Map、WeakMap。数据新方法 flat、includes 等等 api,这些东西不属于 babel语法编译的范畴,是属于一些新的api。这些叫作 垫片,英文名叫 polyfill。在社区里,polyfill垫片经过另外2个npm包来实现:一个叫作 corejs(用来实现除了generator以外的垫片,如今要使用他的版本3),另外一个叫作 regenerator-runtime(用来实现generator垫片)。
其实最佳实践的前提就是正确的理解上文中两个概念。从而正确的使用 babel/runtime 和 corejs3.
若是比喻成作饭,babel/runtime 和 corejs3 就是咱们的食材。有了食材,咱们就要用一个配套的刀具来加工他们。
咱们接下来所讲的实践,其背景是在 webpack + babel 下的实践。由于,毕竟咱们一般不会单据来用 babel 操做一个 js 文件。
这种场景的要求是:
解决方法:
一、 为了能按需转译语法。在最新babel7以后,咱们只需使用preset/env预设就很简单了。
// babel.config.js module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": false, // polyfill配置先关掉,后面再讲 } ] ] }
咱们能够经过修改 targets ,来决定babel编译哪些语法。例如你把chrome版本号调到最新1个版本,那么箭头函数必然是不会转换的。
二、 三、按需加载polyfill
按需加载polyfill有2种方式,一种是把 useBuiltIns配置成 entry,另外一种是配置成 usage.
使用上的区别是:
以下是usage的方式:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": 'usage', "corejs": 3 // 写死就好!当前阶段就该用3 } ] ] }
以下是entry方式
//babel.config.js module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": 'entry', "corejs": 3 } ] ] }
entry模式时,须要在代码里手工引入一下。
// index.js // 手工引入 import "core-js/stable"; import "regenerator-runtime/runtime";
既然usage这么好,咱们何苦要用entry呢? 因此,就用usage模式吧!
四、问题来了:怎么解决辅助函数的冗余问题。
babel/runtime是干啥的,要杂用?
上文的配置,咱们解决了polyfill的问题。下一步,咱们要解决辅助函数在每一个js里都有冗余的问题。由于如今的配置下,若是你有 a.js 和 b.js,那么两个js中都会被babel放入那些 helper 辅助函数。当webpack打包完成一个 bundle。学过webpack原理的应该指到:最终的bundle里也会有 a.js 和 b.js模块,每一个模块中必然也存在重复的辅助函数。
怎么解决? 那就用 babel/plugin-transform-runtime 插件。
这个插件的工做就是:在babel编译每一个js时,把里面的辅助函数给换成 对 babel/runtime 的 require 引用。
当每一个js里的辅助函数,都变成 babel/runtime的引用。那么webpack打包后的bundle,必然这些辅助函数就变成一个公共模块了,解决了冗余问题。
配置方式:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": 'usage', "corejs": 3 } ] ], "plugins": [ ["@babel/plugin-transform-runtime", { "helpers": true, // 这就是抽离helper 的配置 "corejs": false, // 先设置false,后面再讲 "regenerator": false // 先设置false,后面再讲 }] ] }
上面把 helper配置为true,就能够实现helper函数抽到 babel/runtime。
至此,网站打包讲解配置完成!开发网站时就用上述配置便可。
开发jssdk跟开发网站对bundle.js有不一样的要求。
能够显而易见,对于开发jssdk,咱们第一要作的,就是先把全局polyfill给他关掉。好比关掉那个 usage:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": false } ] ] }
幸运的是,jssdk的另外几个要求,靠 babel/plugin-transform-runtime 均可以搞定。以下配置便可:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": false } ] ], "plugins": [ ["@babel/plugin-transform-runtime", { "helpers": true, // 这就是抽离helper 的配置 "corejs": 3, // 这是局部polyfill的配置 "regenerator": true // 这是局部polyfill generator的配置 }] ] }
咱们只需把该插件的配置全开。把 preset/env 的polyfill配置关掉,就是一个适配jssdk开发的配置了。