细说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 相关文章推荐
prototype 的说明 js类
Sep 07 Javascript
用js实现控制内容的向上向下滚动效果
Jun 26 Javascript
javascript XML数据显示为HTML一例
Dec 23 Javascript
分享纯手写漂亮的表单验证
Nov 19 Javascript
详解AngularJS Filter(过滤器)用法
Dec 28 Javascript
javascript创建对象、对象继承的实用方式详解
Mar 08 Javascript
JS加载器如何动态加载外部js文件
May 26 Javascript
微信js-sdk分享功能接口常用逻辑封装示例
Oct 13 Javascript
Vue.js基础知识小结
Jan 13 Javascript
浅谈angular2路由预加载策略
Oct 04 Javascript
在react-router4中进行代码拆分的方法(基于webpack)
Mar 08 Javascript
jQuery 动态粒子效果示例代码
Jul 07 jQuery
微信小程序实现拖拽功能
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基础知识:类与对象(5) static
2006/12/13 PHP
PHP 第二节 数据类型之数值型
2012/04/28 PHP
PHP 自定义错误处理函数的使用详解
2013/05/10 PHP
thinkPHP5使用Rabc实现权限管理
2019/08/28 PHP
jQuery动态添加的元素绑定事件处理函数代码
2011/08/02 Javascript
jquery 检测元素是否存在的实例代码
2013/11/19 Javascript
js操作table示例(个人心得)
2013/11/29 Javascript
jQuery取得设置清空select选择的文本与值
2014/07/08 Javascript
JSON.stringify转换JSON时日期时间不准确的解决方法
2014/08/08 Javascript
js实现防止被iframe的方法
2015/07/03 Javascript
json对象转为字符串,当做参数传递时加密解密的实现方法
2016/06/29 Javascript
轻松学习Javascript闭包
2017/03/01 Javascript
AngularJS全局警告框实现方法示例
2017/05/18 Javascript
node中koa中间件机制详解
2017/08/22 Javascript
基于js中的原型(全面讲解)
2017/09/19 Javascript
vue-awesome-swiper滑块插件使用方法详解
2017/11/27 Javascript
vue学习教程之带你一步步详细解析vue-cli
2017/12/26 Javascript
vue+iview 实现可编辑表格的示例代码
2018/10/31 Javascript
Vue中使用create-keyframe-animation与动画钩子完成复杂动画
2019/04/09 Javascript
小程序两种滚动公告栏的实现方法
2019/09/17 Javascript
python启动办公软件进程(word、excel、ppt、以及wps的et、wps、wpp)
2009/04/09 Python
python将xml xsl文件生成html文件存储示例讲解
2013/12/03 Python
Python文本处理之按行处理大文件的方法
2018/04/09 Python
用python爬取租房网站信息的代码
2018/12/14 Python
使用Python的SymPy库解决数学运算问题的方法
2019/03/27 Python
python面试题小结附答案实例代码
2019/04/11 Python
正则给header的冒号两边参数添加单引号(Python请求用)
2019/08/09 Python
Python3 无重复字符的最长子串的实现
2019/10/08 Python
python解析多层json操作示例
2019/12/30 Python
python随机模块random的22种函数(小结)
2020/05/15 Python
西式婚礼证婚词
2014/01/12 职场文书
百家讲坛观后感
2015/06/12 职场文书
企业法人任命书
2015/09/21 职场文书
先进个人主要事迹怎么写
2015/11/04 职场文书
导游词之南京夫子庙
2019/12/09 职场文书
jQuery实现广告显示和隐藏动画
2021/07/04 jQuery