babel.js:Babel.js 不单单是解决了 ES6 的使用问题,同时它也加入了很多新的功能,支持更多的编译需求。
JavaScript 的编译
面对不同浏览器对 ES6 的支持程度的不一致,有两种处理的办法。一种是编译,一种是 polyfill。这两者没有明显的界限,但是大致的区别是编译会在运行前先将代码转化成低版本的代码,再运行。而 polyfill 则是在运行时判断浏览器是否支持一个功能,只有在不支持的情况下,才使用补丁代码;如果支持,就使用原生的功能。
今天,我们重点说到的就是编译这种方式。
Babel 在过去很长一段时间,提供了用来帮助开发者提前使用下一代的 JavaScript 版本来编写代码的编译工具,它可以将 ES6+ 的代码编译成向下兼容的版本。你可能会问,编译不是浏览器的工作吗?为什么我们说 Babel 是一个编译器呢?
其实 Babel 是一种从源代码到源代码的编译,不是从源代码到机器码的编译,所以为了和浏览器中的 JavaScript 引擎做区分,Babel 也被叫做“转”译器(transcompiler 或 transpiler)。对新特性进行转译,使开发者可以使用JS新特性.
Babel 可以通过 npm install --save-dev @babel/core 来安装,然后直接在代码中导入使用,但是这样的使用会把编译的工作加到终端用户侧,更合理的方式应该是将 Babel 的使用放在开发流程中。
const babel = require("@babel/core");
babel.transformSync("code", optionsObject);
在开发的流程中加入 Babel 的方式如下:
npm install --save-dev @babel/core @babel/cli
./node_modules/.bin/babel src --out-dir lib
之后,我们可以加入预设(preset)。预设的加入有两种方式,一种是将所用的编译插件都一次性安装;另外一种则是只针对特定的插件,比如箭头函数做安装。
npm install --save-dev @babel/preset-env
./node_modules/.bin/babel src --out-dir lib --presets=@babel/env
npm install --save-dev @babel/plugin-transform-arrow-functions
./node_modules/.bin/babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions
假如我们安装了上述的箭头函数插件,那么在代码中,如果我们使用相关的箭头,Babel 在编译的过程中就会将代码转化成 ES5 版本的样式。在 Babel 内部,通过读取一个 .babelrc 配置文件,来判断如何转换 JavaScript 代码。所以,你可以按需创建想要编译的功能,来进行这些预设,也可以全量使用所有插件,来对所有的功能进行相关的转译。比如在下面这个例子中,就是将一个箭头函数转化为和 ES5 版本兼容的代码。
// Babel输入: ES6箭头函数
[1, 2, 3].map(n => n + 1);
// Babel输出: ES5匿名函数
[1, 2, 3].map(function(n) {
return n + 1;
});
尽管现在很多时候,我们已经不太需要转换 JavaScript 的核心语言了,但 Babel 仍然常用于支持 JavaScript 语言的非标准扩展,其中一个就是我们在前面一讲提到的 Flow。
npm install --save-dev @babel/preset-flow
npm install --save-dev @babel/preset-typescript
JavaScript 的打包
JavaScript 做模块化开发...
当时,为了能提前使用这些功能,开发者会使用一个代码打包工具以一个主入口为开始,顺藤摸瓜,通过导入指令树查找程序所依赖的所有模块。然后,打包工具会把所有单独模块的文件合并成一个 JavaScript 代码文件,然后重写导入导出指令,让代码可以以转译后的形式运行。打包后的结果,是一个可以被加载到不支持模块化的浏览器的单一文件。
时至今日,ES6 的模块几乎被所有的主流浏览器支持了,但是开发者却仍然使用代码打包工具,至少在生产发布时还是这样的。这么做的原因是为了将核心功能一次性加载,这样比起一个个模块单独加载,性能更高,并且可以带来更好的用户体验。
Webpack、Rollup和Parcel。
这些打包工具的基础功能基本上都大同小异,它们的区别主要是在配置和易用性上。
Webpack 可以算是这几个工具中最元老级的一个了,并且可以支持比较老的非模块化的库。但同时,它也比较难配置。和它正好相反的是 Parcel,一个零配置的替代方案。而 Rollup 相比 Webpack,更加简约,适合小型项目的开发。
加载优化
比如很多程序都有多个入口。一个有很多页面的 Web 应用,每个页面都有不同的入口。打包工具通常会允许我们基于每个入口或几个入口来创建一个包。
前边分享前端的设计模式提到,一个程序除了可以在初始化时静态加载资源外,也可以使用导入来按需动态加载模块,这样做的好处是可以优化应用初始化的时间。通常支持导入的打包工具可以创建多个导出的包:一个在初始时加载的包,和一个或多个动态加载的包。多个包适用于程序中只有几个 import 调用,并且它们加载的模块没有什么交集。如果动态加载的模块对依赖高度共享的话,那么计算出要生成多少个包就会很难,并且很可能需要手动配置包来进行排序。
tree-shaking
非标模块化插件
打包工具通常有一个支持插件的架构,并且支持导入和打包非 JavaScript 代码的模块。假设你的程序包含一个很大的 JSON 兼容的数据结构,我们可以用代码打包工具来将它配置成一个单独的 JSON 文件,然后通过声明的方式将它导入到程序中。
import widgets from "./app-widget-list.json"
类似的,我们也可以使用打包工具的插件功能,在 JavaScript 中,通过 import 来导入 CSS 文件。不过,这里需要注意的是,导入任何 JS 以外的文件所使用的都是非标准的扩展,并且会让我们的代码对打包工具产生一定程度上的依赖。
更新加载
在像 JavaScript 这种在执行前不需要预先编译打包的语言里,运行一个打包工具的感觉像是一个预先编译的过程,每次写完代码,都需要打包一次才能在浏览器中执行,这个步骤对于有些开发者而言,可能感觉比较繁琐。
为了解决这个问题,打包工具通常支持文件系统以观察者的模式来侦测项目目录中文件的改动,并且基于改动来自动重新生成所需的包。通过这个功能,你通常可以在保存编辑过的文件后,在不需要手动再次打包的情况下,及时地刷新。有些打包工具还会支持针对开发者的“热更新”选项。每次重新打包的时候,会自动加载到浏览器。
源代码排查
和 Babel 等编译工具类似,打包工具通常也会生成一个源代码和打包后代码的映射文件。这样做的目的,同样是帮助浏览器开发者工具在报错的时候,可以自动找到问题在源文件中的所在位置。
此文章为2月Day17学习笔记,内容来源于极客时间《Jvascript进阶实战课》,大家共同进步💪💪
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。