1 引言
在服务端 ssr 成为常识的今天,前端 ssr 是我最近的新尝试,效果可以点击上面链接查看。说说前端 ssr 有哪些好处:
- 不消耗服务器资源。对,不消耗服务器资源,完美的分布式运行!对于百万 UV 甚至更高的页面,服务器成本减少了几十万或者上百万。
- 前后端分离,首先 ssr 不需要部署服务器,其次前端代码也不需要担心质量问题导致的内存泄露了,同时可以不必时刻注意使用同构的三方库,只需要考虑前端可运行!
- 不需要后端缓存服务,对于千人千面的复杂页面,对后端 ssr 来说缓存规模庞大的无法计算。
- 相比后端 ssr,在前端可以绕过复杂的权限系统,同时 http 请求的权限问题也无需关心。
- 因为第一点,对于不支持后端服务的 github pages 也能做到 ssr。
相对的,缺点是:
- 需要客户端支持 serviceWorker。
- 第二次首屏才会生效。后端 ssr 可以做到访问前预缓存 ssr 结果。
- 可能破坏前端页面状态,因为在同一个环境偷偷执行了一些页面逻辑。不过这个缺点可以通过 web worker 执行 ssr 解决,还在调研中。
- service worker 拦截入口 html 风险很高,一旦代码有故障可能导致严重后果,需要提前考虑完备的回滚方案。
像缓存清空时机等问题,前后端 ssr 都会遇到,所以不列在优缺点中。
2 精读
本篇精读分享的是前端 ssr 方案具体实现步骤。
我们先了解整体流程:
![](https://www.shuli.cc/wp-content/uploads/2023/08/image-171.png)
service worker 拦截首页
service worker 可以在浏览器尝试请求首屏 html 之前的时机拦截,此时如果 caches 命中,直接将 response 扔给浏览器,那么服务端将完全不会收到请求,完成了最高效的缓存命中。
当然第一次没有缓存,所以在没有命中缓存时,会同步的做两件事:
- 发送请求,拿到后端返回的 response,扔给浏览器。这是最普通的请求逻辑。
- 当前端代码 ready 后,postMessage 给浏览器,索要 ssr 内容。
附上代码片段:
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 中,比较简单就省略了。
浏览器执行 ssr
监听就不说了,主要是如何利用 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 内容作为首屏给用户一个惊喜的。
3 总结
同样这次只是抛砖引玉,希望大家能提出建议一起帮助我们完善这个方案。
此方案正式用在生产环境后,会再写一篇文章介绍实践过程。
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=17654,转载请注明出处。
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=17654,转载请注明出处。
评论0