美文网首页JavaScript
首屏预渲染方案

首屏预渲染方案

作者: jy789 | 来源:发表于2019-05-14 11:24 被阅读0次

    该方案主要是为了解决,前端 spa (单页面应用),首屏渲染慢,白屏时间过长问题。

    实现方法

    通过 webpack 的 prerender-spa-plugin 编译应用中的静态页面,并将其输出到对应的索引目录。

    prerender-spa-plugin 插件原理介绍

    prerender-spa-plugin 利用了 Puppeteer 的爬取页面的功能。 Puppeteer 是 Google Chrome 团队官方的无界面(Headless)Chrome 工具,它是一个 Node 库,提供了一个高级的 API 来控制 DevTools 协议上的无头版 Chrome 。prerender-spa-plugin 原理是在 Webpack 构建阶段的最后,在本地启动一个 Puppeteer 的服务,访问配置了预渲染的路由,然后将 Puppeteer 中渲染的页面输出到 HTML 文件中,并建立路由对应的目录。

    在 create-react-app 中配置 prerender-spa-plugin

    create-react-app 配置未 eject 的情况:

    在项目根目录添加 config-overrides.js 配置文件,参考配置如下:

    const PrerenderSpaPlugin = require('prerender-spa-plugin');
    const path = require('path');
    
    module.exports = (config, env) => {
      if (env === 'production') {
        config.plugins = config.plugins.concat([
          new PrerenderSpaPlugin({
            staticDir: path.join(__dirname, 'build'),
            routes: ['/'],
            renderer: new PrerenderSpaPlugin.PuppeteerRenderer({
              injectProperty: '__PRERENDER_INJECTED',
              inject: {
                prerender: true
              },
              // 这个是监听 document.dispatchEvent 事件,决定什么时候开始预渲染
              // document.dispatchEvent(new Event('render-event'))
              renderAfterDocumentEvent: 'custom-render-trigger',
            })
          })
        ]);
      }
    
      return config;
    };
    
    create-react-app 配置已经 eject 的情况:

    修改 config 文件夹下的 webpack.config.js ,参考代码如下:

    plugins: [
      // 预渲染插件
      isEnvProduction && new PrerenderSpaPlugin({
        staticDir: path.join(__dirname, '../build'),
        routes: ['/'],
        renderer: new PrerenderSpaPlugin.PuppeteerRenderer({
          injectProperty: '__PRERENDER_INJECTED',
          inject: {
            prerender: true
          },
          renderAfterDocumentEvent: 'custom-render-trigger',
        })
      }),
      ...
    ]
    

    prerender-spa-plugin 详细配置参考官方文档

    prerender-spa-plugin.png

    页面中使用方案推荐

    纯静态页面,无接口数据情况:

    在首页 react 组件的 didMount 事件中调用 document.dispatchEvent(new Event('custom-render-trigger'))

    非静态页面,有接口数据情况:

    方案一:

    修改入口文件 index.js,

    //判断是否是预渲染环境
    if(window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.prerender){
      // loading 组件
      import('./skeleton/index.js');
    }else{
      // 原先的入口文件
      import('./page.js');
    }
    

    skeleton/index.js 将骨架组件或者 loading 组件输出到 index.html,参考代码如下:

    // 创建loading容器
    const container = document.createElement("div");
    container.className = 'prerender-loading';
    document.body.appendChild(container);
    
    // 渲染骨架组件或者 loading 组件
    ReactDOM.render(
      <div style={{padding: '16px'}}>
        <Skeleton/>
      </div>, 
    container);
    

    在首页组件中,当数据加载完成后,调用:

    // 移除 loading 或者骨架组件
    document.querySelector('.prerender-loading').remove();
    // 展示首页
    ...
    

    方案二:

    首页数据未加载前,静态部分显示,动态部分显示骨架组件,参考代码如下:

    export default function Index() {
      const [data, setData] = useState(null)
    
      useMount(() => {
        document.dispatchEvent(new Event('custom-render-trigger'));
      });
    
      // 模拟getdata
      useTimeout(() => {
        setData({...});
      }, 200)
    
      return (
        <div className='Index'>
          <div>静态ui</div>
          {
            data === null
            ? <Skeleton>骨架ui</Skeleton> 
            : <div>动态数据ui</div>
          }
        </div>
      )
    }
    

    相关文章

      网友评论

        本文标题:首屏预渲染方案

        本文链接:https://www.haomeiwen.com/subject/rizjaqtx.html