Webpack-Babel

本文整理来自深入Vue3+TypeScript技术栈-coderwhy大神新课,只作为个人笔记记录使用,请大家多支持王红元老师。

为什么需要babel?

事实上,在开发中我们很少直接去接触babel,但是babel对于前端开发来说,目前是不可缺少的一部分。开发中,我们想要使用ES6+的语法,想要使用TypeScript,开发React项目,它们都是离不开Babel的。所以,学习Babel对于我们理解代码从编写到线上的转变过程至关重要。

那么,Babel到底是什么呢?
Babel是一个工具链,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript,包括:语法转换、源代码转换等。

Babel命令行使用

babel和postcss一样,是一个独立的工具,不和webpack等构建工具一块也可以单独使用。

如果我们希望在命令行中尝试使用babel,需要安装如下库:

  • @babel/core:babel的核心代码,必须安装;
  • @babel/cli:可以让我们在命令行使用babel;
npm install @babel/cli @babel/core -D

首先我们写如下ES6的代码:

const message = "Hello World"; //ES6的const
const names = ["abc", "cba", "nba"];
names.forEach(item => console.log(item));//ES6的箭头函数

然后使用babel来处理我们的源代码:

  • src:是源文件的目录,或者单个文件;
  • --out-dir:指定要输出的文件夹dist;
npx babel src --out-dir dist

执行上面的命令后发现,依然还是ES6的箭头函数语法,这是因为我们没安装必要的插件。

插件的使用

比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件。

先安装:

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-block-scoping
,@babel/plugin-transform-arrow-functions

转换后的ES5代码如下:

"use strict";

var message = "Hello World";
var names = ["abc", "cba", "nba"];
names.forEach(function (item) {
  return console.log(item);
});

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也拥有编译器的工作流程:

  • 解析阶段(Parsing)
  • 转换阶段(Transformation)
  • 生成阶段(Code Generation)

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

Babel编译器执行原理

Babel的执行阶段:

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

babel-loader

在实际开发中,我们通常会在构建工具中通过配置babel-loader 来对其进行使用,比如在webpack中。我们需要去安装相关的依赖,如果之前已经安装了@babel/core,那么这里不需要再次安装。

npm install babel-loader @babel/core

我们可以设置一个规则,在加载js文件时,使用我们的babel-loader :

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

babel-preset

如果我们一个个去安装使用插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个preset,webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel。

比如常见的预设有三个:

  • env
  • react
  • TypeScript

安装preset-env:

npm install @babel/preset-env

Babel的配置文件

像之前postcss.config.js一样,我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写。

它们两个有什么区别呢?
目前很多的项目都采用了多包管理的方式(babel本身、element-plus、umi等);

  • .babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件:早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的。
  • babel.config.js(或者.json,.cjs,.mjs)文件:可以直接作用于Monorepos项目的子包,更加推荐。babel.config.js里面代码如下:

单独配置好之后,webpack.config.js里面的rules我们就写的更简单了,如下:

// {
//   test: /\.js$/,
//   use: {
//     loader: "babel-loader",
//     options: {
//       // plugins: [
//       //   "@babel/plugin-transform-arrow-functions",
//       //   "@babel/plugin-transform-block-scoping",
//       // ]
//       presets: [
//         "@babel/preset-env"
//       ]
//     }
//   }
// }
{
  test: /\.js$/,
  loader: "babel-loader"
}

Vue源码的打包

前面我们讲了,安装和使用Vue有四种方式:

方式一:在页面中通过CDN的方式来引入
方式二:下载Vue的JavaScript文件,并且自己手动引入
方式三:通过npm包管理工具安装使用它(webpack再讲)
方式四:直接通过Vue CLI创建项目,并且使用它

前两种方式我们讲过了,现在讲第三种方式,先安装vue:

//如果当前默认版本是vue3,直接安装就可以
npm install vue
//如果当前默认版本是vue2,需要加@next,也就是下一个版本的意思
npm install vue@next 
//不需要加-D,因为无论是开发还是上线我们都需要,默认是-S,运行依赖

和前两种方式不一样,现在vue就相当于一个模块,所以我们通过:
import { createApp } from 'vue';引入,代码中的vue就相当于一个模块。

在index.html里面编写模板:

<template id="my-app">
  <h2>我是Vue渲染出来的</h2>
  <h2>{{title}}</h2>
</template>

在main.js里面编写Vue相关的代码,指定模板。

界面上是没有效果的,并且我们查看运行的控制台,会发现如下的警告信息:

为什么报这个警告?

因为vue有两个版本:runtime+complier和runtime-only,默认我们上面导入的就是runtime-only,runtime-only是无法对template进行解析的。因为上面我们用到了template,所以只能使用runtime+complier版本的vue。

其实不止上面两个版本,打开node_modules/vue/dist,可以发现有很多版本:

下面讲一下不同版本的区别:

Vue打包后不同版本解析

  • vue(.runtime).global(.prod).js
    通过浏览器中的 <script src=“...”> 直接引用的就是这个版本,我们之前通过CDN引入和下载的Vue版本也是这个版本。
    会暴露一个全局的Vue来使用。

