Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Rax App 自定义 HTML #713

Open
SoloJiang opened this issue Apr 23, 2021 · 5 comments
Open

[RFC] Rax App 自定义 HTML #713

SoloJiang opened this issue Apr 23, 2021 · 5 comments

Comments

@SoloJiang
Copy link
Contributor

SoloJiang commented Apr 23, 2021

Rax App 自定义 HTML

  • 提案时间: 2021 年 4 月 23 日
  • 相关实现的 issue:
  • 相关实现的 pr:

概述

面向未来,Rax App 推荐开发者尽量少的自定义 HTML,但是依然存在一些特殊的场景业务需要自定义 HTML 内容。

现有的方案中,Rax App 提供了 src/document/index.jsx ,用以下方式在构建时执行 renderToString 获取 HTML 文档:

import { createElement } from 'rax';
import { Root, Style, Script } from 'rax-document';

function Document(props) {
  return (
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover"/>
        <title>{props.title}</title>
        <Style />
      </head>
      <body>
        {/* root container */}
        <Root />
        <Script />
      </body>
    </html>
  );
}

export default Document;

但随着业务发展,该方案渐渐无法满足诉求,基于此我们希望可以通过更强大的方式来构建 HTML 文档。

背景

现有的 src/document/index.jsx 方案优势是:

  • 在无需引入额外模板引擎的前提下,可以用 JSX 的方式编写 HTML 结构,写法灵活(条件渲染等)
  • 针对 SSR 场景,可以在该文件中使用 Document.getInitialProps 统一获取页面数据(这个实用性后续再讨论)

劣势是:

  • CSR & SSR 均需要构建 src/document/index.jsx
  • 在不引入 cheerio 的前提下,使用 rax-document 无法很好的对产出的 HTML 结构进行修改
  • TDK 只能在 src/document/index.jsx 里做,但实际上有很多业务场景 TDK 是和页面逻辑关联的
  • 页面渲染的节点是固定的,即 id="root"
  • 存在历史包袱,即多页应用差异性渲染依然需要使用 path 作为唯一值

为什么不用 html-webpack-plugin

  • 核心最大的问题还是无法解决 SSR 场景下条件渲染的问题,参考现有 icejs 的相关实现,如果业务需要在服务端渲染的时候做条件渲染,只能在服务端调用 render(ctx, { htmlTemplate }) 。这种形式引入了两个问题:

    • 业务本身的 HTML 逻辑需要分开维护,即 public/index.html 一份,htmlTemplate 一份
    • 必须使用 cheerio 或者约束业务将页面渲染的节点使用特殊的占位符标识。否则 SSR 时,无法将页面渲染出的 HTML 字符串填入挂载节点
  • 该方案依然让自定义挂载节点变得更加复杂,例如目前 icejs 只能用 id="root" 作为挂载节点

  • 从 Rax App 框架本身渐进式演进来看,需要引入额外一套构建方式,会增加维护成本

解法

我们新的方案即使无法解决所有的问题,但是依然会着重考虑以下几个点:

  • 几乎所有常用属性和节点都可以由框架自定义,不用依赖 cheerio
  • 页面组件 TDK 的能力,使用方法参考 react-helmet
  • 通过 page name 进行差异性渲染
  • 页面挂载节点可自定义

基于以上所有的分析,新方案大方向上沿用 src/document/index.jsx,将 rax-document 的能力内置到 rax-app,并由 rax-app 对外提供 HTML 核心组件,同时面向三方的 build scripts 插件提供操作 HTML 结构的能力。

基础示例

Document

// ./src/document/index.tsx

import { createElement } from 'rax';
import { Head, Body, Root, BundleStyle, BundleScript } from 'rax-app';

function Document(props) {
  return (
    <html>
      <Head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover"/>
        <title>{props.title}</title>
        <BundleStyle />
      </Head>
      <Body>
        {/* root container */}
        <Root />
        <BundleScript />
      </Body>
    </html>
  );
}

export default Document;

TDK

// ./src/pages/Home/index.tsx

import { createElement } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import { Title, Meta } from 'rax-app';

import styles from './index.module.css';

function Home(props) {
  return (
    <View className={styles.homeContainer}>
      <Title>{props.data?.title}</Title>
      <Meta description="Home Page" />
      <Text className={styles.homeTitle}>Welcome to Your Rax App</Text>
      <Text className={styles.homeInfo}>More information about Rax</Text>
      <Text className={styles.homeInfo}>Visit https://rax.js.org</Text>
    </View>
  );
}

Home.getInitialProps = async function () {
  return {
    data: {
      title: 'Hello World',
    },
  };
};

export default Home;

Rax App 提供的 HTML 组件

组件名 作用 是否能在非 Document 模式使用
Head head 标签
Body body 标签
Title title 标签,构建后插入到 head 标签内 ✔️
Meta meta 标签,构建后插入到 head 标签内 ✔️
Link link 标签,构建后插入到开发者业务样式的 link 标签前 ✔️
Style style 标签,构建后插入到开发者业务样式的 link 标签前 ✔️
Script script 标签,构建后插入到 Root 节点和开发者业务 js bundle 之间 ✔️
BundleScript 开发者的业务 js bundle
BundleStyle 开发者的业务样式的 link 标签

基于以上能力,95% 以上的业务都不再需要 document。

如何引导开发者使用

  1. 提供对应的 codemod 工具帮助开发者快速从现有的 document 模式迁移到新的模式
  2. 增量用户模板改为新的模式
@ClarkXia
Copy link
Contributor

从框架本身渐进式演进来看,需要引入额外一套构建方式,会增加维护成本

这个点是 Document 引入的吧。

html-webpack-plugin 相比 Document 最核心的区别感觉在条件渲染这块,html 如果要做到 Document 那种能力需要借助模版语言开发体验和维护性都比较差。

@SoloJiang
Copy link
Contributor Author

从框架本身渐进式演进来看,需要引入额外一套构建方式,会增加维护成本

这个点是 Document 引入的吧。

html-webpack-plugin 相比 Document 最核心的区别感觉在条件渲染这块,html 如果要做到 Document 那种能力需要借助模版语言开发体验和维护性都比较差。

是的,核心还是目前 Rax App 需要兼容构建 Document,再引入这个就会存在三种模式

@SoloJiang
Copy link
Contributor Author

#657

@waylon-gmail
Copy link

https://rax.js.org/docs/guide/safe-area

安全区域适配需要自定义html才能加入meta 吗?

@SoloJiang
Copy link
Contributor Author

https://rax.js.org/docs/guide/safe-area

安全区域适配需要自定义html才能加入meta 吗?

这个 meta 默认已经加了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants