Skip to content

Commit fc89b2f

Browse files
committed
code formatting and write up
1 parent d4f688b commit fc89b2f

File tree

9 files changed

+104
-60
lines changed

9 files changed

+104
-60
lines changed

ClientApp/client.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@ router.onReady(() => {
1313
const matched = router.getMatchedComponents(to)
1414
const prevMatched = router.getMatchedComponents(from)
1515

16+
// compare two list of components from previous route and current route
1617
let diffed = false
1718
const activated = matched.filter((c, i) => {
1819
return diffed || (diffed = (prevMatched[i] !== c))
1920
})
2021

22+
// if no new components loaded, do nothing
2123
if (!activated.length) {
2224
return next()
2325
}
2426

2527
NProgress.start()
2628

29+
// for each newly loaded components, asynchorously load data to them
2730
Promise.all(activated.map(c => {
2831
if (c.asyncData) {
2932
return c.asyncData({store, route: to})

ClientApp/components/Messages.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export default {
1414
computed: mapGetters(['messages', 'lastFetchedMessageDate']),
1515
methods:
1616
mapActions(['fetchMessages']),
17-
asyncData ({ $store }) {
18-
return $store.dispatch('fetchInitialMessages')
17+
asyncData ({ store }) {
18+
return store.dispatch('fetchInitialMessages')
1919
}
2020
}
2121
</script>

ClientApp/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default context => {
77
router.onReady(() => {
88
const matchedComponents = router.getMatchedComponents()
99
if (!matchedComponents.length) {
10-
return reject({ code: 404 })
10+
return reject(new Error({ code: 404 }))
1111
}
1212
Promise.all(matchedComponents.map(Component => {
1313
if (Component.asyncData) {

ClientApp/vuex/store.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ const store = new Vuex.Store({
1010

1111
mutations: {
1212
INITIAL_MESSAGES: (state, payload) => {
13-
state.messages = payload.messages
14-
state.lastFetchedMessageDate = payload.lastFetchedMessageDate
15-
},
13+
state.messages = payload.messages
14+
state.lastFetchedMessageDate = payload.lastFetchedMessageDate
15+
},
1616
FETCH_MESSAGES: (state, payload) => {
17-
state.messages = state.messages.concat(payload)
18-
state.lastFetchedMessageDate = minBy(state.messages, 'date').date
19-
}
17+
state.messages = state.messages.concat(payload)
18+
state.lastFetchedMessageDate = minBy(state.messages, 'date').date
19+
}
2020
},
2121
actions: {
2222
fetchInitialMessages,

Controllers/HomeController.cs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ public class HomeController : Controller
1212
{
1313
public IActionResult Index()
1414
{
15+
return View();
16+
}
17+
18+
[Route("initialMessages")]
19+
public JsonResult initialMessages(){
20+
//Added to simulate initial loading from remote server
21+
Thread.Sleep(2000);
22+
1523
var initialMessages = FakeMessageStore.FakeMessages
1624
.OrderByDescending(m => m.Date)
1725
.Take(2);
@@ -21,26 +29,9 @@ public IActionResult Index()
2129
LastFetchedMessageDate = initialMessages.Last().Date
2230
};
2331

24-
return View(initialValues);
32+
return Json(initialValues);
2533
}
2634

27-
// [Route("initialMessages")]
28-
// public JsonResult initialMessages(){
29-
// //Added to simulate initial loading from remote server
30-
// Thread.Sleep(2000);
31-
32-
// var initialMessages = FakeMessageStore.FakeMessages
33-
// .OrderByDescending(m => m.Date)
34-
// .Take(2);
35-
36-
// var initialValues = new ClientState(){
37-
// Messages = initialMessages,
38-
// LastFetchedMessageDate = initialMessages.Last().Date
39-
// };
40-
41-
// return Json(initialValues);
42-
// }
43-
4435
[Route("fetchMessages")]
4536
public JsonResult FetchMessages(DateTime lastFetchedMessageDate){
4637
return Json(FakeMessageStore.FakeMessages.OrderByDescending(m => m.Date)

Models/FakeMessageStore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
using System;
33

44
public class FakeMessageStore {
5-
private static DateTime startDateTime = new DateTime(2017, 11, 24);
5+
private static DateTime startDateTime = DateTime.Now;
66

77
public static readonly List<Message> FakeMessages = new List<Message>(){
88
Message.CreateMessage("First message title", "First message text", startDateTime),
99
Message.CreateMessage("2nd msg title", "2nd msg txt", startDateTime.AddDays(1)),
1010
Message.CreateMessage("3rd msg title", "3rd msg txt", startDateTime.AddDays(2)),
1111
Message.CreateMessage("4th msg title", "4th msg txt", startDateTime.AddDays(3)),
1212
Message.CreateMessage("5th msg title", "5th msg txt", startDateTime.AddDays(4)),
13-
Message.CreateMessage("6th msg title", "6th msg txy", startDateTime.AddDays(5))
13+
Message.CreateMessage("6th msg title", "6th msg txt", startDateTime.AddDays(5))
1414
};
1515
}

README.md

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
This repository is a clone from Stu Ratcliffe [Server rendering Vue.js applications with ASP.NET Core](https://github.com/sturatcliffe/VueDotnetSSR)
1+
# README
22

3-
Following his articile and trying to have some write up. (e.g. using latest packages in package.json)
3+
This repository was built from scratch following the steps described in the original blog post *[Server rendering Vue.js applications with ASP.NET Core](https://stu.ratcliffe.io/2017/07/20/vuejs-serverside-rendering-with-aspnet-core)* from Stu Ratcliffe. The aim of this repository is to adding more steps and comments from my experience following the steps that make it works as a supplement of the original blog post. Hope this helps if you also read the same blog post and got stuck some way. :)
4+
5+
You can get the complete code repo made by Stu Ratcliffe from [[Here]](https://github.com/sturatcliffe/VueDotnetSSR)
46

57
## Install and run:
68
if Webpack is not installed yet:
@@ -13,10 +15,27 @@ else
1315
webpack
1416
dotnet run
1517

16-
## Packages used:
18+
## Packages used: *The description may not accurate, just my understanding*
19+
- vue <- VueJS
1720
- lodash <- similiar to numpy in Python, utilies library for manipulating array/object.
1821
- axios <- Promise based HTTP client for the browser and Node.js, think of $.ajax() if you come from jQuery world
19-
- vuex <- Vue variant of Flux implementation, just like Redux in React
22+
- vuex <- Vue variant of Flux implementation, just like Redux in React
23+
- nprogress <- loading indiciator
24+
- vue-router <- enable client side "page" routing
25+
- vue-server-renderer <- enable Server Side Rendering
26+
- aspnet-prerendering <- enable ASP.NET to trigger Node for SSR
27+
28+
- webpack <- pack your JavaScript files into bundles for faster loading, remove duplicate imports, reduce final code size
29+
- webpack-merge <- merge webpack config so common configuration attributes can be shared among configurations
30+
- webpack-hot-middleware <- enable hot reload of code changes
31+
- aspnet-webpack <- enable ASP.NET to execute webpack on demand during runtime.
32+
- vue-loader <- required for packing vuejs code
33+
- css-loader <- required if you have css file to pack
34+
- style-loader <- required if you use template with style
35+
- json-loader <- requiredd if you need to pack json file
36+
- vue-template-compiler <- required for packing if you use template in vue components
37+
- babel-* <- transpile code syntax used in Vue into browser understandable version before packing
38+
2039
### Working with data:
2140
- .Net part
2241
1. Message.cs
@@ -25,7 +44,8 @@ else
2544
- Vue part
2645
0. (create ClientApp/vuex folder)
2746
1. ClientApp/vuex/action.js
28-
2. ClientApp/vuex/store.js
47+
2. ClientApp/vuex/store.js
48+
2949
### Client side routing:
3050
0. Move to ClientApp/components
3151
1. Dashboard.vue
@@ -37,7 +57,8 @@ else
3757
7. Move back to ClientApp folder
3858
8. Modify app.js
3959
9. Move back to .
40-
10. Modify Startup.cs
60+
10. Modify Startup.cs
61+
4162
### Loading indicator:
4263
Here we try to modify the implementation order different from the original post.
4364
We are going to add the loading indicator before implementing the Server Side Rendering.
@@ -67,4 +88,35 @@ To simulate timely API call form remote server, we add the following line in Hom
6788
loader: "style-loader!css-loader"
6889
}
6990

70-
3. Modify ClientApp/vuex/actions.js, add `NProgress.start()` and `NProgress.done()` before and after axios remote call.
91+
3. Modify `ClientApp/vuex/actions.js`, add `NProgress.start()` and `NProgress.done()` before and after axios remote call.
92+
93+
### Server Side Renderering (SSR)
94+
95+
1. Add the following dependencies to package.json:
96+
97+
- devDependencies:
98+
- aspnet-webpack
99+
- webpack-merge
100+
- dependencies:
101+
- vue-server-renderer
102+
- aspnet-prerenderer *(Note that only version ^1.0.0 is supported, using latest ^3.0.0+ will break the code.)*
103+
104+
2. Split the code into two part:
105+
106+
1. `server.js` <- this will load by renderOnServer.js, which aspnet-prerendering will trigger Node to execute and return pre-rendered result back to renderOnServer.js and thus send to browser as initial state of app.
107+
2. `client.js` <- once initial app state rendered and injected in the resulting index.cshtml, the script tag will load client.js and mount it to pre-rendered app tag.
108+
109+
3. Create Node server code for ASP.NET Core to trigger the Node hosting service to execute
110+
111+
- `rendererOnServer.js` <- responsible for loading the webpacked server.js for Node to render the initial app state.
112+
113+
4. ASP.NET Core part
114+
115+
- Edit `Views/Home/index.cshtml` app tag to use `aspnet-prerendering` attributes
116+
117+
5. Webpack Configuration
118+
119+
- Edit `webpack.config.js`, make use of `webpack-merge` to split the original configuration into two sets.
120+
121+
### Thoughts:
122+
SSR was by far the most difficult part of my VueJS journey, it takes more than half of the time of my VueJS learning. Whether to use Server Side Rendering or not is highly optional, you don't need it to write a cool SPA. The performance and user experience gain is arguablely worth the complexity and develop time involved.

Views/Home/Index.cshtml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
@model ClientState
2-
<div id="app" asp-prerender-module="ClientApp/renderOnServer" asp-prerenderer-data="Model"></div>
1+
<div id="app" asp-prerender-module="ClientApp/renderOnServer"></div>
32
<script src="~/dist/main-client.js" asp-append-version="true"></script>

webpack.config.js

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const path = require('path')
2-
// const webpack = require('webpack')
32

43
// using webpack-merge so we don't have to repeat common configuration attributes twice
54
const merge = require('webpack-merge')
@@ -33,28 +32,28 @@ module.exports = (env) => {
3332
})
3433

3534
const clientBundleConfig = merge(sharedConfig(), {
36-
entry: { 'main-client': './ClientApp/client.js' },
37-
output: {
38-
path: path.join(__dirname, 'wwwroot/dist')
39-
}
40-
});
35+
entry: { 'main-client': './ClientApp/client.js' },
36+
output: {
37+
path: path.join(__dirname, 'wwwroot/dist')
38+
}
39+
})
4140

4241
const serverBundleConfig = merge(sharedConfig(), {
43-
target: 'node',
44-
entry: { 'main-server': './ClientApp/server.js' },
45-
output: {
46-
libraryTarget: 'commonjs2',
47-
path: path.join(__dirname, 'wwwroot/dist')
48-
},
49-
module: {
50-
rules: [
51-
{
52-
test: /\.json?$/,
53-
loader: 'json-loader'
54-
}
55-
]
56-
},
57-
});
42+
target: 'node',
43+
entry: { 'main-server': './ClientApp/server.js' },
44+
output: {
45+
libraryTarget: 'commonjs2',
46+
path: path.join(__dirname, 'wwwroot/dist')
47+
},
48+
module: {
49+
rules: [
50+
{
51+
test: /\.json?$/,
52+
loader: 'json-loader'
53+
}
54+
]
55+
}
56+
})
5857

59-
return [clientBundleConfig, serverBundleConfig];
58+
return [clientBundleConfig, serverBundleConfig]
6059
}

0 commit comments

Comments
 (0)