1. babel的作用
-
babel对于前端开发来说,目前是不可缺少的一部分:
- 开发中,我们想要使用ES6+的语法,想要使用TypeScript,开发React项目,它们都是离不开Babel的
-
那么,Babel到底是什么呢?
- Babel是一个工具链,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript
- 包括:语法转换、源代码转换、Polyfill实现目标环境缺少的功能等
2. babel命令行
-
babel本身可以作为一个独立的工具(和postcss一样),不和webpack等构建工具配置来单独使用
-
如果希望在命令行尝试使用babel,需要安装如下库:
- @babel/core:babel的核心代码,必须安装
- @babel/cli:可以让我们在命令行使用babel
npm install @babel/cli @babel/core
-
使用babel来处理我们的源代码:
- src:是源文件的目录
- --out-dir:指定要输出的文件夹build
npx babel ./src --out-dir ./build
-
转换块级作用域的插件:
npm install @babel/plugin-transform-block-scoping -D
- 终端命令:
npx babel src --out-dir build --plugins=@babel/plugin-transform-block-scoping
-
转换箭头函数相关的插件:
npm install @babel/plugin-transform-arrow-functions -D
- 终端命令:
npx babel src --out-dir build --plugins=@babel/plugin-transform-arrow-functions
-
预设(预设的含义后续补充)的使用:不需要安装一个个单独的插件
npm install @babel/preset-env -D
npx babel src --out-dir build --presets=@babel/preset-env
3. babel底层原理
-
babel是如何做到将我们的一段代码(ES6、TypeScript、React)转成另外一段代码(ES5)的呢?
- 从一种源代码(原生语言)转换成另一种源代码(目标语言),这是编译器的工作
- 事实上我们可以将babel看成就是一个编译器
- Babel编译器的作用就是将我们的源代码,转换成浏览器可以直接识别的另外一段源代码
-
Babel也拥有编译器的工作流程:
- 解析阶段(Parsing)
- 转换阶段(Transformation)
- 生成阶段(Code Generation)
-
babel编译器执行原理
4. webpack和babel的结合
4.1 webpack中的使用
-
安装webpack:
npm install webpack webpack-cli -D
-
编写webpack配置文件
const path = require('path') module.exports = { mode: 'development', devtool: false, entry: './src/index.js', output: { clean: true, filename: 'bundle.js', path: path.resolve(__dirname, './build') }, module: { rules: [ { test: /\.m?js$/, use: { loader: 'babel-loader', options: { // 应用插件 // plugins: [ // '@babel/plugin-transform-arrow-functions', // '@babel/plugin-transform-block-scoping' // ] presets: [ '@babel/preset-env' ] } } } ] } }
4.2 babel-preset
-
使用过程中如果一个个去安装使用的插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个preset,webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel。
-
常见的预设有三个:
- env
- react
- TypeScript
5. 浏览器兼容性问题
5.1 兼容性问题
-
开发中,浏览器的兼容性问题,应该如何去解决和处理?
- 这里指的兼容性是针对不同的浏览器支持的特性:比如css特性、js语法之间的兼容性
-
市面上有大量的浏览器:
- 有Chrome、Safari、IE、Edge、Chrome for Android、UC Browser、QQ Browser等等
- 它们的市场占率是多少?我们要不要兼容它们呢?
-
其实在很多的脚手架配置中,都能看到类似于这样的配置信息:
- 这里的
> 1%
就是占有率
> 1% last 2 versions not dead
- 这里的
5.2 浏览器的市场占有率
-
查询到浏览器的市场占有率的方法
- 通常会查询的一个网站就是 caniuse
5.3 认识Browserslist工具
-
怎么在css兼容性和js兼容性下共享配置的兼容性条件呢?
- 就是当我们设置了一个条件:
> 1%
; - 表达的意思是css要兼容市场占有率大于1%的浏览器,js也要兼容市场占有率大于1%的浏览器;
- 如何通过工具来达到这种兼容性的,比如
postcss-preset-env
、babel
、autoprefixer
等
- 就是当我们设置了一个条件:
-
Browserslist
是一个在不同的前端工具之间,共享目标浏览器和Node.js版本的配置:- Autoprefixer
- Babel
- postcss-preset-env
- eslint-plugin-compat
- stylelint-no-unsupported-browser-features
- postcss-normalize
- obsolete-webpack-plugin
-
编写规则
-
defaults
:Browserslist的默认浏览器(> 0.5%, last 2 versions, Firefox ESR, not dead) -
5%
:通过全局使用情况统计信息选择的浏览器版本。 >=,<和<=工作过。5% in US
:使用美国使用情况统计信息。它接受两个字母的国家/地区代码。> 5% in alt-AS
:使用亚洲地区使用情况统计信息。有关所有区域代码的列表,请参见caniuse-lite/data/regions> 5% in my stats
:使用自定义用法数据。> 5% in browserslist-config-mycompany stats
:使用 来自的自定义使用情况数据browserslist-config-mycompany/browserslist-stats.json。cover 99.5%
:提供覆盖率的最受欢迎的浏览器。cover 99.5% in US
:与上述相同,但国家/地区代码由两个字母组成。cover 99.5% in my stats
:使用自定义用法数据。
-
dead
:24个月内没有官方支持或更新的浏览器。- 现在是IE 10,IE_Mob 11,BlackBerry 10,BlackBerry 7, Samsung 4和OperaMobile 12.1
-
last 2 versions
:每个浏览器的最后2个版本- last 2 Chrome versions:最近2个版本的Chrome浏览器
- last 2 major versions或last 2 iOS major versions:最近2个主要版本的所有次要/补丁版本
-
5.4 Browserslist工具使用
-
命令行使用
- 通过命令来查询某些条件所匹配到的浏览器
npx browserslist ">1%, last 2 version, not dead"
- 通过命令来查询某些条件所匹配到的浏览器
-
配置browserslist
-
方案一:在package.json中配置
-
方案二:单独的一个配置文件
.browserslistrc
文件
-
-
默认值
-
编写多个条件
6. babel配置浏览器兼容性
-
最终打包的JavaScript代码,是需要跑在目标浏览器上的,那么如何告知babel我们的目标浏览器呢?
- browserslist工具
- targets属性
const path = require('path') module.exports = { // ... module: { rules: [ { test: /\.m?js$/, use: { loader: 'babel-loader', options: { // 应用插件 // plugins: [ // '@babel/plugin-transform-arrow-functions', // '@babel/plugin-transform-block-scoping' // ] // presets: [ // '@babel/preset-env' // ] presets: [ // 在开发中针对babel的浏览器兼容查询使用browserslist工具, 而不是设置target // 因为browserslist工具, 可以在多个前端工具之间进行共享浏览器兼容性(postcss/babel) ['@babel/preset-env', { targets: '>5%' }] ] } } } ] } }
-
如果两个同时配置了,哪一个会生效呢?
- 配置的targets属性会覆盖browserslist;
- 但是在开发中,更推荐通过browserslist来配置,因为类似于postcss工具,也会使用browserslist,进行统一浏览器的适配
7. 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项目的子包,更加推荐
-
配置文件编写
module.exports = { // plugins: [ // '@babel/plugin-transform-arrow-functions', // '@babel/plugin-transform-block-scoping' // ] // presets: [ // '@babel/preset-env' // ] presets: [ ['@babel/preset-env', { // targets: '>5%' }] ] }
8. babel和polyfill使用
8.1 认识polyfill
-
Polyfill是什么呢?
- 翻译:一种用于衣物、床具等的聚酯填充材料, 使这些物品更加温暖舒适
- 理解:更像是应该填充物(垫片),一个补丁,可以帮助我们更好的使用JavaScript
-
为什么时候会用到polyfill呢?
- 比如我们使用了一些语法特性(例如:
Promise
,Generator
,Symbol
等以及实例方法例如Array.prototype.includes
等) - 但是某些浏览器压根不认识这些特性,必然会报错
- 我们可以使用polyfill来填充或者说打一个补丁,那么就会包含该特性了
- 比如我们使用了一些语法特性(例如:
8.2 使用polyfill
-
babel7.4.0
之前,可以使用 @babel/polyfill的包,但是该包现在已经不推荐使用了 -
babel7.4.0
之后,可以通过单独引入core-js和regenerator-runtime来完成polyfill的使用npm install core-js regenerator-runtime --save
8.3 结合babel使用
-
在babel.config.js文件中进行配置,给preset-env配置一些属性:
- useBuiltIns:设置以什么样的方式来使用polyfill
- false
- 打包后的文件不使用polyfill来进行适配
- 并且此时是不需要设置corejs属性的
- usage
- 会根据源代码中出现的语言特性,自动检测所需要的polyfill
- 这样可以确保最终包里的polyfill数量的最小化,打包的包相对会小一些
- 可以设置corejs属性来确定使用的corejs的版本
- entry
- 如果我们依赖的某一个库本身使用了某些polyfill的特性,但是因为我们使用的是usage,之后用户浏览器可能会报错;
- 要想解决这种报错,可以使用 entry;
- 并且需要在入口文件中添加
import 'core-js/stable'; import 'regenerator-runtime/runtime'
; - 这样做会根据 browserslist 目标导入所有的polyfill,但是对应的包也会变大
import 'core-js/stable' import 'regenerator-runtime/runtime'
- false
- corejs:设置corejs的版本,目前使用较多的是3.x的版本,本文使用的是
3.26.1
- 另外corejs可以设置是否对提议阶段的特性进行支持
- 设置 proposals属性为true即可
// babel.config.js module.exports = { presets: [ ['@babel/preset-env', { // targets: '>5%' corejs: 3, // // false: 不使用polyfill进行填充 // useBuiltIns: 'usage' // 开发中推荐使用usage useBuiltIns: 'entry' // 注意在入口文件处引入 }] ] }
- useBuiltIns:设置以什么样的方式来使用polyfill
9. babel对react的支持
-
在编写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
// babel.config.js module.exports = { presets: [ ['@babel/preset-env', { // targets: '>5%' // corejs: 3, // // // false: 不使用polyfill进行填充 // // useBuiltIns: 'usage' // useBuiltIns: 'entry' }], ['@babel/preset-react'] ] }
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', devtool: false, // 不生成source-map entry: './src/index.js', output: { clean: true, filename: 'bundle.js', path: path.resolve(__dirname, './build') }, module: { rules: [ { test: /\.m?jsx?$/, use: { loader: 'babel-loader', } } ] }, plugins: [ new HtmlWebpackPlugin({ template: './index.html' }) ] }
10. webpack对typescript支持
10.1 ts-loader
-
安装
npm install ts-loader -D
npm install typescript -D
-
编写一个tsconfig.json文件
tsc --init
-
配置webpack
-
执行
npm run build
进行打包
10.2 babel-loader
-
Babel是有对TypeScript进行支持;
- 可以使用插件:
@babel/tranform-typescript
- 更推荐直接使用preset:
@babel/preset-typescript
- 可以使用插件:
-
安装@babel/preset-typescript:
npm install @babel/preset-typescript -D
-
配置babel
// babel.config.js module.exports = { presets: [ ['@babel/preset-env'], ['@babel/preset-react'], ['@babel/preset-typescript'] ] }
10.3 两者如何选择
-
使用ts-loader(TypeScript Compiler)
- 来直接编译TypeScript,那么只能将ts转换成js
- 如果我们还希望在这个过程中添加对应的polyfill,那么ts-loader是无能为力的
- 这时需要借助于babel来完成polyfill的填充功能
-
使用babel-loader(Babel)
- 来直接编译TypeScript,也可以将ts转换成js,并且可以实现polyfill的功能
- 但是babel-loader在编译的过程中,不会对类型错误进行检测
-
结合使用:
-
使用Babel来完成代码的转换,使用tsc来进行类型的检查
-
打包代码使用babel-loader
-
开发阶段 执行
tsc --noEmit --watch
监听类型错误,纠正错误后再进行打包
-