什么是swc?
SWC 既可用于编译,也可用于打包。对于编译,它使用现代 JavaScript 功能获取 JavaScript / TypeScript 文件并输出所有主流浏览器支持的有效代码。
swc官网 Rust-based platform for the Web – SWC
V8官网 v8.dev/
SWC在单线程上比 Babel 快 20 倍,在四核上快 70 倍。
简单点来说swc实现了和babel一样的功能,但是它比babel快。
FAQ为什么快?
-
编译型 Rust 是一种编译型语言,在编译时将代码直接转化为机器码(底层的 CPU 指令)。这种机器码在执行时非常高效,几乎不需要额外的开销。
-
解释型 JavaScript 是一种解释型语言,通常在浏览器或 Node.js 环境中通过解释器运行。尽管现代的 JavaScript 引擎(如 V8 引擎)使用了 JIT(即时编译)技术来提高性能,但解释型语言本质上还是需要更多的运行时开销。
js编译阶段要多个阶段开销很高
Blink(谷歌浏览器的渲染引擎,基于webkit分支开发)主要负责HTML DOM CSS 渲染,嵌入V8引擎,执行js,计算样式和布局,嵌入合成器,绘制图形。
1. Blink 拿到html
代码分析,找到script
代码交给V8引擎解析,注意Blink是通过流的形式传给V8的
2. Scanner(扫描器)首先会进行词法分析
3. 解析器parser进行语法分析 转化为 AST
抽象语法树
4. PreParser是预解析器,它的作用是在 JavaScript 代码执行之前对代码进行可选的预处理。预解析器的存在是为了提高代码的执行效率。例如,在一个函数 outer
内部定义了另一个函数 inner
,那么 inner
函数就会进行预解析。这意味着在函数 outer
被调用之前,只会对 outer
函数的内容进行解析,而对于 inner
函数的解析会在 outer
函数调用到 inner
函数时才进行。
5. 解释器Ignition 主要作用就是将AST 抽象语法树 转化成 字节码(bytecode)
为什么要转成字节码而不是直接转成机器码
跨平台执行:不同的硬件架构和操作系统有不同的机器码格式。通过将代码转换为字节码,可以使得同一份字节码在不同的平台上都能执行,实现跨平台的能力
。- 快速启动和解析:将代码转换为字节码可以比直接生成机器码更快速地进行启动和解析。字节码通常具有更简单的格式和结构,可以更快地被引擎加载和解释执行。
- 动态优化:现代的JavaScript引擎通常具有即时编译(JIT)功能,可以将热点代码编译成高效的机器码。通过首先将代码转换为字节码,引擎可以更好地进行动态优化和编译,根据实际执行情况生成最优的机器码。这种方式可以在运行时根据代码的实际执行情况进行优化,而不需要提前生成固定的机器码。
- 代码安全性:字节码作为中间表示形式,可以提供一定的代码安全性。字节码相对于源代码或机器码来说更难以理解和修改,可以提供一定程度的代码保护。
6. 编译器TurboFan就是将字节码也可以叫中间代码 最后 转换成 机器码
能让我们的CPU识别
swc 核心功能
- JavaScript/TypeScript 转换 可以将现代 JavaScript(ES6+)和 TypeScript 代码转换为兼容旧版 JavaScript 环境的代码。这包括语法转换(如箭头函数、解构赋值等)以及一些 polyfill 的处理
- 模块打包 SWC 提供了基础的打包功能,可以将多个模块捆绑成一个单独的文件
- SWC 支持代码压缩和优化功能,类似于 Terser。它可以对 JavaScript 代码进行压缩,去除不必要的空白、注释,并对代码进行优化以减小文件大小,提高加载速度
- SWC 原生支持 TypeScript,可以将 TypeScript 编译为 JavaScript
- SWC 支持 React 和 JSX 语法,可以将 JSX 转换为标准的 JavaScript 代码。它还支持一些现代的 React 特性
案例
1. 语法转换:将新版本的 JavaScript 语法转换为旧版本的语法
下载依赖
npm i react react-dom @swc/core -D
测试用例
//语法
const a = (params = 2) => 1 + params;
const b = [1, 2, 3]
const c = [...b, 4, 5]
class Babel {
}
new Babel()
//API
const x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter((x) => x % 2 === 0)
const y = Object.assign({}, { name: 1 })
swc 转换代码
import swc from '@swc/core'
const result = swc.transformFileSync('./test.js', {
jsc: {
target: "es5", //代码转换es5
parser: {
syntax: 'ecmascript'
}
}
})
console.log(result.code)
转换后结果
//语法
function _array_like_to_array(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
return arr2;
}
function _array_without_holes(arr) {
if (Array.isArray(arr)) return _array_like_to_array(arr);
}
function _class_call_check(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _iterable_to_array(iter) {
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _non_iterable_spread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _to_consumable_array(arr) {
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
}
function _unsupported_iterable_to_array(o, minLen) {
if (!o) return;
if (typeof o === "string") return _array_like_to_array(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
}
var a = function() {
var params = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 2;
return 1 + params;
};
var b = [
1,
2,
3
];
var c = _to_consumable_array(b).concat([
4,
5
]);
var Babel = function Babel() {
"use strict";
_class_call_check(this, Babel);
};
new Babel();
//API
var x = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
].filter(function(x) {
return x % 2 === 0;
});
var y = Object.assign({}, {
name: 1
});
2. swc转换react jsx语法
test.jsx
import react from 'react'
import { createRoot } from 'react-dom/client'
const App = () => {
return <div>?????</div>
}
createRoot(document.getElementById('root')).render(<App />)
index.js
import swc from '@swc/core'
console.time()
const result = swc.transformFileSync('./test.jsx', {
jsc: {
target: "es5", //代码转换es5
parser: {
syntax: 'ecmascript',
jsx: true
},
transform:{
react: {
runtime: 'automatic'
}
}
}
})
console.log(result.code)
console.timeEnd()
结果
import { jsx as _jsx } from "react/jsx-runtime";
import react from 'react';
import { createRoot } from 'react-dom/client';
var App = function() {
return /*#__PURE__*/ _jsx("div", {
children: "小满是谁?????"
});
};
createRoot(document.getElementById('root')).render(/*#__PURE__*/ _jsx(App, {}));
swc转换用时 default: 4.251ms
Babel转换用时 default: 80.613ms
oxc还要比快swc三倍