jsx是JavaScript的一个类似XML的扩展,虽然最早是由react提出的,但实际上 JSX 语法并没有定义运行时语义,
并且能被编译成各种不同的输出形式,
jsx可以由很多工具进行转换为js,比如Babel和TypeScript,接下来就通过babel和ts创建属于自己的jsx
1、TypeScript转换
安装typescript依赖,并进行配置相关的准备工作
pnpm i -D typescript
编写tsconfig.json,输入如下的简单配置,其中jsx: preserve,表示不对jsx进行转换,而是直接保留jsx,
因为接下来我们可以通过Babel进行转换,为了让ts能够识别jsx,需要编写对应的类型
-
tsconfig.json
{ "compilerOptions": { "strict": true, "target": "ES5", "module": "ESNext", "jsx": "preserve", // 保留原始jsx,不进行转换 "outDir": "dist", "types": ["./type.d.ts"] }, "include": ["input.tsx"] } -
type.d.ts:声明jsx类型,不然在编译jsx时会报错
declare namespace JSX { interface IntrinsicElements { [elemName: string]: any } } -
input.tsx: 入口文件,只是通过tsc编译js,不进行转换jsx,直接保留jsx,jsx的编译交给babel做。
const x = ( <div class='xxx' id='test'> <div class='list-1'>1111</div> <div class='list-2'>2222</div> </div> ) -
npx tsc编译input.tsx,输出如下'use strict' var x = ( <div class='xxx' id='test'> <div class='list-1'>1111</div> <div class='list-2'>2222</div> </div> )
2、通过Babel插件转换成自定义的jsx
参考react.createElement,假设存在一个函数Dom,用来创建属于自己的虚拟节点
function Dom(tagName: string, props: Record<string, any>, ...children: CustomVNode[]): CustomVNode{
... // 实现不考虑,本文只考虑jsx的转换
}
安装babel相关依赖
pnpm i -D @babel/cli @babel/core @babel/plugin-syntax-jsx
-
module.exports = function ({ types: t }) { return { visitor: { // 处理 JSXElement JSXElement(path) { // 得到当前 JSX的节点结构 const node = path.node const { openingElement } = node // 获取这个JSX标签的名字 const tagName = openingElement.name.name // {class:'xxx',id: 'test'} const propsList = openingElement.attributes.reduce((result, item) => { result.push( t.objectProperty( t.identifier(item.name.name), t.stringLiteral(item.value.value) ) ) return result }, []) const createIdentifier = t.identifier('Dom') const args = [t.stringLiteral(tagName), t.objectExpression(propsList)] // Dom('div', {class:'xxx',id: 'test'}) const callRCExpression = t.callExpression(createIdentifier, args) // ...children callRCExpression.arguments = callRCExpression.arguments.concat( path.node.children ) path.replaceWith(callRCExpression, path.node) }, // 处理 JSXText 节点 JSXText(path) { const nodeText = path.node.value // 直接用 string 替换 原来的节点 path.replaceWith(t.stringLiteral(nodeText), path.node) }, }, } } -
配置babel,使用刚刚编写的自定义插件(
custom-plugin.js){ "plugins": ["@babel/plugin-syntax-jsx", "./custom-plugin.js"] } -
对ts编译后的产物,再次进行转换,就可以得到自定义的jsx了
npx babel dist/input.jsx --out-file dist/output.js'use strict' var x = Dom( 'div', { class: 'xxx', id: 'test', }, '\n ', Dom( 'div', { class: 'list-1', }, '1111' ), '\n ', Dom( 'div', { class: 'list-2', }, '2222' ), '\n ' )