前端进阶(四)-前端工程化

前端进阶(四)-前端工程化

  前端工程化还包括bable

Babel

Babel主要做的就是这几点:

  • 语法转换:将新的语法转换成兼容性更好的旧语法。
  • 特性垫片:通过Polyfill 实现目标环境中缺少的特性,将环境的特性差异垫平,说白了还是做兼容。

比如下面这段代码

1
2
3
4
5
6
7
// Babel Input: ES2015 arrow function
[1, 2, 3].map((n) => n + 1);

// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
return n + 1;
});

babel的使用场景主要分为两种:命令行和构建工具。

babel为命令行提供了@babel/cli工具,支持在命令行直接执行babel命令。

1
npm install --save-dev @babel/core @babel/cli

安装好在项目中执行babel命令就可以进行转换了。

1
babel src --out-dir lib # 将src下的文件处理后输出到lib

在构建工具中使用是我们使用babel更常用的方式。

以webpack为例,babel提供了一个loader:babel-loader,使用这个loader我们就能很轻松实现代码转换。

1
2
3
4
5
6
7
8
9
10
11
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
// 配置项
}
}
]
}

bable的运行方式:

Babel的运行过程分为三个阶段:解析 — 转换 — 生成

  1. 解析:解析代码,并生成AST抽象语法树
  2. 转换:将上一步的AST转换成目标环境支持的AST
  3. 生成:根据转换后的AST生成目标代码

bable配置

babel7支持多种方式来进行配置:

除了json文件之外,babel还支持其他格式的配置文件,比如 .js.cjs(Commonjs)和.mjs(ESModule),它们相对于json格式更加灵活,可以在配置中进行编程处理,但是会使配置无法静态分析,失去可缓存性。

1
2
3
4
5
// babel.config.js
module.exports = {
presets: [],
plugins: []
};

babel虽然有多种方式来写配置文件,但是配置的内容都是类似的,主要包含pluginspresets两部分,不过这里需要注意几点:

  • 配置格式:plugins和presets配置项均为数组,如果每项不需要配置的话,直接放入数组(值为项的字符串名称);如果要配置的话,则需要把格式改成数组,该数组的第一个元素是该项的字符串名字,第二个元素是该项的配置对象。这里填写的plugins和presets,babel会自动检查是否已被安装在node_modules下面,当然,也可以使用相对路径来使用本地文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    "plugins": [
    [
    "component",
    {
    "libraryName": "element-ui",
    "styleLibraryName": "theme-chalk"
    }
    ],
    "@babel/plugin-transform-runtime"
    ]
  • 执行顺序:

    plugins会从前到后执行,presets会从后向前执行,且plugins会比presets先执行

    presets逆序执行的目的是为了保证向后兼容,我们在配置的时候按照规范的时间顺序列出即可,如['es2015', 'stage-1'],这样就会先使用stage-1,否则就会出现错误。

  • 短名称:在配置plugin和preset名字的时候,可以省略名字中的babel-plugin-preset-字样,仅使用插件的名字就可以了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "plugins": [
    "myPlugin", // 完整名字:"babel-plugin-myPlugin"
    "@org/myPlugin" // 完整名字:"@org/babel-plugin-myPlugin"
    ],
    "presets":[
    "myPreset", // 完整名字:"babel-preset-myPreset"
    "@babel/myPreset" // 完整名字:@babel/preset-myPreset
    ]
    }

plugin

babel的转换完全依靠插件,babel拥有的能力完全取决于给它配置了哪些插件。

babel的插件分为两种:

  • 语法插件:赋予babel解析特定语法的能力,可以理解一个为babel服务的翻译官,仅做翻译解析工作,工作在AST转换阶段。

  • 转译插件:转译插件即把特定的语法转换成指定标准的语法。

    比如将箭头函数 (x) => x 转换成 function (x) {return x},只需要配置箭头函数转译插件即可。

插件的使用大概分为两步:

  1. 将插件添加到配置文件的plugins属性中,可根据插件文档做相应配置。
  2. 安装插件到项目中,使用npm或者yarn均可

每个插件的功能是单一的,所以一般转换需求要使用的插件数量比较多,这时候可以考虑presets

编写插件

