本文整理来自深入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';