细说webpack6 Babel的使用详解


Posted in Javascript onSeptember 26, 2019

在 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) {
 return Math.pow(n, 2);
});

Babel 初体验

下面来介绍下 Babel 的安装和功能及其配置文件。

1. 使用 babel-cli 命令行工具

Babel 本身自己带有 CLI(Command-Line Interface,命令行界面)工具,可以单独安装使用。下面我们在项目中安装 @babel/cli 和 @babel/core。

npm i -D @babel/core @babel/cli

然后创建一个 babel.js 文件:

// babel.js
[1,2,3].map(n => n**2);

然后执行npx babel babel.js,则会看到输出的内容,此时可能会看到输出的内容跟源文件内容没有区别,这是因为没有加转换规则,下面安装@babel/preset-env。然后执行 CLI 的时候添加 --presets flag:

// 安装 preset-env
npm i -D @babel/preset-env
// 执行 CLI 添加 --presets
npx babel babel.js --presets=@babel/preset-env

最终输出的代码就是转换为 ES5 的代码了:

‘use strict'
[1,2,3].map(function(n) {
 return Math.pow(n, 2);
});

如果要输出结果到固定文件,可以使用 --out-file 或 -o 参数:

npx babel babel.js -o output.js。

Tips: Babel 7 使用了 @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< Stage-1 阶段的转换插件集合。

@babel/preset-env 是 Babel 官方推出的插件预设,它可以根据开发者的配置按需加载对应的插件,通过 @babel/preset-env 我们可以根据代码执行平台环境和具体浏览器的版本来产出对应的 JavaScript 代码,例如可以设置代码执行在 Node.js 8.9 或者 iOS 12 版本。

Babel polyfill

Babel 只负责进行语法转换,即将 ES6 语法转换成 ES5 语法,但是如果在 ES5 中,有些对象、方法实际在浏览器中可能是不支持的,例如:Promise、 Array.prototype.includes,这时候就需要用 @babel/polyfill 来做模拟处理。@babel/polyfill 使用方法是先安装依赖,然后在对应的文件内显性的引入:

// 安装,注意因为我们代码中引入了 polyfill,所以不再是开发依赖(--save-dev,-D)
npm i @babel/polyfill

在文件内直接 import 或者 require 进来:

// polyfill
import ‘@babel/polyfill'
console.log([1,2,3].includes(1));

Bable runtime

@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:npm i @babel/runtime;
  • 安装 npm i -D @babel/plugin-transform-runtime 作为 Babel 插件;
  • 安装需要转换 Object.assign 的插件:
npm i -D @babel/plugin-transform-object-assign

编写一个 runtime.js 文件,内容如下:

Object.assign({}, {a:1});

执行 npx babel runtime.js --plugins @babel/plugin-transform-runtime,@babel/plugin-transform-object-assign,最终的输出结果是:

import _extends from ‘@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 的引用时,会自动加上,例如:

const p = new Promise();
[1,2].includes(1);
‘foobar'.includes(‘foo');

使用 useBuiltIns='usage' 编译之后,上面代码变成,真正的做到了按需加载,而且类似 [].includes() 这类直接使用原型方法的语法是能被转换的:

‘use strict'
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');
var p = new Promise();
[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,则首先安装依赖 npm i -S core-js@3,然后在 Babel 配置文件 .babelrc 中写上版本。

// .babelrc
{
 ‘presets': [
 [
  ‘@babel/preset-env',
  {
   ‘useBuiltlns': ‘useage',
   ‘corejs': 3
  }
 ]
 ]
}

preset-env 的 target

