swc初体验
这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战
swc 是什么
在开始说swc
之前,我们先来了解一下Rust
这门编程语言。
Rust
是一种快速、可靠、内存高效的编程语言。它是 C++
或 C
等语言的现代替代品。
Rust
与其他语言最大的不同就是在内存管理方面建立了一种新的概念叫做所有权。
可能这样说比较抽象,下面有一段代码可以感受一下所有权。
fn main() {
let arr = vec![1, 2, 3];
let new_arr = arr;
// 无奖竞猜,打印啥
println!("{:?}", arr);
}
其他的关于Rust
的一些特性和细节就不过多介绍了,我们依旧把重心放到swc
上。
对Rust感兴趣的小伙伴可以看一下我的(Rust学习笔记)
目前Rust
被应用在前端工具链方面,如压缩(Terser)、编译(Babel)、格式化(Prettier)、打包(webpack)等场景中。而今天我们要讲的swc
做的就是做的这样一件事。
我们来看一下swc
的官方介绍:
SWC (stands for Speedy Web Compiler) is a super-fast TypeScript / JavaScript compiler written in Rust.
swc
的出现其实很大一部分原因是要替换掉工程中的babel
,所以babel
有的功能他几乎都有。
与babel最大的区别可能就是: swc is super-fast
swc官网中还有这样一句话,体现了他的速度:
除了swc
的官方宣传外,Next.js
基于swc
实现了一个Rust
编译器,用来解析编译、打包代码。下面是Next.js
结合swc
之后给出的一个数据:
所以从上面这些数据也可以简单看出来swc
的优势:可以提高开发效率,提升开发体验。
这也是目前很多工程选择接入它的原因。
swc 怎么用
基本使用
- 安装依赖:
npm i -D @swc/cli @swc/core
- 运行命令:
npx swc ./index.js -o output.js
(编译单个文件)
执行过命令之后会把结果打印在标准输出里,并没有生成文件之类的。
如果想要输出的文件中需要携带参数来完成-o ouput.js
或者-d dist
编译到dist目录下
Bundling
- 根目录下需要有一个配置文件
spack.config.js
const { config } = require('@swc/core/spack');
module.exports = config({
entry: {
web: './src/index.js',
// 可以配置多入口
},
output: {
path: './bundle/',
},
module: {},
options:{},
});
- 运行命令
npx spack
进行打包
打包支持tree-shaking
、Commonjs
模块、提取公共模块等。
详细配置项:swc.rs/docs/config…
Plugin
swc
的plugin
其实就是将核心包中的一些API
暴露出来,给开发者做一些自定义的操作。
看个例子,这个例子的作用就是将代码中的console.log()
过滤掉,用void 0
代替
用过Bable的同学看起来可能会简单一些。
const Visitor = require('@swc/core/Visitor').default;
const { transformSync } = require('@swc/core');
module.exports = class ConsoleStripper extends Visitor {
visitCallExpression(expression) {
if (expression.callee.type !== 'MemberExpression') {
return expression;
}
// 判断代码类型以及对应的value是否为console
if (
expression.callee.object.type === 'Identifier' &&
expression.callee.object.value === 'console'
) {
// 如果是就替换为`void 0`
if (expression.callee.property.type === 'Identifier') {
return {
type: 'UnaryExpression',
span: expression.span,
operator: 'void',
argument: {
type: 'NumericLiteral',
span: expression.span,
value: 0,
},
};
}
}
return expression;
}
};
const out = transformSync(
`
if (foo) {
console.log("Foo")
} else {
console.log("Bar")
}`,
{
plugin: (m) => new ConsoleStripper().visitProgram(m),
}
);
值得一提的是,目前
swc
的插件系统是有性能问题存在的。该性能问题主要聚焦在两个方面
- 将
AST
通过Rust
传递给JS时会产生通讯上的损耗- 在JS中通过
JSON.parese()
去转换代码时所产生的损耗。目前
swc
也正在着手解决这个问题。点击吃瓜
上面提到的是我认为可能比较常用的一些东西,当然除此之外swc
还提供了像Jest
、和wasm
等工具,而且swc
还提供了一个loader
供开发者在webpack
中使用。
swc 为什么快
因为JavaScript
本身就有点慢。
我们先来看一下js
的执行流程:
这其中转换为
AST
以及编译成字节码应该是最耗费性能的。
而swc
是直接将代码根据不同平台来编译成对应的二进制文件,省略了前面最耗时的步骤。
接下来我们拿上面那个Plugin
来看一下swc
在代码转换过程中大概是怎么执行的
接下来的整个过程就是其实就是证实swc在编译代码的时候是直接由二进制文件中的代码来执行的。
我们将断点打在transformSync
处,看一下此处的执行:
来看一下比较重要的一部分代码:
transformSync(src, options) {
// ...
const { plugin } = options, newOptions = __rest(options, ["plugin"]);
// 是否有plugin
if (plugin) {
const m = typeof src === "string" ? this.parseSync(src, (_c = options === null || options === void 0 ? void 0 : options.jsc) === null || _c === void 0 ? void 0 : _c.parser, options.filename) : src;
return this.transformSync(plugin(m), newOptions);
}
// 最终的输出都是bindings.transformSync
return bindings.transformSync(isModule ? JSON.stringify(src) : src, isModule, toBuffer(newOptions));
}
大致执行流程:
从上面的执行图中可以看出来,我们获取到的结果,最终都是由bindings.transformSync
解析完成,然后输出的结果。
从源码中可以找到bindings
的入口,我们在这里打个断点,从这里看一下bindings
的执行流程
function loadBinding(dirname, filename = 'index', packageName) {
// 获取系统信息
const triples = triples_1.platformArchTriples[PlatformName][ArchName];
// 遍历系统信息
for (const triple of triples) {
if (packageName) {
try {
// 获取到需要加载的二进制文件路径
// /Users/xx/swc-demo/node_modules/@swc/core-darwin-x64/swc.darwin-x64.node
return require(
require.resolve(
`${packageName}-${triple.platformArchABI}`,
{ paths: [dirname] }
));
}
catch (e) {
// ...
}
}
// ...
}
流程图:
我们最后得到的结果就是require进来一个二进制文件
。
这个文件的路径大概是这样的:/Users/xx/swc-demo/node_modules/@swc/core-darwin-x64/swc.darwin-x64.node
,当然这会根据不同电脑生成不同的路径~
来看一下,swc
的包里这个文件:
为了证实它是一个二进制文件,我们可以点开看一下(这里是用了vscode的插件,才可以看到)
打开之后是这样的,这里我们就不做过多解读了,有兴趣的可以逐行翻译一下。
总结
其实从上面看来swc
的执行流程并不算很复杂,当然我们看到的是编译过的代码,大家也可以尝试去看一下swc
的rust
源码。看完之后应该会有一个感受:
JS是世界上最美丽的语言。
ok,本篇文章就是浅出的了解一下swc是什么,有兴趣的同学可以自己下来玩一下,下面我把我自己体验时候的代码仓库放到下面,有一些debug的配置已经配好了,大家可以尝试玩一下。
- 代码:github.com/rust-toys/s…
- swc:swc.rs/
转载自:https://juejin.cn/post/7034316603890237477