使用jsx来编写模板

529 阅读3分钟

前言

模板引擎这个应该都不陌生,最早可以追溯到前后端还没分离的时候,后端为了循环、判断值之类简化编写流程而使用的,毕竟手动拼接字符串之类的也太麻烦了。

下面是 php easytpl 的一个示例,从这个可以看到模板引擎的基本用途。

use PhpPkg\EasyTpl\EasyTemplate;

$tplCode = <<<'CODE'
My name is {{ $name | strtoupper }},
My develop tags:

{{ foreach($tags as $tag) }}
- {{ $tag }}

{{ endforeach }}
CODE;

$t = new EasyTemplate();

$str = $t->renderString($tplCode, [
    'name' => 'inhere',
    'tags' => ['php', 'go', 'java'],
]);

echo $str;

上面这对代码会输出

My name is INHERE,
My develop tags:

- php
- go
- java

回到 JavaScript,随着职能的不断提升前端也面临同样的问题,于是社区涌现了一堆库,例如:

上面的几个使用方法都大同小异,都是通过内置一些语法来简化重复的书写。不过思考一下使用模板引擎会有什么缺点?

下面是一段 ejs 代码示例

<% if (resultPreSale.length) { %>
<h2>本次新增入网楼栋:<%= resultPreSale.length %>个</h2>
<ul>
  <% resultPreSale.forEach(function(item){ %>
  <li>
    <p>
      <a href="<%= item.url %>"> 项目名称:<%= item.entryName %> </a>
    </p>
    <p>楼幢号:<%= item.buildingNumber %></p>
    <p>许可面积(㎡):<%= item.permittedArea %></p>
    <p>发放日期:<%= item.time %></p>
  </li>
  <% }); %>
</ul>
<% } %>

从上面的示例可以看到以下缺点:

  1. 缺少语法提示:在使用编辑器的时候使用相对应语法都会有提示,但是上面在使用 forEach 之类的根本不会有提示;
  2. 不能借助 Eslint、TypeScript 来提前检查编写错误:例如有一些 props 没有传递或者类型缺少,以及变量不存在而引用等错误,必须运行之后才会发现错误;
  3. 有额外的学习成本,每个模板引擎的语法其实都不一样,例如 ejs 和 pug 就不一致;

jsx

回归到最熟悉的 jsx 本身,得益于 React 的流行 jsx 这种灵活方便的语法就是首选,首先生态很丰富可以跟 TypeScript 和 eslint 无缝衔接,且语法跟 js 基本一致不会增加额外的学习成本,且跟编辑器配合很好可以有语法提示。

下面是一段示例

const App = (props: Props) => {
  return (
    <>
      <PreSale resultPreSale={props.resultPreSale}></PreSale>
      <List resultList={props.resultList}></List>
      <List resultList={props.residueList} title={<h2>其他正在摇号项目({props.residueList.length}个):</h2>}></List>
      <p>本次爬取时间:{props.currentTime}</p>
      <style>{style}</style>
    </>
  );
};

jsx 基础的语法和如何解析这里并不关心,说回主题如何使用 jsx 来编写模板呢?

解析 jsx

模板返回的都是字符串的形式,但是上面示例的 App 明显是一个函数,所以首先需要把 jsx 转换为字符串,幸运的是社区有相关的库,这里使用的是 preact-render-to-stringpreact是一个跟 React 实现很接近的库,你可以理解为 mini React。

这里其实并不会使用 hooks 之类的功能,使用的只是 jsx 语法本身,所以选用什么框架并不关键。

import { render } from 'preact-render-to-string';
const html = render(<App {...props}></App>);

如果结合 TypeScript 使用,还需要做一些额外的配置

  • 安装 preact

  • 修改 tsconfig.json

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

node 环境使用

这里并没有万事大吉,在 node 环境使用模板引擎也是一个很正常的事情,但是 tsx 或者 jsx 文件都是属于需要编译来进行处理的,否则 node 并不认识。

  1. tsx,使用 tsx 这个库,它通过 esbuild 来运行 TypeScript 和 ESM 文件;
  2. webpack

下面是一个完整的 webpack 示例

const path = require('path');
const nodeExternals = require('webpack-node-externals');
const RunNodeWebpackPlugin = require('run-node-webpack-plugin');

module.exports = {
  entry: {
    index: './src/index.ts',
  },
  target: 'node',
  devtool: 'source-map',
  externalsPresets: { node: true },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].cjs',
    clean: true,
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'esbuild-loader',
            options: {
              target: 'es2022',
            },
          },
        ],
      },
    ],
  },
};

最后

如果文章有错误欢迎留言指出。