假设希望代码中使用 ES6 的模板字面量`语法,但是实际执行代码的宿主浏览器是 IE 10 却不支持,那么我们可以使用</code>target指定目标浏览器了。

{
 ‘presets': [
  [
  ‘@babel/preset-env',
  {
   ‘targets': {
    ‘browsers': ‘IE 10'
   }
  }
  ]
 ]
}

如果我们代码是在 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 设置使用 ES Modules 语法,最新浏览器支持,这个在后面 Webpack 插件章节会详细介绍如何实现 Modern Mode。

在 Webpack 中使用 Babel

通过上面的内容,我们已经掌握了 Babel 的基本用法,下面在 webpack 中使用 Babel 就变得很简单了,首先安装 npm 依赖,然后修改 webpack.config.js。

安装依赖包:

// 安装开发依赖
npm i webpack babel-loader webpack-cli @babel/core @babel/preset-env @babel/plugin-transform-runtime -D

// 将 runtime 作为依赖
npm i @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 这个超好用的工具。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery 插件 任意位置浮动固定层
Dec 25 Javascript
ExtJs Excel导出并下载IIS服务器端遇到的问题
Sep 16 Javascript
Dom操作之兼容技巧分享
Sep 20 Javascript
JQuery对id中含有特殊字符的转义处理示例
Sep 06 Javascript
JavaScript中setter和getter方法介绍
Jul 11 Javascript
JS实现可编辑的后台管理菜单功能【附demo源码下载】
Sep 13 Javascript
jQuery实现带延时功能的水平多级菜单效果【附demo源码下载】
Sep 21 Javascript
JS检测数组类型的方法小结
Mar 14 Javascript
js中的触发事件对象event.srcElement与event.target详解
Mar 15 Javascript
微信小程序实现红包雨功能
Jul 11 Javascript
如何解决React官方脚手架不支持Less的问题(小结)
Sep 12 Javascript
vue中利用three.js实现全景图的完整示例
Dec 07 Vue.js
微信小程序实现拖拽功能
Sep 26 #Javascript
vue用BMap百度地图实现即时搜索功能
Sep 26 #Javascript
layui.tree组件的使用以及搜索节点功能的实现
Sep 26 #Javascript
微信小程序点击列表跳转到对应详情页过程解析
Sep 26 #Javascript
vue+element tabs选项卡分页效果
Jun 29 #Javascript
layui实现根据table数据判断按钮显示情况的方法
Sep 26 #Javascript
vue+element表格导出为Excel文件
Sep 26 #Javascript
You might like
PHP读书笔记_运算符详解
2016/07/01 PHP
thinkPHP实现基于ajax的评论回复功能
2018/06/22 PHP
jQuery学习笔记 获取jQuery对象
2012/09/19 Javascript
22点关于jquery性能优化的建议
2014/05/28 Javascript
JavaScript极简入门教程(二):对象和函数
2014/10/25 Javascript
ECMAScript6中Map/WeakMap详解
2015/06/12 Javascript
javascript实现获取图片大小及图片等比缩放的方法
2016/11/24 Javascript
简单的Vue SSR的示例代码
2018/01/12 Javascript
iview table高度动态设置方法
2018/03/14 Javascript
JavaScript变量声明var,let.const及区别浅析
2018/04/23 Javascript
js隐式转换的知识实例讲解
2018/09/28 Javascript
vue-router的使用方法及含参数的配置方法
2018/11/13 Javascript
微信小程序如何修改radio和checkbox的默认样式和图标
2019/07/24 Javascript
Element Card 卡片的具体使用
2020/07/26 Javascript
遗传算法之Python实现代码
2017/10/10 Python
深入理解Django的自定义过滤器
2017/10/17 Python
详解PyTorch批训练及优化器比较
2018/04/28 Python
Python决策树之基于信息增益的特征选择示例
2018/06/25 Python
python配置grpc环境
2019/01/01 Python
淘宝秒杀python脚本 扫码登录版
2019/09/19 Python
python设置环境变量的作用整理
2020/02/17 Python
Pytorch - TORCH.NN.INIT 参数初始化的操作
2021/02/27 Python
英国轻奢珠宝品牌:Astley Clarke
2016/12/18 全球购物
英国地毯卖家:The Rug Seller
2019/07/18 全球购物
说说你所熟悉或听说过的j2ee中的几种常用模式?及对设计模式的一些看法
2012/05/24 面试题
青春演讲稿范文
2014/05/08 职场文书
征兵宣传标语
2014/06/20 职场文书
社会实践活动总结范文
2014/07/03 职场文书
井冈山红色之旅心得体会
2014/10/07 职场文书
党的群众路线教育实践活动个人整改措施
2014/10/27 职场文书
党的群众路线教育实践活动心得体会(乡镇)
2014/11/03 职场文书
2014年个人教学工作总结
2014/12/09 职场文书
七一建党节慰问信
2015/02/14 职场文书
2015年审计人员工作总结
2015/05/26 职场文书
运动会宣传语
2015/07/13 职场文书
php引用传递
2021/04/01 PHP