Skip to content

Commit 291ac20

Browse files
committed
Improved Vue3/async rendering after looking at code in a fork (thanks)
1 parent 66b81ce commit 291ac20

14 files changed

Lines changed: 234 additions & 137 deletions

File tree

src/SSR.Net.DotNet8/Controllers/HomeController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ public ActionResult React19() {
7878
return View(renderedComponent);
7979
}
8080

81-
public ActionResult Vue3() {
81+
public async Task<ActionResult> Vue3() {
8282
var propsJson = JsonConvert.SerializeObject(
8383
new {
8484
title = "Vue 3 with SSR"
8585
});
86-
var renderedComponent = _vue3Renderer.RenderComponent("Components.Example", propsJson);
86+
var renderedComponent = await _vue3Renderer.RenderComponentAsync("Components.Example", propsJson);
8787
return View(renderedComponent);
8888
}
8989
}

src/SSR.Net.DotNet8/Services/ServiceCollectionExtensionsVue3SSR.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public static void AddVue3Renderer(this IServiceCollection services, IWebHostEnv
1010
var pool = new JavaScriptEnginePool(new V8JsEngineFactory(), config =>
1111
config
1212
.AddScriptFile(Path.Combine(webHostEnvironment.WebRootPath, "vue3example.js"))
13+
.WithAsync()
1314
);
1415
services.AddSingleton(new Vue3Renderer(pool));
1516
}

src/SSR.Net.DotNetFramework/App_Start/Vue3SSR.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Newtonsoft.Json;
33
using SSR.Net.Models;
44
using SSR.Net.Services;
5+
using System.Threading.Tasks;
56
using System.Web;
67

78
namespace SSR.Net.Vue3DotNetFramework
@@ -15,11 +16,12 @@ public static void ConfigureVue3()
1516
var pool = new JavaScriptEnginePool(new V8JsEngineFactory(), config =>
1617
config
1718
.AddScriptFile(HttpContext.Current.Server.MapPath("~/Frontend/vue3example.js"))
19+
.WithAsync()
1820
);
1921
Renderer = new Vue3Renderer(pool);
2022
}
2123

22-
public static RenderedComponent Render(string componentName, object props) =>
23-
Renderer.RenderComponent(componentName, JsonConvert.SerializeObject(props));
24+
public static async Task<RenderedComponent> RenderAsync(string componentName, object props) =>
25+
await Renderer.RenderComponentAsync(componentName, JsonConvert.SerializeObject(props));
2426
}
2527
}
Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,77 @@
1-
using System.Web.Mvc;
1+
using SSR.Net.React17DotNetFramework;
2+
using SSR.Net.React18DotNetFramework;
3+
using SSR.Net.React19DotNetFramework;
4+
using SSR.Net.Vue3DotNetFramework;
5+
using System.Threading.Tasks;
6+
using System.Web.Mvc;
27

