render.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //! A shared pool of renderers for efficient server side rendering.
  2. use std::sync::Arc;
  3. use dioxus_core::VirtualDom;
  4. use dioxus_ssr::Renderer;
  5. use crate::prelude::ServeConfig;
  6. /// State used in server side rendering. This utilizes a pool of [`dioxus_ssr::Renderer`]s to cache static templates between renders.
  7. #[derive(Clone)]
  8. pub struct SSRState {
  9. // We keep a pool of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move
  10. renderers: Arc<object_pool::Pool<Renderer>>,
  11. }
  12. impl Default for SSRState {
  13. fn default() -> Self {
  14. Self {
  15. renderers: Arc::new(object_pool::Pool::new(10, pre_renderer)),
  16. }
  17. }
  18. }
  19. impl SSRState {
  20. /// Render the application to HTML.
  21. pub fn render<P: 'static + Clone + serde::Serialize>(&self, cfg: &ServeConfig<P>) -> String {
  22. let ServeConfig { app, props, .. } = cfg;
  23. let mut vdom = VirtualDom::new_with_props(*app, props.clone());
  24. let _ = vdom.rebuild();
  25. self.render_vdom(&vdom, cfg)
  26. }
  27. /// Render a VirtualDom to HTML.
  28. pub fn render_vdom<P: 'static + Clone + serde::Serialize>(
  29. &self,
  30. vdom: &VirtualDom,
  31. cfg: &ServeConfig<P>,
  32. ) -> String {
  33. let ServeConfig { index, .. } = cfg;
  34. let mut renderer = self.renderers.pull(pre_renderer);
  35. let mut html = String::new();
  36. html += &index.pre_main;
  37. let _ = renderer.render_to(&mut html, vdom);
  38. // serialize the props
  39. let _ = crate::props_html::serialize_props::encode_in_element(&cfg.props, &mut html);
  40. #[cfg(all(debug_assertions, feature = "hot-reload"))]
  41. {
  42. // In debug mode, we need to add a script to the page that will reload the page if the websocket disconnects to make full recompile hot reloads work
  43. let disconnect_js = r#"(function () {
  44. const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  45. const url = protocol + '//' + window.location.host + '/_dioxus/disconnect';
  46. const poll_interval = 1000;
  47. const reload_upon_connect = () => {
  48. console.log('Disconnected from server. Attempting to reconnect...');
  49. window.setTimeout(
  50. () => {
  51. // Try to reconnect to the websocket
  52. const ws = new WebSocket(url);
  53. ws.onopen = () => {
  54. // If we reconnect, reload the page
  55. window.location.reload();
  56. }
  57. // Otherwise, try again in a second
  58. reload_upon_connect();
  59. },
  60. poll_interval);
  61. };
  62. // on initial page load connect to the disconnect ws
  63. const ws = new WebSocket(url);
  64. // if we disconnect, start polling
  65. ws.onclose = reload_upon_connect;
  66. })()"#;
  67. html += r#"<script>"#;
  68. html += disconnect_js;
  69. html += r#"</script>"#;
  70. }
  71. html += &index.post_main;
  72. html
  73. }
  74. }
  75. fn pre_renderer() -> Renderer {
  76. let mut renderer = Renderer::default();
  77. renderer.pre_render = true;
  78. renderer
  79. }