Vinson

Vinson

webpack5(三)

发表于 2023-09-12
Vinson
阅读量 38

5.Babel的深入解析

为什么需要babel?

  • 事实上,在开发中我们很少直接去接触babel,但是babel对于前端开发来说,目前是不可缺少的一部分:
    • 开发中,我们想要使用ES6+的语法,想要使用TypeScript,开发React项目,它们都是离不开Babel的;
      • 所以,学习Babel对于我们理解代码从编写到线上的转变过程直观重要;
      • 了解真相,你才能获得真知的自由!
    • 那么,Babel到底是什么呢?
      • Babel是一个工具链,主要用于旧浏览器或者缓解中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;
      • 包括:语法转换、源代码转换、Polyfill实现目标缓解缺少的功能等;

Babel命令行使用

# 我们希望在命令行尝试使用babel,需要安装如下库
# @babel/core babel的核心代码,必须安装
# @babel/cli 可以让我们在命令行使用babel
npm install @babel/core @babel/cli -D

# 使用babel来处理我们的源代码
# src 是源文件目录
# --out-dir 指定要输出的文件夹dist
npx babel src --out-dir dist
shell

插件的使用

# 比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件
npm install @babel/plugin-transform-arrow-functions -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions

# 查看转换后的结果:我们发现const并没有转成var
# 这是因为plugin-transform-arrow-functions,并没有提供这样的功能
# 我们需要使用plugin-transform-block-scoping来完成这样的功能
npm install @babel/plugin-transform-block-scoping -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-block-scoping
shell

Babel的预设preset

  • 但是如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset):
  • 安装@babel/preset-env预设:npm install @babel/preset-env -D
  • 执行命令:npx babel src --out-dir dist --presets=@babel/preset-env

Babel的底层原理

  • babel是如何做到将我们的一段代码(ES6、TypeScript、React)转成另外一段代码(ES5)的呢?

    • 从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?
    • 就是编译器,事实上我们可以将babel看成就是一个编译器。
    • Babel编译器的作用就是将我们的源代码,转换成浏览器可以直接识别的另外一段源代码;
  • Babel也拥有编译器的工作流程: p解析阶段(Parsing) p转换阶段(Transformation) p生成阶段(Code Generation)

  • https://github.com/jamiebuilds/the-super-tiny-compiler

  • Babel的执行阶段

  • 当然,这只是一个简化版的编译器工具流程,在每个阶段又会有自己具体的工作

  • 在实际开发中,我们通常会在构建工具中通过配置babel来对其进行使用的,比如在webpack中。

  • 那么我们就需要去安装相关的依赖:

    • 如果之前已经安装了@babel/core,那么这里不需要再次安装;
    • npm install babel-loader @babel/core
  • 我们可以设置一个规则,在加载js文件时,使用我们的babel:

指定使用的插件

  • 我们必须指定使用的插件才会生效

babel-preset

  • 如果我们一个个去安装使用插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个 preset,webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel。
  • 比如常见的预设有三个:
    • env
    • react
    • TypeScript
  • 安装preset-env:npm install @babel/preset-env

  • 我们最终打包的JavaScript代码,是需要跑在目标浏览器上的,那么如何告知babel我们的目标浏览器呢?
    • browserslist工具
    • target属性
  • 之前我们项目中已经使用了browserslist工具,我们可以对比一下不同的配置,打包的区别:

设置目标浏览器targets

  • 我们也可以通过targets来进行配置:

  • 那么,如果两个同时配置了,哪一个会生效呢?
    • 配置的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中),我们会经常看到这种设置方式:
    • 它表达的含义是使用对应的 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项目的子包,更加推荐;

认识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 -S

配置babel.config.js

  • 我们需要在babel.config.js文件中进行配置,给preset-env配置一些属性:
    • useBuiltIns:设置以什么样的方式来使用polyfill;
    • corejs:设置corejs的版本,目前使用较多的是3.x的版本,比如我使用的是3.8.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,但是对应的包也会变大;

