将ES6编译为ES5

Posted by huangqing on March 21, 2020

babel 7

babel的目的就是为了解决浏览器的自身对于es语言的差异性而带来的一款工具,有了babel就不用担心浏览器不支持es语言这件事。

babel 编译的阶段: babel 总共分为三个阶段:解析,转换,生成。

babel-abstract-syntax-tree

babel7的各个组成部分:

  • @babel/cli
  • @babel/core
  • @babel/preset-env
  • @babel/polyfill
  • @babel/runtime
  • @babel/plugin-transform-runtime
  • @babel/plugin-transform-xxx

@babel/cli

@babel/cli 是一个适合安装在本地项目里,而不是全局安装

@babel/nodenode cli类似,不适用在产品中,意味着适合全局安装

注意一下这个@这个符号,这个是只有babel7才特有的

npm install --save "@babel/cli" "@babel/core"  "@babel/preset-env" "@babel/polyfill" "@babel/plugin-transform-runtime"

@babel/plugin-transform-xxx

真正发挥作用执行转换需要使用插件@babel/plugin-transform-xxx

使用.babelrc配置文件:

{
  "plugins": [
    "@babel/plugin-transform-arrow-functions"
    "@babel/plugin-transform-block-scoping" 
  ]
}

@babel/preset-env

babel提供了一个叫做preset的概念,说好听点叫预设,直白点就是插件包的意思,意味着babel会预先替我们做好了一系列的插件包,例如下面这些babel认为程序员会用到的常用的插件包:

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

@babel/preset-env

Without any configuration options, babel-preset-env behaves exactly the same as babel-preset-latest (or babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017 together).

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "4",
          "chrome": "58",
          "ie": "11"
        }
      }
    ]
  ]
}

babel 编译过程处理第一种情况 - 统一语法的形态,通常是高版本语法编译成低版本的,比如 ES6 语法编译成 ES5 或 ES3。而 babel-polyfill 处理第二种情况 - 让目标浏览器支持所有特性,不管它是全局的,还是原型的,或是其它。这样,通过 babel-polyfill,不同浏览器在特性支持上就站到同一起跑线。

@babel/polyfill

As of Babel 7.4.0, this package has been deprecated in favor of directly including core-js/stable (to polyfill ECMAScript features) and regenerator-runtime/runtime (needed to use transpiled generator functions

polyfill我们又称垫片,见名知意,所谓垫片也就是垫平不同浏览器或者不同环境下的差异,因为有的环境支持这个函数,有的环境不支持这种函数,解决的是有与没有的问题,这个是靠单纯的@babel/preset-env不能解决的,因为@babel/preset-env解决的是将高版本写法转化成低版本写法的问题,因为不同环境下低版本的写法有可能不同而已。

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如IteratorGeneratorSetMapsProxyReflectSymbolPromise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。

import "core-js/stable";
import "regenerator-runtime/runtime";
npm install --save @babel/polyfill

Because this is a polyfill (which will run before your source code), we need it to be a dependency, not a devDependency

Usage in Node / Browserify / Webpack

require("@babel/polyfill");

ES6’s import syntax

import "@babel/polyfill";

Usage in Browser

Available from the dist/polyfill.js file within a @babel/polyfill npm release. This needs to be included before all your compiled Babel code. You can either prepend it to your compiled code or include it in a <script> before it.

ECMAScript6 compatibility

IE中引入polyfill.js文件解决API报错的问题

@babel/runtime

@babel/runtime的作用是提供统一的模块化的helper

npm install --save "@babel/runtime" "@babel/plugin-transform-runtime"
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "4"
        }
      }
    ]
  ],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]
}

执行编译

package.json

{
  "name": "babel",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "babel index.js -d lib"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/cli": "^7.1.5",
    "@babel/core": "^7.1.5",
    "@babel/plugin-transform-runtime": "^7.1.0",
    "@babel/polyfill": "^7.0.0",
    "@babel/preset-env": "^7.1.5"
  }
}

babel.config.js

const presets = [
    [
      "@babel/env",
      {
        targets: {
        ie:"11",
        edge: "17",
        firefox: "60",
        chrome: "67",
        safari: "11.1",
        },
      },
    ],
  ];
  
  module.exports = { presets };

babel是按照CommonJS的规范将 import 转换成CommonJS规范 形式的,但是浏览器本身没有 module export require global 这些变量,并没有原生的支持CommonJS规范 ,所以需要一些库来做这些支持。

打包编译浏览器环境

使用 browserify

安装依赖包:

cnpm install -g browserify
cnpm install --save-dev babelify

执行编译

browserify src/index.js -t babelify --outfile dist-babelify/index.js

在gulp中使用gulp-browserify

安装依赖包:

cnpm install --save-dev gulp-browserify

gulpfile.js


var gulp = require('gulp');
var browserify = require('gulp-browserify');
var rename = require('gulp-rename');
var babelify = require("babelify");

gulp.task('es5', async function () {
    //需要打包的文件入口
    gulp.src('src/dashboard.js', { read: false })
        .pipe(browserify({
            debug: true,
            transform: ["babelify"],
        }))
        .pipe(rename('dashboard.js'))
        .pipe(gulp.dest('./build/'))
})

在gulp中使用browserify

npm install --save-dev browserify vinyl-source-stream vinyl-buffer
var browserify = require('browserify'),  // 插件,实际是node系
    // 转成stream流,gulp系
    stream = require('vinyl-source-stream'),
    // 转成二进制流,gulp系
    buffer = require('vinyl-buffer');
    
gulp.task('browserify', function () {
    // 定义入口文件
    return browserify({
        // 入口必须是转换过的es6文件,且文件不能是es6经过转换的es5文件,否者会报错
        entries: 'src/dashboard.js',
        debug: true
    })
        // 在bundle之前先转换es6,因为readabel stream 流没有transform方法
        //.transform("babelify", {presets: ['es2015']})
        .transform("babelify")
        // 转成stream流(stream流分小片段传输)
        .bundle()
        .on('error', function (error) {
            console.log(error.toString())
        })
        // node系只有content,添加名字转成gulp系可操作的流
        .pipe(stream('dashboard.js'))
        // 转成二进制的流(二进制方式整体传输)
        .pipe(buffer())
        // 输出
        .pipe(gulp.dest('build/'))

source-map在谷歌浏览器上无效,可以重置浏览器的设置使其生效