本周精读内容是 《在浏览器运行 serverRender》。
这里是效果页,先睹为快:client-ssr。
在服务端 ssr 成为常识的今天,前端 ssr 是我最近的新尝试,效果可以点击上面链接查看。说说前端 ssr 有哪些好处:
相对的,缺点是:
像缓存清空时机等问题,前后端 ssr 都会遇到,所以不列在优缺点中。
本篇精读分享的是前端 ssr 方案具体实现步骤。
我们先了解整体流程:
service worker 可以在浏览器尝试请求首屏 html 之前的时机拦截,此时如果 caches 命中,直接将 response 扔给浏览器,那么服务端将完全不会收到请求,完成了最高效的缓存命中。
当然第一次没有缓存,所以在没有命中缓存时,会同步的做两件事:
附上代码片段:
self.addEventListener("fetch", event => { if ( event.request.mode === "navigate" && event.request.method === "GET" && event.request.headers.get("accept").includes("text/html") ) { event.respondWith( caches.open(SSR_BUNDLE_VERSION).then(cache => { return cache.match(event.request).then(response => { // 命中缓存,直接返回结果。 if (response) { return response; } return fetch(event.request).then(response => { const newResponse = response.clone(); return newResponse .text() .then(text => { // 通知浏览器,执行 ssr 并且返回内容。 self.clients.matchAll().then(clients => { if (!clients || !clients.length) { return; } clients.forEach(client => { client.postMessage({ type: "getServerRenderContent", pathname: new URL(event.request.url, location).pathname }); }); }); return response; }) .catch(err => response); }); }); }) ); } });
当然还需要一个监听,用来拿浏览器的 ssr 内容,并缓存到 caches 中,比较简单就省略了。
监听就不说了,主要是如何利用 react-router
与 react-loadable
完成前端 ssr。
首先根据 service worker 告诉我们的 pathname
,拿到对应 loadable
的实例,并通过 loadable.preload()
预先加载 chunk,当 chunk 加载完毕时,资源已经准备好了。
我们利用给 StaticRouter
传递当前的 pathname
,让 react-router
模拟出需要 ssr 的页面内容,通过 renderToString
拿到 ssr 的结果。
附上代码片段:
if (navigator.serviceWorker) { navigator.serviceWorker.addEventListener("message", event => { if (event.data.type === "getServerRenderContent") { const baseHrefRegex = new RegExp( escapeRegExp("${projectConfig.baseHref}"), "g" ); const matchRouterPath = event.data.pathname.replace(baseHrefRegex, ""); const loadableMap = pageLoadableMap.get( matchRouterPath === "/" ? "/" : trimEnd(matchRouterPath, "/") ); if (loadableMap) { loadableMap.preload().then(() => { const ssrResult = renderToString( <StaticRouter location={event.data.pathname} context={{}}> <App /> </StaticRouter> ); if (navigator.serviceWorker.controller) { navigator.serviceWorker.controller.postMessage({ type: "serverRenderContent", pathname: event.data.pathname, content: ssrResult }); } }); } } }); }
这里需要优化,利用 web worker 执行 ssr 才可以用于生产环境。
最后,等待用户的下一次刷新,service worker 会帮我们把 ssr 内容作为首屏给用户一个惊喜的。
同样这次只是抛砖引玉,希望大家能提出建议一起帮助我们完善这个方案。
此方案正式用在生产环境后,会再写一篇文章介绍实践过程。
本文作者:前端小毛
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!