和webpack一样,bable可以使用自定义插件实现功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = function() {
return {
visitor: {
Identifier(path) {
const name = path.node.name;
// reverse the name: JavaScript -> tpircSavaJ
path.node.name = name
.split("")
.reverse()
.join("");
},
},
};
}

preset

在实际的开发中,我们如果用ES6来进行开发的话,那需要转换的语法是非常多的,难道要一个一个去配置吗?还要不要配陪妹子了?

这个时候,就该presets登场了。presets可以理解为一组插件的集合,就像你去德克士直接点一个全家桶,而不必一个一个给服务员说要鸡腿、鸡翅、薯条…(如果想和服务员小姐姐多待一会,也不是不可以)

  • 配置文件:通过配置文件的targets属性指定目标环境

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "presets": [
    ["env", {
    "targets": {
    "browsers": ["last 2 versions", "safari >= 7"],
    "node": "12.10"
    }
    }]
    ]
    }

    browserslistrc:如果是浏览器或者Electron,官方推荐使用.browserslistrc

    文件来指定目标环境(可以和autoprefixer、stylelint等其他工具共享配置)。

    不过如果在配置文件中设置了 targetsignoreBrowserslistConfig.browserslistrc中配置的内容将不会生效。

presets中的其他配置:

  • modules: 用于指定转换后的模块规范,可设置为"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false,默认为"auto"

  • useBuiltIns:此选项配置如何@babel/preset-env处理polyfill,在后面介绍polyfill我们会详细介绍。

  • corejs:用于指定corejs的版本,仅当使用了useBuiltIns: usage或者

    useBuiltIns: entry此配置才有效,使用时一定要配置正确的版本,否则会报错。

    当前默认的版本是2.0,不过建议使用3.0版本,因为2.0已经不会再添加新的特性了,如果使用了一些较新的语法,将会无法转换。

@babel/preset-env

这个是我们最常用的,也是官方现在推荐的preset,它是一个智能预设,我们无需再关心特定环境下所需的转换插件(只需要指定目标环境),就可以使用最新的语法。除了能够减少使用难度,还能够使转换出来的代码体积更小。

preset-env会根据配置的目标环境中缺少的功能,选择对应的插件进行必要的转换和polyfill,而目标环境支持的特性就不会转换,而不是无脑的一把梭哈。

polyfill

前面我们的配置其实是不完美的,转换出来的代码直接在浏览器运行是可能会出问题的。这是因为babel默认只转换了语法,而对于一些新的API是并没有处理的,如Generator、Set、Maps、Proxy、Promise 等全局对象及其方法都不会转码,如果我们在代码中使用到了就会报错。

此时,我们就需要做Polyfill,也就是所谓的’垫片’,作用就是把浏览器的差异垫平,人话就是通过模拟为这些浏览器补全缺失的全局对象及API。

常用插件

@babel/plugin-transform-runtime 是一个可重复使用Babel注入的帮助程序的插件,避免重复注入,以节省代码大小。通常还要搭配@babel/runtime一起使用。

安装

1
2
npm install --save-dev @babel/plugin-transform-runtime 
npm install --save @babel/runtime # 提供缺失的特性,须要在生产环境中安装

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [["@babel/plugin-transform-runtime"]]
}

Babel-plugin-import

动态引入插件,从而不必全局引入antd、lodash等

安装

1
npm install babel-plugin-import --save-dev

在babelrc中配置

1
2
3
4
'plugins': [
['import',{'libraryName': 'antd', 'style': true,'libraryDirectory': 'lib'},'antd']
['import',{'libraryName': 'antd-mobile', 'libraryDirectory': 'lib'},'antd-mobile']
]

SWC

swc是一个类似babel的JavaScript/TypeScript编译器,但是由于swc使用Rust实现,所以具备比babel出色的性能。

webpack与bable的性能瓶颈都在于JS语言,出现了go实现的esbuild与Rus实现的swc等工具,esbuild想要开辟一个构建工具性能的新时代,创建一个易用的现代打包器,swc的目标则是是替代babel,在官方文档的Comparison with babel这个单元,我们可以看到swc团队将相当一部分bable的插件重写了以增强swc的能力。

对比 babel,swc 有至少 10 倍以上的性能优势