认识Plugin-transform-runtime(了解)

  • 在前面我们使用的polyfill,默认情况是添加的所有特性都是全局的

    • 如果我们正在编写一个工具库,这个工具库需要使用polyfill;
    • 别人在使用我们工具时,工具库通过polyfill添加的特性,可能会污染它们的代码;
    • 所以,当编写工具时,babel更推荐我们使用一个插件: @babel/plugin-transform-runtime来完成polyfill 的功能;
  • 安装 @babel/plugin-transform-runtime: npm install @babel/plugin-transform-runtime -D

  • 使用plugins来配置babel.config.js:

  • 注意:因为我们使用了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

  • 在项目开发中,我们会使用TypeScript来开发,那么TypeScript代码是需要转换成JavaScript代码。
  • 可以通过TypeScript的compiler来转换成JavaScript: npm install typescript -D
  • 另外TypeScript的编译配置信息我们通常会编写一个tsconfig.json文件: tsc --init
  • 之后我们可以运行 npx tsc来编译自己的ts代码: npx tsc

使用ts-loader

  • 如果我们希望在webpack中使用TypeScript,那么我们可以使用ts-loader来处理ts文件:

npm install ts-loader -D

  • 配置ts-loader:

  • 之后,我们通过npm run build打包即可。

使用babel-loader

  • 除了可以使用TypeScript Compiler来编译TypeScript之外,我们也可以使用Babel:
    • Babel是有对TypeScript进行支持;
    • 我们可以使用插件: @babel/tranform-typescript;
    • 但是更推荐直接使用preset:@babel/preset-typescript;
  • 我们来安装@babel/preset-typescript: npm install @babel/preset-typescript -D

ts-loader和babel-loader选择

  • 那么我们在开发中应该选择ts-loader还是babel-loader呢?

  • 使用ts-loader(TypeScript Compiler)

    • 来直接编译TypeScript,那么只能将ts转换成js;
    • 如果我们还希望在这个过程中添加对应的polyfill,那么ts-loader是无能为力的; p我们需要借助于babel来完成polyfill的填充功能;
  • 使用babel-loader(Babel)

    • 来直接编译TypeScript,也可以将ts转换成js,并且可以实现polyfill的功能;
    • 但是babel-loader在编译的过程中,不会对类型错误进行检测;
  • 那么在开发中,我们如何可以同时保证两个情况都没有问题呢?

  • 事实上TypeScript官方文档有对其进行说明:

  • 也就是说我们使用Babel来完成代码的转换,使用tsc来进行类型的检查。

  • 但是,如何可以使用tsc来进行类型的检查呢?

    • 在这里,我在scripts中添加了两个脚本,用于类型检查;
    • 我们执行 npm run type-check可以对ts代码的类型进行检测;
    • 我们执行 npm run type-check-watch可以实时的检测类型错误;

认识ESLint

  • 什么是ESLint呢?
  • ESLint是一个静态代码分析工具(Static program analysis,在没有任何程序执行的情况下,对代码进行分析);
  • ESLint可以帮助我们在项目中建立统一的团队代码规范,保持正确、统一的代码风格,提高代码的可读性、可维护性;
  • 并且ESLint的规则是可配置的,我们可以自定义属于自己的规则;
  • 早期还有一些其他的工具,比如JSLint、JSHint、JSCS等,目前使用最多的是ESLint。

使用ESLint?

  • 首先我们需要安装ESLint: npm install eslint -D

  • 创建ESLint的配置文件: npx eslint --init

  • 选择想要使用的ESLint:

  • 执行检测命令: npx eslint ./src/main.js

ESLint的文件解析

  • 默认创建的环境如下:
  • env:运行的环境,比如是浏览器,并且我们会使用es2021(对应的ecmaVersion是12)的语法;
  • extends:可以扩展当前的配置,让其继承自其他的配置信息,可以跟字符串或者数组(多个);
  • parserOptions:这里可以指定ESMAScript的版本、sourceType的类型
    • parser:默认情况下是espree(也是一个JS Parser,用于ESLint),但是因为我们需要编译TypeScript,所以需要指定对应的解释器;
  • plugins:指定我们用到的插件;
  • rules:自定义的一些规则;