上面的(.runtime)代表的是runtime-only的版本,如果不需要对template进行编译,就可以使用vue.runtime.global.js版本。(.prod)代表是否用生产版本,生产版本的代码是压缩的。

  • vue(.runtime).esm-browser(.prod).js
    当我们使用浏览器引入,可以使用上面那个版本,如果你在引入的时候加了 <script type="module">,代表我们的script里面写的是ESModule代码,这时候就可以使用这个版本了。

  • vue(.runtime).esm-bundler.js
    用于 webpack,rollup 和 parcel 等构建工具,构建工具中默认是使用vue.runtime.esm-bundler.js。
    如果我们需要解析模板template,那么需要手动指定vue.esm-bundler.js。

所以,上面的警告,我们指定版本后:

import { createApp } from 'vue/dist/vue.esm-bundler';

这样就没警告了,并且template也能正常被解析。

  • vue.cjs(.prod).js
    服务器端渲染使用;
    通过require()在Node.js中使用;

runtime+complier VS runtime-only

在Vue的开发过程中我们有三种方式来编写DOM元素:
方式一:template模板的方式(之前经常使用的方式);
方式二:通过.vue文件中的template来编写模板;
方式三:render函数的方式,使用h函数来编写渲染的内容;

它们的模板分别是如何处理的呢?

  • 方式一中的template我们必须要通过源码中一部分代码来进行编译;
  • 方式二的.vue文件中的template可以通过在vue-loader对其进行编译和处理;
  • 方式三中的h函数可以直接返回一个虚拟节点,也就是Vnode节点;

所以,Vue在让我们选择版本的时候分为:runtime+complier和runtime-only

  • runtime+complier包含了对template模板的编译代码,更加完整,但是也更大一些;
  • runtime-only没有包含对template版本的编译代码,相对更小一些;

VSCode对SFC文件的支持

上面的代码,模板是在index.html里面,在main.js里面指定模板和编写模板相关的数据和逻辑,这样它们就分开了,很麻烦。我们理想的是模板、数据、逻辑、样式都在一个文件里面,其他地方使用的时候就直接导入就好了,这就需要使用SFC单文件组件了。真实开发中多数情况下我们都是使用SFC( single-file components (单文件组件) )。

默认情况下,VSCode对.vue文件是没有代码高亮的,我们需要安装如下插件:

  • 插件一:Vetur,从Vue2开发就一直在使用的VSCode支持Vue的插件;
  • 插件二:Volar,官方推荐的插件(后续会基于Volar开发官方的VSCode插件 );

编写App.vue代码

接下来我们编写自己的App.vue代码:

下面就可以在main.js里面直接使用了:

App.vue的打包过程

我们对代码打包会报错,因为我们需要合适的Loader来处理.vue文件

这个时候我们需要使用vue-loader,默认情况下vue-loader是用来处理vue2的代码的,我们写的是vue3代码,所以加个@next。

npm install vue-loader@next -D

在webpack的模板规则中进行配置:

@vue/compiler-sfc

打包依然会报错,这是因为我们必须添加@vue/compiler-sfc来对template进行解析:

npm install @vue/compiler-sfc -D

另外我们需要配置对应的Vue插件,在webpack.config.js里面引入插件:

然后在下面进行使用:

重新打包即可支持App.vue的写法。

另外,我们也可以通过编写其他的.vue文件来编写自己的组件,然后在我们的APP.vue里面局部注册之后就可以使用了:

<template>
  <h2>我是Vue渲染出来的</h2>
  <h2>{{title}}</h2>
  <!-- 3. 使用组件 -->
  <hello-world></hello-world>
</template>

<script>
  // 1. 引入组件
  import HelloWorld from './HelloWorld.vue';

  export default {
    //2. 注册局部组件
    components: {
      HelloWorld
    },
    data() {
      return {
        title: "Hello World",
        message: "哈哈哈"
      }
    },
    methods: {

    }
  }
</script>

<style scoped>
  h2 {
    color: red;
  }
</style>

全局标识的配置

我们会发现控制台还有另外的一个警告:

在GitHub上的文档中我们可以找到说明:

这是两个特性的标识:

  • 一个是是否支持使用Vue2的OptionsAPI,默认是true。如果设置成false,那么就会把这些代码从源代码里面删除,这样我们的代码体积就会小一点。
  • 一个是Production模式下是否支持devtools工具,默认false。
  • 虽然他们都有默认值,但是强烈建议我们手动对他们进行配置。

配置的地方就是我们配置BASE_URL的地方:

new DefinePlugin({
  BASE_URL: "'./'",
  __VUE_OPTIONS_API__: true,
  __VUE_PROD_DEVTOOLS__: false
}),

配置完后重新打包就没警告了。

因为我们现在是使用SFC单文件组件了,.vue文件里面的template会被vue-loader解析(内部使用@vue/compiler-sfc来解析),那么我们导入的时候就不需要导入vue.esm-bundler.js了,直接导入vue即可,因为webpack,rollup 和 parcel 等构建工具,构建工具中默认是使用vue.runtime.esm-bundler.js。

// import { createApp } from 'vue/dist/vue.esm-bundler';
import { createApp } from 'vue';
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容