停止使用 ejs 和 pug 并使用 tsx 编写 html?

介绍

这是一个html模板选择的故事。

对于不叫Modern Front的项目,我们还是使用ejs、pug等html模板引擎库来编写和交付html。

这一次,我们说的是采用 tsx 作为这样的模板引擎。

结论

renderToStaticMarkup中的tsx文件(react-dom的函数)转换成字符串,输出为html文件。这使得使用 tsx 表示法生成 html可以做。

下面是一个使用 webpack 自动编译的示例。

ejs 和 pug 的局限性

ejs和pug可以用html来描述JavaScript,可以用if, for, switch, try/catch等js语法,用let, const进行变量声明,用include进行部分文件的提取和读取,可以高效处理html。

但是,随着项目(以下简称 PJ)的发展,您可能会感到各种限制。

  • 无法进行 lint 检查(如果你寻找它,可能有IDE扩展)
  • 未键入 include 的参数
  • 在编译之前我不知道错误&失败时难以识别错误位置
  • 限制你的感觉

为了解决上面的问题,我想介绍一下 React 的语法 tsx 作为 html 模板引擎。

tsx 的优势

tsx 首先是什么?

jsx 是 JavaScript 语法的扩展,是用 TypeScript 编写的。

这里是对jsx的详细解释。

与 ejs 能够在 html 中编写 JavaScript 的形象相比,jsx 就像能够在 JavaScript 中编写 HTML。

使用 tsx 可以解决 ejs 和 pug 的局限性

作为前提,上面出现的所有ejs和pug函数if, for, const, include都可以用tsx实现。

最重要的是,您可以解决使用 ejs 和 pug 时遇到的问题。

  • 无法检查 lint 的问题
    • 已解决,因为 eslint 按原样运行。
  • include 参数未输入
    • 由于参数作为 props 传递给部分文件,这是 tsx (/jsx) 的函数,因此可以使用静态类型。
  • 难以理解错误位置
    • Eslint 运行,所以在编码时解决

另一个优点是学习成本很低,因为您没有创建 React 应用程序。

简单地记住 jsx 语法(if 是单行,formap 等)与学习 ejs 和 pug 语法没有太大区别。

如何将 tsx 转换为 html

我们来看看具体的实现。

首先,使用ReactDOMServer对象的renderToStaticMarkup方法作为将tsx转换为html的方法。

tsx 被 React 转换为 JavaScript 一次,但是可以通过上面的函数在 Node 服务器上转换为静态的 html 字符串。

我将介绍两种方法,一种使用 webpack,一种不使用。

1.没有webpack的方法

首先,我们将了解如何使用ts-node 简单地编译 tsx,而不使用 webpack 并输出 html。

你需要的包

yarn add -D @types/node @types/react @types/react-dom react react-dom ts-node typescript

适当准备tsconfig.json(需要设置"jsx": "react-jsx"),准备以下文件。

页面.tsx

import ReactDOMServer from "react-dom/server";

const App = () => {
  return (
    <div>
      <h1>App Page.</h1>
      <p>description.</p>
    </div>
  );
};

const pageString = ReactDOMServer.renderToString(<App />);