VSCode的ESLint插件

  • 但是如果每次校验时,都需要执行一次npm run eslint就有点麻烦了,所以我们可以使用一个VSCode的插件: ESLint

  • 一方面我们可以修改代码来修复错误,另外我们可以通过配置规则:

    • 格式是: 配置的规则名称:对应的值值可以是数字、字符串、数组:
      • 字符串对应有三个值: off、warn、error;
      • 数字对应有三个值: 0、1、2(分别对应上面的值);
      • 数组我们可以告知对应的提示以及希望获取到的值:比如 [‘error’, ‘double’]

VSCode的Prettier插件

  • ESLint会帮助我们提示错误(或者警告),但是不会帮助我们自动修复:
    • 在开发中我们希望文件在保存时,可以自动修复这些问题;
    • 我们可以选择使用另外一个工具:prettier;

ESLint-Loader的使用

  • 事实上,我们在编译代码的时候,也希望进行代码的eslint检测,这个时候我们就可以使用eslint-loader来完成了: npm install eslint-loader -D

加载Vue文件

  • 编写vue相关的代码:

Webpack中配置vue加载

  • 安装相关的依赖:

npm install vue-loader vue-template-compiler -D

  • 配置webpack

为什么要搭建本地服务器?

  • 目前我们开发的代码,为了运行需要有两个操作:
    • 操作一:npm run build,编译相关的代码;
    • 操作二:通过live server或者直接通过浏览器,打开index.html代码,查看效果;
  • 这个过程经常操作会影响我们的开发效率,我们希望可以做到,当文件发生变化时,可以自动的完成 编译 和 展示;
  • 为了完成自动编译,webpack提供了几种可选的方式:
    • webpack watch mode;
    • webpack-dev-server;
    • webpack-dev-middleware
  • 接下来,我们一个个来学习一下它们;

Webpack watch

  • webpack给我们提供了watch模式:
    • 在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译;
    • 我们不需要手动去运行 npm run build指令了;
  • 如何开启watch呢?两种方式:
    • 方式一:在导出的配置中,添加 watch: true;
    • 方式二:在启动webpack的命令中,添加 --watch的标识;
  • 这里我们选择方式二,在package.json的 scripts 中添加一个 watch 的脚本:

webpack-dev-server

  • 上面的方式可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的:

    • 当然,目前我们可以在VSCode中使用live-server来完成这样的功能;
    • 但是,我们希望在不适用live-server的情况下,可以具备live reloading(实时重新加载)的功能;
  • 安装webpack-dev-server: npm install webpack-dev-server -D

  • 修改配置文件,告知 dev server,从什么位置查找文件:

  • webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中:

    • 事实上webpack-dev-server使用了一个库叫memfs(memory-fs webpack自己写的)

webpack-dev-middleware

  • webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server
    • webpack-dev-server 在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置;
    • 我们可以搭配一个服务器来使用它,比如express;

npm install express webpack-dev-middleware -D

  • 根目录创建server.js文件

node server.js 运行代码

publicPath

  • Output中有两个很重要的属性:path和publicPath
    • path:用于指定文件的输出路径(比如打包的html、css、js等),是一个绝对路径;
    • publicPath:默认是一个空字符串,它为我们项目中的资源指定一个公共的路径(publicPath);
  • 这个publicPath很不容易理解,其实就是给我们打包的资源,给它一个路径:

资源的路径 = output.publicPath + 打包资源的路径(比如“js/[name].bundle.js”)

  • 比较常设置的是两个值:
    • ./:本地环境下可以使用这个相对路径;
    • /:服务器部署时使用,服务器地址 + /js/[name].bundle.js;
评论
来发一针见血的评论吧!
表情

快来发表评论吧~

推荐文章
  • 测试文章

    20点赞10评论

  • webpack5(一)

    20点赞10评论