該方案主要是爲了解決,前端 spa (單頁面應用),首屏渲染慢,白屏時間過長問題。html
經過 webpack 的 prerender-spa-plugin 編譯應用中的靜態頁面,並將其輸出到對應的索引目錄。前端
prerender-spa-plugin 利用了 Puppeteer 的爬取頁面的功能。 Puppeteer 是 Google Chrome 團隊官方的無界面(Headless)Chrome 工具,它是一個 Node 庫,提供了一個高級的 API 來控制 DevTools 協議上的無頭版 Chrome 。prerender-spa-plugin 原理是在 Webpack 構建階段的最後,在本地啓動一個 Puppeteer 的服務,訪問配置了預渲染的路由,而後將 Puppeteer 中渲染的頁面輸出到 HTML 文件中,並創建路由對應的目錄。react
在項目根目錄添加 config-overrides.js 配置文件,參考配置以下:webpack
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; }; 複製代碼
修改 config 文件夾下的 webpack.config.js ,參考代碼以下:git
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 詳細配置參考官方文檔github
在首頁 react 組件的 didMount 事件中調用 document.dispatchEvent(new Event('custom-render-trigger'))web
方案一:markdown
修改入口文件 index.js,app
//判斷是不是預渲染環境 if(window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.prerender){ // loading 組件 import('./skeleton/index.js'); }else{ // 原先的入口文件 import('./page.js'); } 複製代碼
skeleton/index.js 將骨架組件或者 loading 組件輸出到 index.html,參考代碼以下:less
// 建立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> ) } 複製代碼