This
article is a translation of this
article . In it, I will tell you how conveniently to use the Vue.js framework when developing an application on ASP.NET MVC
Introduction
For the past few months I have been looking at various JavaScript frameworks to integrate them into my MVC projects. As soon as you choose something in the spirit of React, Angular, or Ember for working with .NET, you must install adapter modules, rewrite all routing logic for all controllers. In the end, this affects the entire web application when you want to work side by side with a ready-made working stack. If you are starting a new project, it is advisable to use the Web API for the backend, which will provide the REST interface for the JS framework of your choice, but for existing MVC projects this is not an option.
After some research, I came across Vue.JS and after some experiments I managed to get it to work in conjunction with MVC. Vue.JS is a relatively lightweight framework, so I can add it to the view where I need additional JS features, thereby leaving the rest of the web application untouched. Now, when I use Vue in production, I’m happy to share with you some examples for everyone who shares my problems. My workflow borrows the concept of Migel's Castro [
tyk ] where he used it to integrate AngularJS with MVC.
Getting Started
- First we need an MVC project, create a standard MVC 5 project.
- We will allow npm to process all our packages, so create a package.json file in the project.
- Add VueJS dependencies
')
{ "version": "1.0.0", "Name": "ASP.NET", "private": true, "devDependencies": { }, "dependencies": { "vue": "^1.0.26" } }
- Save the file and Visual Studio itself will call the npm install command and then download all the necessary packages for you.
Now let's use Vue in our submissions
- In the default view (index.cshtml), I removed the standard markup and connected VueJS from the node-modules folder.
@{ ViewBag.Title = "Home Page"; } @Scripts.Render("~/node_modules/vue/dist/vue.min.js")
- Let's add an example of Hello World, from the VueJS documentation, to our application.
@{ ViewBag.Title = "Home Page"; } <div id="app"> </div> @Scripts.Render("~/node_modules/vue/dist/vue.min.js") <script> const v = new Vue({ el: '#app', data: { message: 'Hello Vue.js!' } }) </script>
- Let's run our project

