- 在开发中我们很少直接去接触babel,但是对于前端开发而言babel是不可缺少的一部分:
- 想要使用ES6+的语法,
- 想要使用TypeScript,
- 开发React项目,
- 它们都是离不开Babel的;
- 所以,学习Babel对于我们理解代码从编写到线上的转变过程至关重要;
- Babel是什么呢?
- Babel是一个工具链,主要用于在旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;
- 包括:语法转换、源代码转换等;
Babel命令行使用
babel本身可以作为一个独立的工具(和postcss一样)来使用的。
- 在命令行尝试使用babel:
- @babel/core:babel的核心代码,必须安装;
- @babel/cli:可以让我们在命令行使用babel;
npm install @babel/cli @babel/core -D
- 使用babel来处理我们的源代码:
- src:是源文件的目录;
- —out-dir:指定要输出的文件夹dist;
- —out-file:指定要输出的文件名称;
npx babel src --out-dir dist
这样写babel时不会帮我们做任何的代码转化的
插件的使用
- 比如需要转换箭头函数,可以使用箭头函数转换相关的插件:
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
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来对其进行使用的,比如在webpack中。
- 那么我们就需要去安装相关的依赖:
npm install babel-loader @babel/core
设置在加载js文件时,使用babel:
{
test: /\.js$/,
loader: 'babel-loader',
},
指定使用的插件
这样才会生效:
module.exports = {
...
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-transform-arrow-functions',
'@babel/plugin-transform-block-scoping',
],
},
},
},
]
}
babel-preset
如果一个个去安装使用插件,需要手动管理大量的babel插件
- 可以直接给webpack提供一个preset,webpack会根据预设来加载对应的插件列表,并且将其传递给babel。
- 比如常见的预设有三个:
- env
- react
- TypeScript
npm install @babel/preset-env
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/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项目的子包,更加推荐;
{
test: /\.js$/,
loader: 'babel-loader',
},
Vue源码的打包 ```javascript import { createApp } from ‘vue’;module.exports = {
presets: ['@babel/preset-env'],
};
const app = createApp({
template: <h2>{{msg}}</h2>
,
data() {
return {
msg: ‘hello cos..’,
};
},
});
app.mount(‘#app’);
这种写法现在是存在问题的,打包可以通过,但是在浏览器中不会显示出渲染的内容,并且在控制台会给出对应的警告信息:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/439030/1646619311760-a5e08615-a49f-4b3c-ae93-f907b2cf1df8.png#clientId=u475321df-d3ca-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=51&id=ua7fd1e8e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=65&originWidth=635&originalType=binary&ratio=1&rotation=0&showTitle=false&size=20932&status=done&style=none&taskId=u73c7b2a8-515e-405c-98af-8d2da200c95&title=&width=500)<br />_组件提供了模板选项,但在这个Vue版本中不支持运行时编译_<br />这其实是因为vue不通版本的差别导致的:
<a name="qFHh5"></a>
## Vue打包后不同版本解析
- vue(.runtime).global(.prod).js:4
- 通过浏览器中的 <script src="..."> 直接使用;
- 通过CDN引入和下载的Vue版本就是这个版本;
- 会暴露一个全局的Vue来使用;
- vue(.runtime).esm-browser(.prod).js:4
- 用于通过原生 ES 模块导入使用 (在浏览器中通过 <script type="module"> 来使用)。
- vue(.runtime).esm-bundler.js:2
- 用于 webpack,rollup 和 parcel 等构建工具;
- 构建工具中默认是vue.runtime.esm-bundler.js;
- 如果需要解析模板template,需要手动指定vue.esm-bundler.js;
- vue.cjs(.prod).js:2
- 服务器端渲染使用;
- 通过require()在Node.js中使用;
<a name="i44Uj"></a>
## 运行时+编译器 vs 仅运行时
- Vue的开发过程中有三种方式来编写DOM元素:
- 方式一:template模板的方式(之前经常使用的方式);
- 方式二:render函数的方式,使用h函数来编写渲染的内容;
- 方式三:通过.vue文件中的template来编写模板;
- 它们的模板分别是如何处理的呢?
- 方式二中的h函数可以直接返回一个虚拟节点,也就是Vnode节点;
- 方式一和方式三的template都需要有特定的代码来对其进行解析:
- 方式三.vue文件中的template可以通过在vue-loader对其进行编译和处理;
- 方式一中的template必须要通过源码中一部分代码来进行编译;
- 所以,Vue在让我们选择版本的时候分为 **运行时+编译器** 或者 **仅运行时**
- 运行时+编译器包含了对template模板的编译代码,更加完整,但是也更大一些;
- 仅运行时没有包含对template版本的编译代码,相对更小一些;
综上,我们应该给vue指定一个带compiler的版本:
```javascript
import { createApp } from 'vue/dist/vue.esm-bundler';
全局标识的配置
目前网页上还有一个警告:
GitHub上也有对应的说明:
https://github.com/vuejs/core/tree/main/packages/vue#bundler-build-feature-flags
- 这是两个特性的标识,
- 一个是使用Vue的Options,
- 一个是Production模式下是否支持devtools工具;
虽然他们都有默认值,但是强烈建议我们手动对他们进行配置;
//配置在define plugin的配置项里面即可:
new DefinePlugin({
BASE_URL: '"./"',
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
}),
Vetur,从Vue2开发就一直在使用的VSCode支持Vue的插件
- Volar,官方推荐的插件(后续会基于Volar开发官方的VSCode插件)
编写App.vue代码
我们新建App.vue文件,并在main.js中引入:
import App from './vue/App.vue';
createApp(App).mount('#app');
此时打包会报错:
我们使用vue-loader
: npm install vue-loader -D
在webpack的模板规则中进行配置:
{
test: /\.vue$/,
loader: 'vue-loader',
},
打包依然报错:VueLoaderPlugin
在plugins里面配置对应的组件:
const { VueLoaderPlugin } = require('vue-loader/dist/index');
new VueLoaderPlugin(),
这时候打包已经成功了,但是在早前的版本中还需要配置 @vue/compiler-sfc 来对template进行解析:npm install @vue/compiler-sfc -D
这时候应该学会阅读报错信息。灵活处理。