38
namespace SSR.Net.DotNetFramework.Controllers
49
{
510
public class HomeController : Controller
611
{
712
public ActionResult Index() => View();
813

9-
public ActionResult React17() => View();
14+
public ActionResult React17()
15+
{
16+
var component = React17SSR.Render("Components.FrontPage", new {
17+
header = "React 17 with SSR",
18+
links = new[]{
19+
new {
20+
text = "Google.com",
21+
href = "https://www.google.com"
22+
},
23+
new {
24+
text = "Hacker news",
25+
href = "https://news.ycombinator.org"
26+
}
27+
}
28+
});
29+
return View(component);
30+
}
1031

11-
public ActionResult React18() => View();
32+
public ActionResult React18()
33+
{
34+
var component = React18SSR.Render("Components.FrontPage", new {
35+
header = "React 18 with SSR",
36+
links = new[]{
37+
new {
38+
text = "Google.com",
39+
href = "https://www.google.com"
40+
},
41+
new {
42+
text = "Hacker news",
43+
href ="https://news.ycombinator.org"
44+
}
45+
}
46+
});
47+
return View(component);
48+
}
1249

13-
public ActionResult React19() => View();
50+
public ActionResult React19()
51+
{
52+
var component = React19SSR.Render("Components.FrontPage", new {
53+
header = "React 19 with SSR",
54+
links = new[]{
55+
new {
56+
text= "Google.com",
57+
href="https://www.google.com"
58+
},
59+
new {
60+
text = "Hacker news",
61+
href="https://news.ycombinator.org"
62+
}
63+
}
64+
});
65+
return View(component);
66+
}
1467

15-
public ActionResult Vue3() => View();
68+
public async Task<ActionResult> Vue3()
69+
{
70+
var component = await Vue3SSR.RenderAsync(
71+
"Components.Example", new {
72+
title = "Vue 3 with SSR"
73+
});
74+
return View(component);
75+
}
1676
}
1777
}
Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
1-
@{
1+
@model SSR.Net.Models.RenderedComponent
2+
@{
23
Layout = null;
3-
var component = React17SSR.Render("Components.FrontPage", new
4-
{
5-
header = "React 17 with SSR",
6-
links = new[]{
7-
new {
8-
text= "Google.com",
9-
href="https://www.google.com"
10-
},
11-
new {
12-
text = "Hacker news",
13-
href="https://news.ycombinator.org"
14-
}
15-
}
16-
});
174
}
185

196
<!DOCTYPE html>
@@ -25,7 +12,7 @@
2512
@Scripts.Render("/Frontend/react17example.js")
2613
</head>
2714
<body>
28-
@Html.Raw(component.Html)
29-
<script>@Html.Raw(component.InitScript)</script>
15+
@Html.Raw(Model.Html)
16+
<script>@Html.Raw(Model.InitScript)</script>
3017
</body>
3118
</html>
Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
1-
@{
1+
@model SSR.Net.Models.RenderedComponent
2+
@{
23
Layout = null;
3-
var component = React18SSR.Render("Components.FrontPage", new
4-
{
5-
header = "React 18 with SSR",
6-
links = new[]{
7-
new {
8-
text= "Google.com",
9-
href="https://www.google.com"
10-
},
11-
new {
12-
text = "Hacker news",
13-
href="https://news.ycombinator.org"
14-
}
15-
}
16-
});
174
}
185

196
<!DOCTYPE html>
@@ -25,7 +12,7 @@
2512
@Scripts.Render("/Frontend/react18example.js")
2613
</head>
2714
<body>
28-
@Html.Raw(component.Html)
29-
<script>@Html.Raw(component.InitScript)</script>
15+
@Html.Raw(Model.Html)
16+
<script>@Html.Raw(Model.InitScript)</script>
3017
</body>
3118
</html>
Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
1-
@{
1+
@model SSR.Net.Models.RenderedComponent
2+
@{
23
Layout = null;
3-
var component = SSR.Net.React19DotNetFramework.React19SSR.Render("Components.FrontPage", new
4-
{
5-
header = "React 19 with SSR",
6-
links = new[]{
7-
new {
8-
text= "Google.com",
9-
href="https://www.google.com"
10-
},
11-
new {
12-
text = "Hacker news",
13-
href="https://news.ycombinator.org"
14-
}
15-
}
16-
});
174
}
185

196
<!DOCTYPE html>
@@ -25,7 +12,7 @@
2512
@Scripts.Render("/Frontend/react19example.js")
2613
</head>
2714
<body>
28-
@Html.Raw(component.Html)
29-
<script>@Html.Raw(component.InitScript)</script>
15+
@Html.Raw(Model.Html)
16+
<script>@Html.Raw(Model.InitScript)</script>
3017
</body>
3118
</html>
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
@{
1+
@model SSR.Net.Models.RenderedComponent
2+
@{
23
Layout = null;
3-
var component = Vue3SSR.Render("Components.Example", new
4-
{
5-
title = "Vue 3 with SSR"
6-
});
74
}
85

96
<!DOCTYPE html>
@@ -15,7 +12,7 @@
1512
@Scripts.Render("/Frontend/vue3example.js")
1613
</head>
1714
<body>
18-
@Html.Raw(component.Html)
19-
<script>@Html.Raw(component.InitScript)</script>
15+
@Html.Raw(Model.Html)
16+
<script>@Html.Raw(Model.InitScript)</script>
2017
</body>
2118
</html>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Threading.Tasks;
4+
5+
namespace SSR.Net.Models
6+
{
7+
public enum ResultCallbackState
8+
{
9+
AwaitingCode,
10+
AwaitingResult,
11+
ResultAvailable
12+
}
13+
14+
public class SSRNetResultCallback
15+
{
16+
private string _html { get; set; }
17+
private string _error { get; set; }
18+
private ResultCallbackState _state { get; set; } = ResultCallbackState.AwaitingCode;
19+
private string _executionId { get; set; }
20+
private object _lockObject { get; } = new object();
21+
22+
public void SetExecutionId(string executionId)
23+
{
24+
lock (_lockObject) {
25+
_html = null;
26+
_error = null;
27+
_executionId = executionId;
28+
_state = ResultCallbackState.AwaitingResult;
29+
}
30+
}
31+
32+
public void SetHtml(string executionId, string html) =>
33+
SetResult(executionId, html, null);
34+
35+
public void SetError(string executionId, string error) =>
36+
SetResult(executionId, null, error);
37+
38+
private void SetResult(string executionId, string html, string error)
39+
{
40+
lock (_lockObject) {
41+
if (_state == ResultCallbackState.AwaitingResult && _executionId == executionId) {
42+
_html = html;
43+
_error = error;
44+
_state = ResultCallbackState.ResultAvailable;
45+
}
46+
}
47+
}
48+
49+
public async Task AwaitResult(int timeoutMs)
50+
{
51+
var sw = Stopwatch.StartNew();
52+
while (_state != ResultCallbackState.ResultAvailable && sw.ElapsedMilliseconds < timeoutMs)
53+
await Task.Delay(1);
54+
lock (_lockObject) {
55+
if (_state != ResultCallbackState.ResultAvailable) {
56+
_state = ResultCallbackState.ResultAvailable;
57+
_error = $"Timeout after {timeoutMs}ms";
58+
}
59+
}
60+
}
61+
62+
public bool HasHtml() => !(_html is null);
63+
64+
public bool HasError() => !(_error is null);
65+
66+
internal string RetrieveResult(string executionId)
67+
{
68+
lock (_lockObject) {
69+
if (_state != ResultCallbackState.ResultAvailable)
70+
throw new InvalidOperationException("Result not available.");
71+
if (_executionId != executionId)
72+
throw new InvalidOperationException("Execution ID didn't match.");
73+
var result = _html;
74+
_html = null;
75+
_error = null;
76+
_executionId = null;
77+
_state = ResultCallbackState.AwaitingCode;
78+
return result;
79+
}
80+
}
81+
}
82+
}

src/SSR.Net/Services/IJavaScriptEnginePool.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
using SSR.Net.Models;
22
using System;
3+
using System.Threading.Tasks;
34

45
namespace SSR.Net.Services
56
{
67
public interface IJavaScriptEnginePool
78
{
89
string EvaluateJs(string js, int timeoutMs = 200);
9-
string EvaluateJsAsync(string js, string resultVariableName, int asyncTimeoutMs = 200, int timeoutMs = 200);
10+
Task<string> EvaluateJsAsync(string js, string resultVariableName, int asyncTimeoutMs = 200, int timeoutMs = 200);
1011
JavaScriptEnginePoolStats GetStats();
1112
JavaScriptEnginePool Reconfigure(Func<JavaScriptEnginePoolConfig, JavaScriptEnginePoolConfig> config);
1213
}

0 commit comments

Comments
 (0)