Obviously, putting all your scripts in our views is a guaranteed path to headaches, so for now let's organize our code.
Getting Started
When we have a large application built on VueJS, I divide the project into parts. For each controller, I have a corresponding Vue application, which consists of several Vue components. The one-to-one relationship between the controller and the application makes our code more readable and easy to maintain. Each module of the application will contain only the JavaScript libraries that are needed, and not one large package.
JavaScript packers have improved over the years. Browserify allows you to use the style of node.js modules to work in the browser. We define the dependencies and then Browserify collects them into one small and clean JavaScript file. You include your javascript files using
require ("./ your_file.js"); expression. This allows us to use only those libraries that are needed. So now, if we assume that each controller is a container, each of which contains one or more js files. These files will then be placed in a package that will be placed in the folder of our project, which then loads and uses the browser.
I usually follow the following structure. All my Vue code is placed in the ViewModel folder. For each controller that uses Vue, I create a sub folder in the ViewModel folder, and then I call the containers in the
main.js file. After that, I use Gulp and Browserify to pack all the files in a package that is stored in the project folder. Views refer to our packages and when the browser requests the page the package is downloaded and launched.
Some practice
- In our project, I created a new folder ViewModels, and in it a folder Home that is responsible for the HomeController controller.
- In the ViewModel folder, I also created the main.js file.
- Now add some dependencies.
"devDependencies": { "browserify": "^13.0.0", "watchify": "^3.7.0", "gulp": "^3.9.1", "gulp-util": "^3.0.7", "gulp-babel": "^6.1.2", "gulp-uglify": "^2.0.0", "gulp-sourcemaps": "^1.6.0", "fs-path": "^0.0.22", "vinyl-source-stream": "^1.1.0", "vinyl-buffer": "^1.0.0", "babel-preset-es2015": "^6.13.2" }
- Add the gulp file to the project and paste this code:
const gulp = require('gulp'); const gutil = require('gulp-util'); var babel = require('gulp-babel'); var minify = require('gulp-uglify'); var sourcemaps = require('gulp-sourcemaps'); const fs = require('fs'); const path = require('path'); const browserify = require('browserify'); const watchify = require('watchify'); const fsPath = require('fs-path'); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); var es2015 = require('babel-preset-es2015'); function getFolders(dir) { return fs.readdirSync(dir) .filter(function (file) { return fs.statSync(path.join(dir, file)).isDirectory(); }); } const paths = [ process.env.INIT_CWD + '\\ViewModels\\home', process.env.INIT_CWD + '\\ViewModels\\home\\components', process.env.INIT_CWD + '\\ViewModels\\common\\components' ]; function watchFolder(input, output) { var b = browserify({ entries: [input], cache: {}, packageCache: {}, plugin: [watchify], basedir: process.env.INIT_CWD, paths: paths }); function bundle() { b.bundle() .pipe(source('bundle.js')) .pipe(buffer()) .pipe(sourcemaps.init({ loadMaps: true }))
- Also here we use watchify to monitor changes in the ViewModels folder, so any files that have been changed will be reassembled. All you need to do is refresh the page in the browser.
Now back to the code
- Let's transfer our Hello World application to main.js.
- Using browserify we can install dependencies
- The main.js file should look like this:
const Vue = require('vue'); const v = new Vue({ el: '#app', data: { message: 'Hello Vue.js!' } });
- In the index.cshtml file, change the links to our packaged package.
@{ ViewBag.Title = "Home Page"; } <div id="app"> { { message } } </div> @Scripts.Render("~/Scripts/app/home/bundle.js")
- Refresh page
- Since during the transition to Vue we will have to set up links for each controller, we can create some kind of universal template.
@{ var controllerName = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString(); } @Scripts.Render("~/Scripts/app/" + controllerName + "/bundle.js")
Loading data from the server
With Vue, we can extract and display data that comes from the server.
- Add some data to our controller.
public JsonResult GetData() { return Json(new { Name = "Marco", Surname = "Muscat", Description = "Vue data loaded from razor!" },JsonRequestBehavior.AllowGet); }
- Add jQuery to package.json and update your Vue application to retrieve data from the server
const Vue = require("vue"); const $ = require("jquery"); const v = new Vue({ el: '#app', ready: function () { this.loadData(); }, data: { message: 'Hello Vue.js!', serverData: null }, methods: { loadData: function (viewerUserId, posterUserId) { const that = this; $.ajax({ contentType: "application/json", dataType: "json", url: window.ServerUrl + "/Home/GetData", method: "GET", success: function (response) { console.log(response); that.$data.serverData = response; }, error: function () { console.log("Oops"); } }); } } })
- To connect to the server, I usually add the global variable window.ServerUrl for easy access to the current host. If you want to do something similar, just add the following code to the view file.
@{ var controllerName = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString(); var serverUrl = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~")); var controllerUrl = Url.Content("~") + controllerName; } <script> window.ServerUrl = '@serverUrl'; window.VueRouterUrl = '@controllerUrl'; </script> @Scripts.Render("~/Scripts/app/" + controllerName + "/bundle.js")
- Rebuild your project and run it.
- The next step is to display the received data.
<div id="app"> { { message } } <br/> <span>coming straight from mvc! { {serverData.Name} } { {serverData.Surname} }. { {serverData.Description} }</span> </div>
At the moment, this is enough to use vue.js and MVC, but, like any other frontend frameworks, vue suffers from delays when loading. When a page is requested, JavaScript must be loaded and running. Then the framework makes several more requests to the server to get the data. To prevent this, we must resort to loading animations and other hacks, but since we also use MVC, we can do better and speed up the loading process by using Razor and loading data to present along with the rest of the page.
Initial data load
Many JS frameworks are currently working on different implementations of pre-rendering, but with the .NET stack we can come up with an alternative solution.
A warning! This method is suitable if a small amount of data is involved in the initial load. In other cases, it is better to use pagination or AJAX requests to achieve a smoother page load and reduce delays.
Let's get down to creating. For this we will use the project that we did before.
public ActionResult Index() { var serverModel = JsonConvert.SerializeObject(new { Name = "Marco", Surname = "Muscat", Description = "Vue data loaded from razor!" }); return View(new SampleModel() { Data = serverModel }); } public class SampleModel { public string Name { get; set; } public string Surname { get; set; } public string Description { get; set; } public string Data { get; set; } }
- SampleModel is a simple class, we need it in order to facilitate binding in Razor.
- In Index.cshtml, we will load our data and serialize it into a JavaScript object.
<script> window.preLoadeddata = JSON.parse('@Html.Raw(Model.Data)')</script>
- Now let's tell Vue where to get the data from.
const Vue = require("vue"); const $ = require("jquery"); const v = new Vue({ el: '#app', ready: function () { }, data: { message: 'Hello Vue.js!', serverData: window.preLoadeddata }, methods: { } })
Routing
Our project can not be considered complete as there is no routing. More complex applications will require several presentations, since there is too much information on one page. Using Vue with MVC is awesome, since we can load all our views while staying on the same page without a full reload.
- To demonstrate, let's create another controller, I called it vuerouting.
- Create an index.cshtml view for this controller.
- Add the “vuerouting” folder in the viewmodels folder and the main.js file.
- Add vue-router to package.json.
- Finally, add this code to the main.js file.
const Vue = require("vue"); const VueRouter = require("vue-router"); Vue.use(VueRouter); var Foo = Vue.extend({ template: '<p>This is foo!</p>' }); var Bar = Vue.extend({ template: '<p>This is bar!</p>' }); var App = Vue.extend({}); var router = new VueRouter({ history: true, root: "/vue-example/vuerouting" }); router.map({ '/foo': { component: Foo }, '/bar': { component: Bar } }); router.start(App, '#app');
- Also add markup to the index.cshtml file.
<div id="app"> <h1>Hello App!</h1> <p> <a v-link="{ path: '/foo' }">Go to Foo</a> <a v-link="{ path: '/bar' }">Go to Bar</a> </p> <router-view></router-view> </div>
- After building the project, follow this path: localhost / vue-example / vuerouting
- Clicking on the links will load the corresponding component Vue and, if you look at the address bar, Vue adds a route to this component.
The result may seem very good, but that's not all. We still have a problem. If we copy our address with the Vue component (http: // localhost / vue-example / vuerouting / bar) and paste it into a new tab, we get a 404 error because the server is not able to find this route. We need to configure the routing on the server so that it ignores our Vue routes.
- In App_start / RouteConfig.cs, rewrite the code to the following:
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}/", defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional}, constraints:new { controller = "Home"} ); routes.MapRoute( name: "Silo Controller", url: "{controller}/{*.}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, constraints: new { controller = "examplevuerouter|ExampleSeedingRazor" } );
- The code above leaves the standard routing implementation in MVC, and also adds alternative routing for our Vue components.
- Our new route redirects us to the Index action of the specified controller while ignoring the rest of the route.
- After building the project, try switching to localhost / vue-example / vuerouting / bar and you will see that the page has completely loaded with our Bar component.
- Another thing that can be improved is the root values ​​in the Vue routing settings. In the example above, you will have to change the route value if you add another Vue module. Instead, we can add another JavaScript variable to our layout, as we did above with serverUrl. Actually, when we changed the layout of the project page, we already added window.VueRouterUrl. So we only need to change the code a bit:
var router = new VueRouter({ history: true, root: "/vue-example/vuerouting" });
var router = new VueRouter({ history: true, root: window.VueRouterUrl });
Link to the original:
tyk .
I hope that the information I provided in this article will help you in creating your projects using VueJS and MVC. If you have comments, questions and suggestions please leave them in the comments.
Thank you all for reading.