细说webpack6 Babel的使用详解
在webpack中编写JavaScript代码,可以使用最新的ES语法,而最终打包的时候,webpack会借助Babel将ES6+语法转换成在目标浏览器可执行ES5语法。所以Babel是一个重要的知识点需要掌握。
什么是Babel
Babel是JavaScript的编译器,通过Babel可以将我们写的最新ES语法的代码轻松转换成任意版本的JavaScript语法。随着浏览器逐步支持ES标准,我们不需要改变代码,只需要修改Babel配置即可以适配新的浏览器。
举例说明,下面是ES6箭头函数语法的代码:
[1,2,3].map(n=>n**2);
经过Babel处理后,可以转换为普通的ES5语法:
[1,2,3].map(function(n){ returnMath.pow(n,2); });
Babel初体验
下面来介绍下Babel的安装和功能及其配置文件。
1.使用babel-cli命令行工具
Babel本身自己带有CLI(Command-LineInterface,命令行界面)工具,可以单独安装使用。下面我们在项目中安装@babel/cli和@babel/core。
npmi-D@babel/core@babel/cli
然后创建一个babel.js文件:
//babel.js [1,2,3].map(n=>n**2);
然后执行npxbabelbabel.js,则会看到输出的内容,此时可能会看到输出的内容跟源文件内容没有区别,这是因为没有加转换规则,下面安装@babel/preset-env。然后执行CLI的时候添加--presetsflag:
//安装preset-env npmi-D@babel/preset-env //执行CLI添加--presets npxbabelbabel.js--presets=@babel/preset-env
最终输出的代码就是转换为ES5的代码了:
‘usestrict' [1,2,3].map(function(n){ returnMath.pow(n,2); });
如果要输出结果到固定文件,可以使用--out-file或-o参数:
npxbabelbabel.js-ooutput.js。
Tips:Babel7使用了@babel命名空间来区分官方包,因此以前的官方包babel-xxx改成了@babel/xxx。
2.配置文件
除了使用命令行配置flag之外,Babel还支持配置文件,配置文件支持两种:
使用package.json的babel属性;
在项目根目录单独创建.babelrc或者.babelrc.js文件。
示例如下:
//package.json { ‘name':‘my-package', ‘version':‘1.0.0', ‘babel':{ ‘presets':[‘@babel/preset-env'] } } //.babelrc { ‘presets':[‘@babel/preset-env'] }
Babel会在正在被转义的文件当前目录中查找一个.babelrc文件。如果不存在,它会向外层目录遍历目录树,直到找到一个.babelrc文件,或一个package.json文件中有"babel":{}。
3.env选项
如果我们希望在不同的环境中使用不同的Babel配置,那么可以在配置文件中添加env选项:
{ ‘env':{ ‘production':{ ‘presets':[‘@babel/preset-env'] } } }
env选项的值将从process.env.BABEL_ENV获取,如果没有的话,则获取process.env.NODE_ENV的值,它也无法获取时会设置为"development"。
Babel的插件和Preset
Babel的语法转换是通过强大的插件系统来支持的。Babel的插件分为两类:转换插件和语法解析插件。
不同的语法对应着不同的转换插件,比如我们要将箭头函数转换为ES5函数写法,那么可以单独安装@babel/plugin-transform-arrow-functions插件,转换插件主要职责是进行语法转换的,而解析插件则是扩展语法的,比如我们要解析jsx这类React设计的特殊语法,则需要对应的jsx插件。
如果不想一个个的添加插件,那么可以使用插件组合preset(插件预设,插件组合更加好理解一些),最常见的preset是@babel/preset-env。之前的preset是按照TC39提案阶段来分的,比如看到babel-preset-stage-1代表,这个插件组合里面是支持TC39
@babel/preset-env是Babel官方推出的插件预设,它可以根据开发者的配置按需加载对应的插件,通过@babel/preset-env我们可以根据代码执行平台环境和具体浏览器的版本来产出对应的JavaScript代码,例如可以设置代码执行在Node.js8.9或者iOS12版本。
Babelpolyfill
Babel只负责进行语法转换,即将ES6语法转换成ES5语法,但是如果在ES5中,有些对象、方法实际在浏览器中可能是不支持的,例如:Promise、Array.prototype.includes,这时候就需要用@babel/polyfill来做模拟处理。@babel/polyfill使用方法是先安装依赖,然后在对应的文件内显性的引入:
//安装,注意因为我们代码中引入了polyfill,所以不再是开发依赖(--save-dev,-D) npmi@babel/polyfill
在文件内直接import或者require进来:
//polyfill import‘@babel/polyfill' console.log([1,2,3].includes(1));
Bableruntime
@babel/polyfill虽然可以解决模拟浏览器不存在对象方法的事情,但是有以下两个问题:
直接修改内置的原型,造成全局污染;
无法按需引入,Webpack打包时,会把所有的Polyfill都加载进来,导致产出文件过大。
为了解决这个问题,Babel社区又提出了@babel/runtime的方案,@babel/runtime不再修改原型,而是采用替换的方式,比如我们用Promise,使用@babel/polyfill会产生一个window.Promise对象,而@babel/runtime则会生成一个_Promise(示例而已)来替换掉我们代码中用到的Promise。另外@babel/runtime还支持按需引入。下面以转换Object.assign为例,来看下@babel/runtime怎么使用。
- 安装依赖@babel/runtime:npmi@babel/runtime;
- 安装npmi-D@babel/plugin-transform-runtime作为Babel插件;
- 安装需要转换Object.assign的插件:
npmi-D@babel/plugin-transform-object-assign
编写一个runtime.js文件,内容如下:
Object.assign({},{a:1});
执行npxbabelruntime.js--plugins@babel/plugin-transform-runtime,@babel/plugin-transform-object-assign,最终的输出结果是:
import_extendsfrom‘@babel/runtime/helpers/extends'; _extends( {}, { a:1 } );
代码中自动引入了@babel/runtime/helpers/extends这个模块(所以要添加@babel/runtime依赖啊)。
@babel/runtime也不是完美的解决方案,由于@babel/runtime不修改原型,所以类似[].includes()这类使用直接使用原型方法的语法是不能被转换的。
Tips:'@babel/polyfill'实际是core-js和
regenerator-runtime的合集,所以如果要按需引入'@babel/polyfill'的某个模块,可以直接引入对应的
core-js模块,但是手动引入的方式还是太费劲。
@babel/preset-env
铺垫了这么多,我们继续来讲@babel/preset-env,前面介绍了@babel/preset-env可以零配置的转化ES6代码,我们如果要精细化的使用@babel/preset-env,就需要配置对应的选项了,在@babel/preset-env的选项中,useBuiltIns和target是最重要的两个,useBuiltIns用来设置浏览器polyfill,target是为了目标浏览器或者对应的环境(browser/node)。
preset-env的useBuiltIns
前面介绍了@babel/polyfill和@babel/runtime两种方式来实现浏览器polyfill,两种方式都比较繁琐,而且不够智能,我们可以使用@babel/preset-env的useBuildIn选项做polyfill,这种方式简单而且智能。
useBuiltIns默认为false,可以使用的值有usage和entry:
{ ‘presets':[ ‘@babel/preset-env',{ ‘useBuiltnls':‘usage|entry|false' } ] }
usage表示明确使用到的Polyfill引用。在一些ES2015+语法不支持的环境下,每个需要用到Polyfill的引用时,会自动加上,例如:
constp=newPromise(); [1,2].includes(1); ‘foobar'.includes(‘foo');
使用useBuiltIns='usage'编译之后,上面代码变成,真正的做到了按需加载,而且类似[].includes()这类直接使用原型方法的语法是能被转换的:
‘usestrict' require(‘core-js/modules/es.array.includes'); require(‘core-js/modules/es.object.to-string'); require(‘core-js/modules/es.promise'); require(‘core-js/modules/es.string.includes'); varp=newPromise(); [1,2].includes(1); ‘foobar'.includes(‘foo');
entry表示替换import"@babel/polyfill";(新版本的Babel,会提示直接引入core-js或者regenerator-runtime/runtime来代替@babel/polyfill)的全局声明,然后根据targets中浏览器版本的支持,将polyfill拆分引入,仅引入有浏览器不支持的polyfill,所以entry相对usage使用起来相对麻烦一些,首先需要手动显性的引入@babel/polyfill,而且根据配置targets来确定输出,这样会导致代码实际用不到的polyfill也会被打包到输出文件,导致文件比较大。
一般情况下,个人建议直接使用usage就满足日常开发了。
需要提一下的是,polyfill用到的core-js是可以指定版本的,比如使用core-js@3,则首先安装依赖npmi-Score-js@3,然后在Babel配置文件.babelrc中写上版本。
//.babelrc { ‘presets':[ [ ‘@babel/preset-env', { ‘useBuiltlns':‘useage', ‘corejs':3 } ] ] }
preset-env的target
假设希望代码中使用ES6的模板字面量`语法,但是实际执行代码的宿主浏览器是IE10却不支持,那么我们可以使用target指定目标浏览器了。
{ ‘presets':[ [ ‘@babel/preset-env', { ‘targets':{ ‘browsers':‘IE10' } } ] ] }
如果我们代码是在Node.js环境执行的,则可以指定Node.js的版本号:
{ ‘presets':[ [ ‘env', { ‘@babel/preset-env':{ ‘node:‘8.9.3' } } ] ] }
targets.browsers需要使用browserslist的配置方法,但是其设置会被targets.[chrome,opera,edge,firefox,safari,ie,ios,android,node,electron]覆盖;
targets.node设置为true或"current"可以根据当前Node.js版本进行动态转换。也可以设置为具体的数字表示需要支持的最低Node.js版本;
targets.esmodules设置使用ESModules语法,最新浏览器支持,这个在后面Webpack插件章节会详细介绍如何实现ModernMode。
在Webpack中使用Babel
通过上面的内容,我们已经掌握了Babel的基本用法,下面在webpack中使用Babel就变得很简单了,首先安装npm依赖,然后修改webpack.config.js。
安装依赖包:
//安装开发依赖 npmiwebpackbabel-loaderwebpack-cli@babel/core@babel/preset-env@babel/plugin-transform-runtime-D //将runtime作为依赖 npmi@babel/runtime-S
第二步创建webpack.config.js文件,内容如下:
//webpack.config.js module.exports={ entry:‘./babel.js', mode:‘development', devtool:false, module:{ rules:[ { test:/\.js$/, use:[ { loader:‘babel-loader', options:{ presets:[ [ ‘@babel/preset-env', { useBuiltlns:‘usage' } ] ] } } ] } ] } }
上面的webpack.config.js文件直接将Babel的配置写到了options中,还可以在项目根目录下创建.babelrc或者使用package.json的babel字段。
小结
在本篇中,我们学习了Webpack怎么配置Babel,希望对大家有所帮助,如果喜欢萝卜的文章,请大家持续关注,下一篇我将给大家介绍webpack-dev-server这个超好用的工具。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。