未加星标

Vue.js server side rendering with ASP.NET Core

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二03 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

I really like working with Vue.js on the frontend, altough server side rendering have not been supported until the second version of it. In this sample I would like to show how to use Vue.js 2’s server-side rendering capabilities with ASP.NET Core. On the server side we use the Microsoft.AspNetCore.SpaServices package which offers ASP.NET Core APIs to invoke Node.js-hosted javascript code with context information and inject the resulting HTML string into the rendered page.

In this example the application will show a list of messages where the last two messages (ordered by date) will be rendered by the server. Tthe remaining messages can be downloaded from the server by clicking on the “Fetch a message” button.

The structure of the project looks like this:

. ├── VuejsSSRSample | ├── Properties | ├── References | ├── wwwroot | └── Dependencies ├── Controllers | └── HomeController.cs ├── Models | ├── ClientState.cs | ├── FakeMessageStore.cs | └── Message.cs ├── Views | ├── Home | | └── Index.cshtml | └── _ViewImports.cshtml ├── VueApp | ├── components | | ├── App.vue | | └── Message.vue | ├── vuex | | ├── actions.js | | └── store.js | ├── app.js | ├── client.js | ├── renderOnServer.js | └── server.js ├── .babelrc ├── appsettings.json ├── Dockerfile ├── packages.json ├── Program.cs ├── project.json ├── Startup.cs ├── web.config ├── webpack.client.config.js └── webpack.server.config.js

As you can see the Vue app is located under the VueApp folder which has two components, a simple Vuex store with one mutation and one action, and also have some other files which we are talking about next: app.js, client.js, renderOnServer.js, server.js.

Implement Vue.js server side rendering
Vue.js server side rendering with ASP.NET Core

To use server side rendering we have to create two different bundles from the Vue app: one for the server (which will run by Node.js) and one for the client which will run in the browser and hydrate the application on the client side.

app.js

Bootstrap the Vue instance in this module. It is used by both bundles.

import Vue from 'vue'; import App from './components/App.vue'; import store from './vuex/store.js'; const app = new Vue({ store, ...App }); export { app, store };

server.js

The entry point for the server bundle which exports a function with a context attribute which can be used to push any data from the render call.

import { app, store } from './app' // push context related data (in our case the initial state of the application) with the context attribute export default function(context) { return new Promise((resolve, reject) => { store.replaceState(context); resolve(app); }); };

client.js

The entry point for the client bundle which replaces the store’s current state with a global Javascript object called __INITIAL_STATE__ (this object will be created by the prerendering module) and it mounts the app to the specified element ( .my-app ).

import { app, store } from './app'; store.replaceState(__INITIAL_STATE__); app.$mount('.my-app');

Webpack Configuration

In order to create the bundles we have to add two Webpack configuration files (one for the server, one for the client build) without forgetting to install Webpack if you haven’t installed it yet: npm install -g webpack .

webpack.server.config.js const path = require('path'); module.exports = { target: 'node', entry: path.join(__dirname, 'VueApp/server.js'), output: { libraryTarget: 'commonjs2', path: path.join(__dirname, 'wwwroot/dist'), filename: 'bundle.server.js', }, module: { loaders: [ { test: /\.vue$/, loader: 'vue', }, { test: /\.js$/, loader: 'babel', include: __dirname, exclude: /node_modules/ }, { test: /\.json?$/, loader: 'json' } ] }, }; ___ webpack.client.config.js const path = require('path'); module.exports = { entry: path.join(__dirname, 'VueApp/client.js'), output: { path: path.join(__dirname, 'wwwroot/dist'), filename: 'bundle.client.js', }, module: { loaders: [ { test: /\.vue$/, loader: 'vue', }, { test: /\.js$/, loader: 'babel', include: __dirname, exclude: /node_modules/ }, ] }, }; ___

Run webpack --config webpack.server.config.js . If it is successfully finished the server bundle can be found at /wwwroot/dist/bundle.server.js . To get the client bundle run webpack --config webpack.client.config.js . The output can be found at /wwwroot/dist/bundle.client.js

Implement the Bundle Renderer

This module will be executed by ASP.NET Core and it is responsible for

rendering the server bundle which we had created before set the window.__INITIAL_STATE__ to the object sent from the server side

process.env.VUE_ENV = 'server'; const fs = require('fs'); const path = require('path'); const filePath = path.join(__dirname, '../wwwroot/dist/bundle.server.js') const code = fs.readFileSync(filePath, 'utf8'); const bundleRenderer = require('vue-server-renderer').createBundleRenderer(code) module.exports = function (params) { return new Promise(function (resolve, reject) { bundleRenderer.renderToString(params.data, (err, resultHtml) => { // params.data is the store's initial state. Sent by the asp-prerender-data attribute if (err) { reject(err.message); } resolve({ html: resultHtml, globals: { __INITIAL_STATE__: params.data // window.__INITIAL_STATE__ will be the initial state of the Vuex store } }); }); }); };

Implement the ASP.NET Core part

As I had mentioned before, we use the Microsoft.AspNetCore.SpaServices package which provides a few Tag Helpers to easily invoke Node.js-hosted Javascript (in the background SpaServices uses the Microsoft.AspNetCore.NodeServices package to execute Javascript).

Views/_ViewImports.cshtml

In order to use SpaServices’ TagHelpers we need to add them to the _ViewImports.

@addTagHelper "*, Microsoft.AspNetCore.SpaServices"

Home/Index

public IActionResult Index() { var initialMessages = FakeMessageStore.FakeMessages.OrderByDescending(m => m.Date).Take(2); var initialValues = new ClientState() { Messages = initialMessages, LastFetchedMessageDate = initialMessages.Last().Date }; return View(initialValues); }

It does fetch the two most recent Messages (order by date desc) from the MessageStore (just some static data for demo purposes) and create a ClientState object which will be used as the initial state of the Vuex store.

The Vuex store default state

const store = new Vuex.Store({ state: { messages: [], lastFetchedMessageDate: -1 }, // ... });

The ClientState class

public class ClientState { [JsonProperty(PropertyName = "messages")] public IEnumerable<Message> Messages { get; set; } [JsonProperty(PropertyName = "lastFetchedMessageDate")] public DateTime LastFetchedMessageDate { get; set; } } The Index View

Finally we have the initial state (from the server) and the Vue app so there is only one step left: render the Vue app with the initial values in the view by using asp-prerender-module and asp-prerender-data Tag Helpers.

@model VuejsSSRSample.Models.ClientState <!-- ... --> <body> <div class="my-app" asp-prerender-module="VueApp/renderOnServer" asp-prerender-data="Model"></div> <script src="~/dist/bundle.client.js" asp-append-version="true"></script> </body> <!-- ... -->

The asp-prerender-module attribute is used to specify which module we would like to render (in our case VueApp/renderOnServer). With the asp-prerender-data attribute we can specify an object which will be serialized and sent to the module’s default function as a parameter.

You can download the source code from http://github.com/mgyongyosi/VuejsSSRSample

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: JavaNode.jsHTMLDockerBootstrapTIVU
分页:12
转载请注明
本文标题:Vue.js server side rendering with ASP.NET Core
本站链接:http://www.codesec.net/view/483362.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(38)