const page = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
</head>
<body>
${pageString}
</body>
</html>
`;

console.log(page);
export default page;

执行以下命令会将字符串化的 html 输出到控制台。

我看到你可以从 tsx 生成 html 字符串。

$ ts-node page.tsx

您所要做的就是根据这个字符串生成一个 html 文件。

我认为使用 Node.js 函数的方法有很多,但以下是一个示例。

索引.ts

import fs from "fs";
import path from "path";
import page from "./page";

const writeFile = async (file: string, data: string): Promise<void> => {
  await fs.promises.mkdir(path.dirname(file), { recursive: true });
  fs.promises.writeFile(file, data);
};

const filename = path.basename(__filename, ".ts");
writeFile(path.resolve(__dirname, `build/${filename}.html`), page);

build/index.html 在我运行时被喷出。

$ ts-node index.ts

我能够使用 tsx 作为模板引擎!

2.如何使用webpack

嗯,我发现用上面的方法可以从tsx生成html。但是,当必要页面的数量增加时,它似乎有点麻烦。

那么让我们使用 webpack 搭建一个自动编译环境。

(那些说不需要 webpack 的人之后就不需要了。)

首先安装所需的软件包。

$ yarn add -D @types/react @types/react-dom esbuild-loader globule html-webpack-plugin html-webpack-skip-assets-plugin prettier react react-dom@17.0.2 typescript webpack webpack-cli webpack-dev-server

※笔记※

react-dom 库的最新版本是 2005 年 10 月 22 日的 ver.18,但由于某种原因,当它是 ver.18 时,它无法编译并出现错误 Error: TextEncoder is not defined。 (如果你知道为什么,请...)

所以我安装了ver.17。

使用export html 字符串(如page.tsxHtmlWebpackPlugin)对tsx 文件进行grep,以自动将其编译为html 文件。

我将编写一个脚本,以便即使屏幕数量像 MPA 一样增加,也可以完成 grep。我将globule 库用于grep。

webpack.config.js

const path = require('path');
const globule = require('globule');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {HtmlWebpackSkipAssetsPlugin} = require('html-webpack-skip-assets-plugin');

const assignPlugins = (env) => {
  const globuleFiles = ['**/*.tsx', '!**/_*.tsx', '!**/_partials/**/*.tsx'];

  /** 指定されたディレクトリからgrepしたtsxファイル */
  const templateFiles = globule.find([...globuleFiles], {cwd: `${__dirname}/src/pages`});

  /** entryファイルを格納したオブジェクトを作成 */
  const entriesList = templateFiles.reduce((temp, current) => {
    temp[`${current.replace(new RegExp(`.tsx`, 'i'), `.html`)}`] = `${__dirname}/src/pages/${current}`;
    return temp;
  }, {});

  const assignObject = { plugins: [] };
  for (const [htmlFileName, tempFileName] of Object.entries(entriesList)) {
    assignObject.plugins.push(new HtmlWebpackPlugin({
      filename: htmlFileName,
      template: tempFileName
    }));
    env.WEBPACK_BUILD && assignObject.plugins.push(new HtmlWebpackSkipAssetsPlugin({ excludeAssets: [/entry.js/] }));
  }

  return assignObject;
};

module.exports = (env) => (
  Object.assign({
    entry: './src/entry',
    output: {
      path: path.join(__dirname, 'dist'),
      filename: 'entry.js'
    },
    devtool: false,
    watchOptions: {
      ignored: /node_modules/
    },
    resolve: {
      extensions: ['.js', '.ts', '.tsx'],
    },
    module: {
      rules: [
        {
          test: /.tsx$/,
          use: [
            {
              loader: 'esbuild-loader',
              options: {
                loader: 'tsx',
              }
            }
          ]
        },
      ],
    },
    devServer: {
      ...
      watchFiles: [
        'src/**/*.tsx'
      ],
    },
  }, assignPlugins(env));
);

需要准备入口文件,所以直接在src目录下准备entry.js

积分

  • 入口文件entry.js 会在构建时自动从html 中的script 元素中读取,但它是不必要的文件,因此不会输出HtmlWebpackSkipAssetsPlugin

    但是,在开发过程中,除非读取这个入口文件,否则它不会被编译,所以这个插件只有在env.WEBPACK_BUILDtrue时才会被读取(构建时)。

  • devServer.watchFiles 选项指定 'src/**/*.tsx' 以根据 tsx 文件的变化使 webpack-dev-server 热重载。

▼ 这里是刚刚吐出html的最小环境

奖金

由于自己搭建了webpack环境,所以尝试创建一个可以同时编译sass和typescript的环境。

yarn start 将启动本地服务器,yarn build 将在 dist 目录下构建它。

好像每个项目都需要设置图片压缩,所以一次都没放。

[额外奖励] 从 html 到 tsx 的转换

在将现有的 html 文件转换为 tsx 以进行增强等并对其进行开发时,有必要的过程,例如重写和转义反引号,我觉得手动操作很麻烦/辛苦,所以我用脚本将它自动化。我是被允许。

pages目录下新建一个转换前的html文件,执行以下命令生成同名的tsx文件。 (html文件被删除)

$ node convertHtmlToTsx.js

由于有新文件差异的文件是用 git 一次性转换的,所以可以同时转换多个文件。

源代码是这里是。

概括

我尝试使用 tsx 作为 html 模板引擎构建环境。

最近SPA、SSR、SSG已经成为主流,webpack本身也在消亡,但我觉得还是有项目交付静态html的。

如果我能为改善某人的开发体验做出贡献,我会很高兴。

参考文章

本文用作创建存储库的参考。谢谢你。

原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308628452.html