同时swc也提供了一个构建打包工具spack,这个工具并不是一个全面的构建工具,但是我们可以通过这个工具简单的体验一下swc的能力

我们在简单的工具库编写时,可以使用swc提供的spack命令来快速打包出commonjs, es6, amd, umd

安装/使用

安装

1
2
npm i -D @swc/core @swc/cli
## yarn add --dev @swc/core @swc/cli

在项目的根目录创建一个 .swcrc 文件,配置信息如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[
{
"jsc": { // 编译规则
"target": "es5", // 输出js的规范
"parser": {
// 除了 ecmascript,还支持 typescript
"syntax": "ecmascript",
// 是否解析jsx,对应插件 @babel/plugin-transform-react-jsx
"jsx": false,
// 是否支持装饰器,对应插件 @babel/plugin-syntax-decorators
"decorators": false,
// 是否支持动态导入,对应插件 @babel/plugin-syntax-dynamic-import
"dynamicImport": false,
// ……
// babel 的大部分插件都能在这里找到对应配置
},
"minify": {}, // 压缩相关配置,需要先开启压缩
},
"env": {
"targets": {
"chrome": "58",
"ie": "11"
}
}
"minify": true // 是否开启压缩
}
]

配置spack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const { config } = require("@swc/core/spack");

module.exports = config({
//入口文件
entry: __dirname + "/src/index.ts",
//输出
output: {
path: __dirname + "/dist",
},
// swc编译配置
options:{
jsc: {
//解析配置
parser: {
syntax: "typescript", //输入文件格式
tsx: false, // 是否支持tsx
dynamicImport: false, //是否支持动态导入
decorators: false, //是否支持装饰器
},
transform: null,
target: "es5", //转译目标
loose: false,
externalHelpers: false,
keepClassNames: false
},
// 输出文件配置
module: {
type: "commonjs",
strict: false,
strictMode: true,
lazy: false,
noInterop: false,
ignoreDynamic: false
}
}
});

在package.json中增加

1
2
3
"scripts": {
"build": "spack"
}

与webpack一起使用

SWC 也提供了 Webpack Loder,可以和 Webpack 一起使用

安装 @swc/coreswc-loader

1
cnpm install --save-dev @swc/core swc-loader

安装完成后在 Webpack 的配置文件中添加 SWC 的 Loder 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const path = require('path');

module.exports = {
// 入口文件
entry: './src/main.js',
output: {
// 打包完成后输出的目录
path: path.resolve(__dirname, 'dist'),
// 打包完成后的 JS 文件名
filename: 'index.js',
},
module: {
rules: [
// SWC 的 Loder 配置
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'swc-loader',
},
},
],
},
};

SWC 的配置还是和上面一样的使用 .swcrc 的配置

运行打包后 swc-loder 就会调用 swc-core 转码

SWC 和 Webpack 一起使用也还是不能转换 ES6 中新增的 Promise 之类的功能,所以还需要使用 polyfill 或 core-js

因为 core-js 可以按需引入,而 polyfill 需要配合 Babel 才能实现按需引入,所以这里就使用 core-js

1
cnpm install --save-dev core-js

安装完成后在 main.js 中引入需要的库,比如我用到了 Promise ,我就需要引入 core-js 的 Promise 转换库

1
import 'core-js/features/promise';

core-js 的转换模块就在 core-js/features 目录中

打包完成后就可以在 IE 浏览器中使用 Promise 之类的功能了

Rome

Rome是 Babel 作者做的基于 Nodejs 的前端基建全家桶,包含但不限于 Babel, ESLint, webpack, Prettier, Jest。

rome 是个全家桶 API,所以你只需要 yarn add rome 就完成了所有环境准备工作。

  • rome bundle 打包项目。
  • rome compile 编译单个文件。
  • rome develop 调试项目。
  • rome parse 解析文件抽象语法树。
  • rome analyzeDependencies 分析依赖。

Rome 还将文件格式化与 Lint 合并为了 rome check 命令,并提供了友好 UI 终端提示

评论

You forgot to set the app_id or app_key for Valine. Please set it in _config.yml.

 

本文章阅读量:

  0

IT学徒、技术民工、斜杠青年

机器人爱好者、摄影爱好者

PS、PR、LR、达芬奇潜在学习者

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×