细说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 相关文章推荐
JavaScript String.replace函数参数实例说明
Jun 06 Javascript
用Jquery实现滚动新闻
Feb 12 Javascript
jQuery在页面加载时动态修改图片尺寸的方法
Mar 20 Javascript
JavaScript中innerHTML,innerText,outerHTML的用法及区别
Sep 01 Javascript
JS实现隔行换色的表格排序
Mar 27 Javascript
浅谈从React渲染流程分析Diff算法
Sep 08 Javascript
使用Object.defineProperty如何巧妙找到修改某个变量的准确代码位置
Nov 02 Javascript
JS浅拷贝和深拷贝原理与实现方法分析
Feb 28 Javascript
jquery+ajax实现上传图片并显示上传进度功能【附php后台接收】
Jun 06 jQuery
8 个有用的JS技巧(推荐)
Jul 03 Javascript
layui实现数据表格点击搜索功能
Mar 26 Javascript
Vue实现背景更换颜色操作
Jul 17 Javascript
微信小程序实现拖拽功能
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 split汉字
2009/06/05 PHP
js的.innerHTML = &quot;&quot;IE9下显示有错误的解决方法
2013/09/16 Javascript
jquery插件tooltipv顶部淡入淡出效果使用示例
2013/12/05 Javascript
为jQuery添加Webkit的触摸的方法分享
2014/02/02 Javascript
javascript类型转换使用方法
2014/02/08 Javascript
javascript获取wx.config内部字段解决微信分享
2016/03/09 Javascript
AngularJS 表达式详解及实例代码
2016/09/14 Javascript
微信小程序前端源码逻辑和工作流
2016/09/25 Javascript
jQuery ajaxForm()的应用
2016/10/14 Javascript
微信小程序 教程之模块化
2016/10/17 Javascript
jquery+css实现侧边导航栏效果
2017/06/12 jQuery
es6+angular1.X+webpack 实现按路由功能打包项目的示例
2017/08/16 Javascript
js实现随机点名小功能
2017/08/17 Javascript
node.js+express+mySQL+ejs+bootstrop实现网站登录注册功能
2018/01/12 Javascript
Vue实现侧边菜单栏手风琴效果实例代码
2018/05/31 Javascript
mac上配置Android环境变量的方法
2018/07/08 Javascript
Python正则表达式和元字符详解
2018/11/29 Python
Python 运行 shell 获取输出结果的实例
2019/01/07 Python
Python两个字典键同值相加的几种方法
2019/03/05 Python
Python Web程序搭建简单的Web服务器
2019/07/31 Python
Python 变量的创建过程详解
2019/09/02 Python
python字符串,元组,列表,字典互转代码实例详解
2020/02/14 Python
浅谈PyTorch的可重复性问题(如何使实验结果可复现)
2020/02/20 Python
pytorch之Resize()函数具体使用详解
2020/02/27 Python
使用HTML和CSS3绘制基本卡通图案的示例分享
2015/11/06 HTML / CSS
大学自我评价
2014/02/12 职场文书
生日寄语大全
2014/04/08 职场文书
岗位职责说明书
2014/05/07 职场文书
婚纱店策划方案
2014/05/22 职场文书
市场部经理岗位职责
2015/02/02 职场文书
大学毕业晚会开场白
2015/05/29 职场文书
详解Apache SkyWalking 告警配置指南
2021/04/22 Servers
python3实现Dijkstra算法最短路径的实现
2021/05/12 Python
nginx部署多前端项目的几种方法
2021/05/25 Servers
ConstraintValidator类如何实现自定义注解校验前端传参
2021/06/18 Java/Android
Python可变与不可变数据和深拷贝与浅拷贝
2022/04/06 Python