如何在构建时将TSX编译为模板字面量?(纯SSR Node.js+TS项目)
问题描述
我正在开发一个基于TypeScript的纯SSR Node.js项目,当前用nanohtml库通过模板字面量实现模板引擎,代码示例如下:
// Some .ts file import html from 'nanohtml' export const template = (variable) => html`<p>Hello ${variable}</p>` // Somewhere (in a .ts file) res.send( template("world").toString() )
我希望减少生产环境依赖数量,更高效利用已有的TypeScript工具,想知道能不能用类JSX语法,将其编译为JS文件与模板字面量(弃用nanohtml),以及对应的tsconfig.json配置方案,期望实现的代码效果如下:
// Example - Some .tsx file with NO nanohtml lib export const template = (variable) => <p>Hello ${variable}</p> // option 1 export const template = (variable) => jsx`<p>Hello ${variable}</p>` // or option 2
解决方案
完全可以通过TypeScript的JSX编译配置实现这个需求,无需依赖nanohtml,以下是具体实现步骤:
1. 配置tsconfig.json
调整TypeScript的JSX编译选项,让它把JSX转换为自定义逻辑或模板字面量:
{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "./src/jsx-runtime", "target": "ES2020", "module": "CommonJS", "strict": true, "esModuleInterop": true }, "include": ["src/**/*"] }
2. 实现Option 1:直接使用语法
在src/jsx-runtime.ts中编写自定义JSX解析逻辑,将JSX元素转为HTML字符串:
// src/jsx-runtime.ts export type JSXElement = string; // 转义用户输入,防止XSS const escapeHtml = (str: string): string => { return str.replace(/[&<>"']/g, char => { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return map[char as keyof typeof map]; }); }; export function jsx(tag: string, props: Record<string, any>, ...children: (string | number)[]): string { // 处理HTML属性 const attrs = Object.entries(props || {}) .map(([key, value]) => { if (typeof value === 'boolean') return value ? key : ''; return `${key}="${escapeHtml(String(value))}"`; }) .filter(Boolean) .join(' '); const attrsStr = attrs ? ` ${attrs}` : ''; const childrenStr = children.map(c => escapeHtml(String(c))).join(''); // 处理自闭合标签 const selfClosingTags = new Set(['img', 'br', 'input', 'meta', 'link']); if (selfClosingTags.has(tag)) { return `<${tag}${attrsStr} />`; } return `<${tag}${attrsStr}>${childrenStr}</${tag}>`; } // 兼容多子元素的jsxs,直接复用jsx逻辑 export const jsxs = jsx;
之后在TSX文件中即可直接使用JSX语法:
// src/template.tsx export const template = (variable: string) => <p>Hello {variable}</p>; // 路由中使用 res.send(template("world"));
3. 实现Option 2:jsx模板字面量函数
如果更偏好模板字面量写法,无需配置JSX运行时,直接实现一个包装函数即可:
// src/jsx.ts const escapeHtml = (str: string): string => { return str.replace(/[&<>"']/g, char => { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return map[char as keyof typeof map]; }); }; export function jsx(strings: TemplateStringsArray, ...values: any[]): string { return strings.reduce((result, str, index) => { const value = values[index] ?? ''; return result + str + escapeHtml(String(value)); }, ''); }
在文件中导入后使用:
// src/template.tsx import { jsx } from './jsx'; export const template = (variable: string) => jsx`<p>Hello ${variable}</p>`; // 使用方式不变 res.send(template("world"));
内容的提问来源于stack exchange,提问作者charnould
相关产品推荐
相关产品推荐

