Skip to content

解耦 menu 逻辑与内容区域逻辑, 加快页面渲染速度。 #103

Closed
@MuYunyun

Description

@MuYunyun

背景

当前内容区由预渲染吐出首屏,但是随后加载 JavaScript 逻辑后,页面重新 loading 载入导致预渲染的页面「被浪费」。究其原因,其中涉及到变动的逻辑仅仅是在菜单区域,但是照成了全部的页面的重新 loading 是不可接受的。因此可以解耦菜单与内容区,在利用预渲染带来的首屏体验的同时,仅仅重新渲染需要加载动态逻辑的部分,进一步完善衔接体验。

  • 问题演示
MailVideo.mov

以访问章节 快速上手 为例,用户从访问到首屏渲染页面到页面可交互(JavaScript 逻辑执行完毕),会经历如下步骤:

首屏阶段:当用户访问 快速上手 时,gp-pages 推送预渲染页面,用户获得友好的首屏体验 😁。

image

衔接阶段:从预渲染页面到页面可交互,出现了干扰体验的加载,体验十分不好 😭。

image

可交互阶段:左侧菜单按钮等 JavaScript 逻辑执行完毕,在这个阶段用户可以与页面进行交互。

22641642138225_ pic

策略

  • ❎ 思路一: 内容区域使用 iframe 单独加载, 与 menu 分离。
    • 要保证内容区的预渲染,如果内容区在 iframe 中加载,会导致内容区的渲染更加延后。
  • ❎ 思路二: 重构左侧 menu, 不用 menu 动画逻辑, 一个层级只显示当前 menu 列表。相关 issue:优化首页菜单区域可可交互时间过长的问题 #219
    • 缺陷: 该做法的交互与存量交互完全不同,成本高。
  • 思路三: 菜单区域使用 iframe 单独加载, 与内容区分离。
    • 缺陷:
      1. 牺牲部分体验: 菜单栏交互需要变更为 fixed 定位,体验没有原来的效果好。
      2. Iframe 区域同步事件成本高: 这部分没有进一步探究,可预计的是成本不小。
  • 思路四: 从根路径解耦区分预渲染与线上环境的渲染,预渲染时渲染出大部分静态页面,线上渲染渲染补充动态页面与逻辑。同时移除「正在加载中逻辑」。
const ifProdRender = ifProd && !ifPrerender

if (!ifProdRender) {
  ReactDOM.render(
    <RouterRoot />,
    document.getElementById('root'),
  )
} else {
  // render dynamic logic(such as menu) here.
  ReactDOM.render(
    <RouterRoot pointRender="menu" />,
    document.getElementById('root'),
  )
}

packages/crd-seed/layout/index.js

  return (
    <>
      {
        pointRender === 'menu'
          // prod render
          ? renderMenuContainer()
          // pre & dev render
          : <div className={styles.wrapper}>
            <Header
              logo={logo}
              href={ifAddPrefix ? `/${repo}` : `/`}
              location={location}
              indexProps={indexProps}
              menuSource={menuSource}
            />
            <div
              className={cx(styles.wrapperContent, {
                [styles.wrapperMobile]: isMobile,
              })}
            >
              {renderPageHeader()}
              <div id="menuPosition">
                {renderMenuContainer()}
              </div>
              {renderContent()}
              <Footer inlineCollapsed={inlineCollapsed} />
            </div>
          </div>
      }
    </>
  )

此番修改后,首屏加载后,确实不会二次刷新了,但是带来了几个问题,

  1. menu 点击切换后,url 会发生变化,但是内容区未发生更新。
  2. 内容区(包含样式)未更新,因此不能展开收起。

当前在思路四的基础上,确实不可避免存在移除预渲染的 html 后的一刹那闪动问题。

  • ✨ 思路五: 依赖 gp-pages 服务,基于预渲染加载 SSR 首屏渲染页面,然后客户端执行注水逻辑。
MailVideo.mov
- ReactDOM.render(
-   <RouterRoot />,
-   document.getElementById('root'),
- )
+ if (ifDev) {
+   // dev render
+   document.getElementById('root').innerHTML = ReactDOMServer.renderToString(<RouterRoot />)
+   ReactDOM.hydrate(
+     <RouterRoot />,
+     document.getElementById('root'),
+   )
+ } else if (ifPrerender) {
+   // prerender
+   document.getElementById('root').innerHTML = ReactDOMServer.renderToString(<RouterRoot />)
+ } else {
+   // prod render:
+   ReactDOM.hydrate(
+     <RouterRoot />,
+     document.getElementById('root'),
+   )
+ }

Metadata

Metadata

Assignees

Labels

performanceexperience optimization

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions