一、Babel是什么
Babel是一个JavaScript编译器
- 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
- babel如果本身没有任何插件,则基本就是源代码不发生变化,要想进行相应的转化,就必须有相应的插件带动它。
babel可以帮助我们做什么:
- 语法转换
- 源代码转换
- Polyfill实现目标缓解缺少的功能等;
二、为什么需要babel
事实上,在开发中很少直接去接触babel,但是babel对于前端开发来说,目前是不可缺少的一部分:
- 开发中,我们想要使用ES6+的语法,想要使用TypeScript,开发React项目,它们都是离不开Babel
- 学习Babel对于我们理解代码从编写到线上的转变过程直观重要
- 了解真相,才能获得真知的自由
三、Babel的命令行使用
babel本身可以作为一个独立的工具(和postcss一样),不用和webpack等构建工具配置,也可以单独使用
如果我们希望在命令行尝试使用babel,需要安装如下库:
- @babel/core:babel的核心代码,必须安装;
- @babel/cli:可以让我们在命令行使用babel
npm install @babel/cli @babel/core
使用babel来处理我们的源代码:
- src:是源文件的目录
- —out-dir:指定要输出的文件夹dist
npx babel src --out-dir dist
源代码文件:
const message = 'Hello Babel';const fun = (msg) => {console.log(msg);};fun(message);
npx babel src --out-dir dist后:
const message = 'Hello Babel';const fun = msg => {console.log(msg);};fun(message);
可见,几乎没有变化,因为babel/core只是核心库,需要借助插件来进行编译转换。
四、插件的使用
比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件:
npm install @babel/plugin-transform-arrow-functions -Dnpx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
const message = 'Hello Babel';const fun = function (msg) {console.log(msg);};fun(message);
可见箭头函数已经转换为普通函数
但是会发现 const 并没有转成 var
这是因为 plugin-transform-arrow-functions并没有提供这样的功能
我们需要使用 plugin-transform-block-scoping 来完成这样的功能;
npm install @babel/plugin-transform-block-scoping -Dnpx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping,@babel/plugin-transform-arrow-functions
var message = 'Hello Babel';var fun = msg => {console.log(msg);};fun(message);
可以,都已经转为ES5的写法。
五、Babel的预设preset
如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset)
后面我们再具体来讲预设代表的含义
安装@babel/preset-env预设
npm install @babel/preset-env -D
执行如下命令:
npx babel src --out-dir dist --presets=@babel/preset-env
"use strict";var message = 'Hello Babel';var fun = function fun(msg) {console.log(msg);};fun(message);
六、Babel的底层原理
babel是如何做到将我们的一段代码(ES6、TypeScript、React)转成另外一段代码(ES5)的呢?
从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?
- 就是编译器,事实上我们可以将babel看成就是一个编译器
- Babel编译器的作用就是将我们的源代码,转换成浏览器可以直接识别的另外一段源代码
Babel也拥有编译器的工作流程:
- 解析阶段(Parsing)
- 转换阶段(Transformation)
- 生成阶段(Code Generation)
编译器执行原理

七、babel-loader
实际开发中,我们通常会在构建工具中通过配置babel来对其进行使用的,比如在webpack中。
那么我们就需要去安装相关的依赖:
如果之前已经安装了@babel/core,那么这里不需要再次安装
npm install babel-loader @babel/core
我们可以设置一个规则,在加载js文件时,使用我们的babel:
module: {rules: [{test: /\.m?js$/,use: {loader: 'babel-loader',},},],},
npm run build后:
/******/ (function() { // webpackBootstrapvar __webpack_exports__ = {};/*!*********************!*\!*** ./src/main.js ***!\*********************/const message = 'Hello Babel';const fun = msg => {console.log(msg);};fun(message);/******/ })();
ES6语法并没有转换,因为我们没用使用插件
指定使用的插件
module: {rules: [{test: /\.m?js$/,use: {loader: 'babel-loader',options: {plugins: ['@babel/plugin-transform-block-scoping','@babel/plugin-transform-arrow-functions',],},},},],},
npm run build后:
/******/ (function() { // webpackBootstrapvar __webpack_exports__ = {};/*!*********************!*\!*** ./src/main.js ***!\*********************/var message = 'Hello Babel';var fun = function (msg) {console.log(msg);};fun(message);/******/ })();
babel-preset
如果我们一个个去安装使用插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个 preset
webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel。
比如常见的预设有三个:
- env
- react
- TypeScript
安装preset-env:
npm install @babel/preset-env
/******/ (function() { // webpackBootstrapvar __webpack_exports__ = {};/*!*********************!*\!*** ./src/main.js ***!\*********************/var message = 'Hello Babel';var fun = function fun(msg) {console.log(msg);};fun(message);/******/ })();
设置目标浏览器 browserslist
我们最终打包的JavaScript代码,是需要跑在目标浏览器上的,那么如何告知babel我们的目标浏览器呢?
- browserslist工具
- ptarget属性
说明一点:babel是转换源代码且运行在浏览器上,那么就要知道浏览器支持的语法来进行源代码的转换
之前项目中已经使用了browserslist工具,可以对比一下不同的配置,打包的区别:

chrome支持ES6语法,所以babel就不会进行转换了。
设置目标浏览器targets
module: {rules: [{test: /\.m?js$/,use: {loader: 'babel-loader',options: {// plugins: [// '@babel/plugin-transform-block-scoping',// '@babel/plugin-transform-arrow-functions',// ],presets: ['@babel/preset-env',{targets:"last 2 version"}],},},},],},
那么,如果两个同时配置了,哪一个会生效呢?
- 配置的targets属性会覆盖browserslist
- 但是在开发中,更推荐通过browserslist来配置,因为类似于postcss工具,也会使用browserslist,进行统一浏览器的适配
Stage-X的preset
要了解Stage-X,我们需要先了解一下TC39的组织:
- TC39是指技术委员会(Technical Committee)第 39 号
- 它是 ECMA 的一部分,ECMA 是 “ECMAScript” 规范下的 JavaScript 语言标准化的机构
- ECMAScript 规范定义了 JavaScript 如何一步一步的进化、发展
TC39 遵循的原则是:分阶段加入不同的语言特性,新流程涉及四个不同的 Stage
- Stage 0:strawman(稻草人),任何尚未提交作为正式提案的讨论、想法变更或者补充都被认为是第 0 阶段的” 稻草人”
- Stage 1:proposal(提议),提案已经被正式化,并期望解决此问题,还需要观察与其他提案的相互影响
- Stage 2:draft(草稿),Stage 2 的提案应提供规范初稿、草稿。此时,语言的实现者开始观察 runtime 的具体 实现是否合理
- Stage 3:candidate(候补),Stage 3 提案是建议的候选提案。在这个高级阶段,规范的编辑人员和评审人员必须在最终规范上签字Stage 3 的提案不会有太大的改变,在对外发布之前只是修正一些问题
- Stage 4:finished(完成),进入 Stage 4 的提案将包含在 ECMAScript 的下一个修订版中
Babel的Stage-X设置
在babel7之前(比如babel6中),我们会经常看到这种设置方式:
module.exports = {"presets":["stage-0"]}
它表达的含义是使用对应的 babel-preset-stage-x 预设
但是从babel7开始,已经不建议使用了,建议使用preset-env来设置;
Babel的配置文件
像之前一样,我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写:
- babel.config.json(或者.js,.cjs,.mjs)文件
- .babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件
它们两个有什么区别呢?
- 目前很多的项目都采用了多包管理的方式(babel本身、element-plus、umi等)
- .babelrc.json:早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的
- babel.config.json(babel7):可以直接作用于Monorepos项目的子包,更加推荐
babel.config.js:
module.exports = {presets: ['@babel/preset-env'],};
八、polyfill
Polyfill是什么
- 翻译:一种用于衣物、床具等的聚酯填充材料, 使这些物品更加温暖舒适
- 理解:更像是应该填充物(垫片),一个补丁,可以帮助我们更好的使用JavaScript
为什么时候会用到polyfill
- 比如我们使用了一些语法特性(例如:Promise, Generator, Symbol等以及实例方法例如 Array.prototype.includes等)
- 但是某些浏览器压根不认识这些特性,必然会报错
- 我们可以使用polyfill来填充或者说打一个补丁,那么就会包含该特性了
使用polyfill
babel7.4.0之前,可以使用 @babel/polyfill的包,但是该包现在已经不推荐使用了
babel7.4.0之后,可以通过单独引入core-js和regenerator-runtime来完成polyfill的使用
npm install core-js regenerator-runtime --save
全局引入polyfill和第三方包自己集成的可能会有冲突,所以babel打包时需要忽略第三方包
{test: /\.m?jsx?$/,//忽略第三方包使用babel打包编译exclude:/node_modules/,use: {loader: 'babel-loader',},},
配置babel.config.js
需要在babel.config.js文件中进行配置,给preset-env配置一些属性:
- useBuiltIns:设置以什么样的方式来使用polyfill
- corejs:设置corejs的版本,目前使用较多的是3.x的版本
- 另外corejs可以设置是否对提议阶段的特性进行支持,设置 proposals属性为true即可
useBuiltIns属性设置
第一个值:false
- 打包后的文件不使用polyfill来进行适配
- 并且这个时候是不需要设置corejs属性的
第二个值:usage
- 会根据源代码中出现的语言特性,自动检测所需要的polyfill;
- 这样可以确保最终包里的polyfill数量的最小化,打包的包相对会小一些
- 可以设置corejs属性来确定使用的corejs的版本
第三个值:entry
- 如果我们依赖的某一个库本身使用了某些polyfill的特性,但是因为我们使用的是usage,所以之后用户浏览器可能会报错
- 所以,如果你担心出现这种情况,可以使用 entry,并且需要在入口文件中添加
import 'core-js/stable'import 'regenerator-runtime/runtime'- 这样做会根据browserslist目标导入所有的polyfill,但是对应的包也会变大
babel.config.js
module.exports = {presets: [['@babel/preset-env',{//false//usage//entryuseBuiltIns: 'entry',corejs: 3,},],],};
入口文件main.js
import 'core-js/stable';import 'regenerator-runtime/runtime';const message = 'Hello Babel';const fun = (msg) => {console.log(msg);};fun(message);let promise = new Promise((resolve, reject) => {});
打包后的文件有15000多行,就不展示了。
Plugin-transform-runtime
在前面我们使用的polyfill,默认情况是添加的所有特性都是全局的
如果我们正在编写一个工具库,这个工具库需要使用polyfill
别人在使用我们工具时,工具库通过polyfill添加的特性,可能会污染它们的代码
所以,当编写工具时,babel更推荐我们使用一个插件:
@babel/plugin-transform-runtime
来完成polyfill的功能
npm install @babel/plugin-transform-runtime -D
使用plugins来配置babel.config.js:
module.exports = {presets: [['@babel/preset-env'],],plugins: [['@babel/plugin-transform-runtime',{corejs: 3,},],],};
注意:因为使用了corejs3,所以我们需要安装对应的库:
npm i @babel/runtime-corejs3
九、React的jsx支持
在编写react代码时,react使用的语法是jsx,jsx是可以直接使用babel来转换的
对react jsx代码进行处理需要如下的插件:
- @babel/plugin-syntax-jsx
- @babel/plugin-transform-react-jsx
- @babel/plugin-transform-react-display-name
但是开发中,我们并不需要一个个去安装这些插件,我们依然可以使用preset来配置:
npm install @babel/preset-react -D
module.exports = {presets: [['@babel/preset-env',{useBuiltIns: 'usage',corejs: 3,},],['@babel/preset-react']],};
十、TypeScript的编译
我们会使用TypeScript来开发,那么TypeScript代码是需要转换成JavaScript代码。
可以通过TypeScript的compiler来转换成JavaScript:
npm install typescript -g
之后我们可以运行 npx tsc来编译自己的ts代码:
tsc index.ts
使用ts-loader
如果希望在webpack中使用TypeScript,那么我们可以使用ts-loader来处理ts文件:
npm install ts-loader -D
配置ts-loader:
{test: /\.ts$/,exclude: /node_modules/,use: ['ts-loader'],},
之后,我们通过npm run build打包,会报错,因为需要一个tsconfig.json文件
tsc —init 生成一个tsconfig.js文件
但是ts-loader会依赖本地的typescript,所以本地也需要安装下
npm install typescript -D
npm run build
bundle.js
/******/ (function() { // webpackBootstrap/******/ "use strict";var __webpack_exports__ = {};// This entry need to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).!function() {var exports = __webpack_exports__;/*!**********************!*\!*** ./src/index.ts ***!\**********************/Object.defineProperty(exports, "__esModule", ({ value: true }));var message = 'Hello TypeScript';var foo = function (msg) {console.log(msg);};foo(message);}();/******/ })();
使用babel-loader
除了可以使用TypeScript Compiler来编译TypeScript之外(ts-loader也是基于tsc的),我们也可以使用Babel
- Babel是有对TypeScript进行支持
我们可以使用插件:
- @babel/tranform-typescript
但是更推荐直接使用preset:
- @babel/preset-typescript
来安装@babel/preset-typescript:
npm install @babel/preset-typescript -D
{test: /\.ts$/,exclude: /node_modules/,use: ['babel-loader'],},
presets: [['@babel/preset-env',{useBuiltIns: 'usage',corejs: 3,},],['@babel/preset-react'],['@babel/preset-typescript']],
ts-loader和babel-loader选择
那么我们在开发中应该选择ts-loader还是babel-loader呢?
使用ts-loader(TypeScript Compiler)
- 直接编译TypeScript,那么只能将ts转换成js
- 如果我们还希望在这个过程中添加对应的polyfill,那么ts-loader是无能为力的
- 我们需要借助于babel来完成polyfill的填充功能
使用babel-loader(Babel)
- 直接编译TypeScript,也可以将ts转换成js,并且可以实现polyfill的功能
- 但是babel-loader在编译的过程中,不会对类型错误进行检测
- 那么在开发中,我们如何可以同时保证两个情况都没有问题呢?
事实上TypeScript官方文档有对其进行说明:

- 也就是说我们使用Babel来完成代码的转换,使用tsc来进行类型的检查。
- 但是,如何可以使用tsc来进行类型的检查呢?
- 在这里,在scripts中添加了两个脚本,用于类型检查
"scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack","type-check": "tsc --noEmit","type-check-watch": "npm run type-check -- --watch"},
- 执行 npm run type-check可以对ts代码的类型进行检测

检测完后就会停止
- 执行 npm run type-check-watch可以实时的检测类型错误

会持续监控代码变化,不会停止,需要手动停止。
十一、ESLint
ESLint是一个静态代码分析工具(Static program analysis,在没有任何程序执行的情况下,对代码进行分析)
ESLint可以帮助我们在项目中建立统一的团队代码规范,保持正确、统一的代码风格,提高代码的可读性、可维护性
并且ESLint的规则是可配置的,我们可以自定义属于自己的规则
早期还有一些其他的工具,比如JSLint、JSHint、JSCS等,目前使用最多的是ESLint。
使用ESLint
使用脚手架的话一般都会配置ESLint
如果从零开始进行搭建的话需要
手动安装
npm install eslint -D
新建配置文件,没有配置文件无法进行代码规范检测
npx eslint --init
- 选择想要使用的ESLint配配置:

执行检测命令
npx eslint ./src/main.js
var message = 'Hello TypeScript';var foo = function (msg) {console.log(msg);};foo("qwe");
会报警告

ESLint文件解析
module.exports = {"env": {"browser": true,"commonjs": true,"es2021": true,//我使用了node的全局变量 不配置这个会报错"node": true},"extends": ["eslint:recommended","plugin:vue/essential","plugin:@typescript-eslint/recommended"],"parserOptions": {"ecmaVersion": 13,"parser": "@typescript-eslint/parser"},"plugins": ["vue","@typescript-eslint"],"rules": {//我使用了require导入包 不配置这个会报错'@typescript-eslint/no-var-requires': 0,}};
env:
- 运行的环境,比如是浏览器,并且我们使用es2021(对应的ecmaVersion是12)的语法
extends:
- 可以扩展当前的配置,让其继承自其他的配置信息,可以跟字符串或者数组(多个)
parserOptions:
- 这里可以指定ESMAScript的版本、sourceType的类型
parser:
- 默认情况下是espree(也是一个JS Parser,用于ESLint),但是因为我们需要编译TypeScript,所以需要指定对应的解释器
plugins:
- 指定我们用到的插件
rules:
- 自定义的一些规则
eslint-loader
可以安装loader,在编译打包js文件时自动校验规范
npm i eslint-loader
{test: /\.m?jsx?$/,exclude: /node_modules/,use: ['babel-loader', 'eslint-loader'],},
这样在编译打包js文件时先回进行eslint检测,不符合规范就会报错。
注意:2021.12.15我安装的eslint是v8版本的,这个版本貌似更新了一些东西,可以回退到v7版本,兼容性貌似更好一些
十二、加载Vue文件
编写vue代码
npm i vue
src -> index.js:
import Vue from 'vue';import App from './App.vue';new Vue({ render: (h) => h(App) }).$mount('app');
src -> App.vue
<template><div><h2 class="title">{{ msg }}</h2></div></template><script>export default {data: () => {msg: 'Hello Vue In Webpack';},};</script><style scope lang="less">.title {color: aqua;}</style>
安装依赖
npm install vue-loader -D
npm install vue-loader -D 这个用于解析template模板
配置webpack
// 加载vue必须使用这个插件const VueLoaderPlugin = require('vue-loader/lib/plugin');{test: /\.vue$/,use: ['vue-loader'],},{test: /\.less$/,use: ['style-loader',{loader: 'css-loader',options: {importLoaders: 2,},},'postcss-loader','less-loader',],},plugins: [new VueLoaderPlugin(),],
注意:
- 必须使用VueLoaderPlugin这个插件
- App.vue文件中使用了less,所以需要对less进行配置
