GiftOasis Technology Blog
GiftOasis Technology Blog

Using VueJs with ASP.NET Razor Can be Great!

May 6, 2019
By Ron Clabo

I have been using VueJs with ASP.NET Core Razor for a couple years now and I find that the two technologies work well together. If you are interested in why I selected Vue, you can read about that here: Why I picked VueJs for my Asp.Net Core Web development. As I mention in that post, Vue works really well as a way to add reactiveness to web pages, and that’s true even for HTML generated using Razor. So that’s what I want to talk about. If that sounds interesting, keep reading.

Web Page Approach, not SPA

My development work primarily involves building out an ecommerce website that has hundreds of pages. I prefer the web page approach rather than the SPA (single page app) approach for such development.

I have this preference for the following reasons:

  1. Ranking well in search engines is important for the work that I do and most crawlers index the html returned by the server without executing any of the related javascript. Google is one of the most sophisticated crawlers and it does execute some javascript but only if that javascript loads and executes very quickly. So I prefer to return pages from the server that contain all the vital information for ranking well even if the javascript for the page is never executed.
  2. All things equal (they rarely are) I prefer to code in C# rather than Javascript. There are a couple reasons for this: 1) in a strongly type language like C# the compiler is a lot more helpful in finding bugs which typically results in a faster development cycle, and 2) from a security perspective backend code tends to be far more trustworthy then client-side code. After all, it’s often said that the first rule of security is never trust the client data.

Loading Vue.js Directly, without webpack

I chose to load Vue as a single file from a CDN rather embracing a module bundler like webpack. Early on I looked in to webpack and thought perhaps I might utilize the SPA approach for non-public areas of the website like the “My Account” area. However I found webpack configuration to be complicated and not well documented. It’s important to me that the front end code base is approachable to junior developers and introducing webpack felt like a step in the wrong direction.

So in the end I chose the road less traveled and opted for using vue.js without webpack or any other module loading system. This of course has some downsides. For one it means that the javascript must be written in (gasp!) ES5 rather than ES6. But I’m fine with that, and if I wasn’t I’d probably go with TypeScript. Maybe someday.

Another downside, and this is also a biggie, is that this approach means that I can’t use most open source Vue Components straight away without modification. The use of webpack and ES6 is so ubiquitous at this point that most open source Vue Components are using it. Again, for me, that’s an OK tradeoff. Vue Components are still and option, they just need to be written in ES5 in a manner that works with vue.js directly rather than needing webpack.

ASP.NET Razor vs Vue.js

Since I do use both ASP.NET Razor and Vue.js together, I often have a choice of whether I should use Vue or Razor for a given aspect of generating the user experience. Let me start by saying that I find both Razor syntax and Vue syntax to be very intuitive, so that’s not typically a driver in the decision. As you may guess, my first choice is almost Razor (for SEO and security reasons) unless the level of interactivity needed on the page requires that Vue be used for that aspect. And even when that’s the case, if it’s a public facing page, I tend to use Razor to fully generate the initial rendering of the page for SEO reasons. The HTML sent to the browser will contains Vue attributes and once Vue takes control of the page it can provide reactivity and can reconfigure the DOM as much as needed.

Even for non-public facing pages, I will use Razor right up to the point where I can’t get the desired behavior without using Vue. So again Razor will generate much of the page with the HTML including Vue attributes and then when the page loads, Vue will take it from there.

I find that in practice this approach works well, and often the question isn’t really should I use Razor more heavily or Vue more heavily on this page, the question typically is more along the lines of “what are the features and use cases that need supported by this page?” And then the technology to lean more heavily on is often obvious.

Architectural Approach Overview

When implementing Vue on the website the first decision I had to make was whether vue would be used on every page of the website or just some of the pages. But once I realized that I’d like to use it for functionality in the header of the page like signing into an account and displaying how many items are in the cart, it became clear that I would be using vue on ever page of the website.

The _layout.cshtml

We are all use to the idea that common areas of the website are defined in the _layout.cshtml file. Typically this is where the HTML for the header and footer portions of the site are located. So to me it just makes sense that if the site will be using Vue to support functionality manifested in the header, then one good place to instantiate vue is in the _layout.cshtml file. So that’s the approach I chose. A simplified example might be something like this:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="~/css/site.css" />
    @RenderSection("Head", required: false)
</head>
<body>
    <div id="vueAppTemplate">
        @RenderBody()
    </div>

    <script src="https://unpkg.com/vue@2.5.16/dist/vue.min.js" asp-fallback-src="/js/vendor/vue-2.5.16.min.js" crossorigin="anonymous"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)

    <script type="text/javascript">
        var app = new Vue({
            el: '#vueAppTemplate',
            data: {
                appMessage: 'Hello World'
                //add more data properties here
            },
            methods: {
                //add methods here
            }
        });
    </script>
</body>
</html>

Views/Pages

Given that the _layout.cshtml file is responsible for setting up vue, every page that uses that layout can count on the fact that vue will be available without needing to do anything to make that happen. Also, the page will have access to any vue data, methods, or components that are declared in the _layout.cshtml file when vue is instantiated. That feels like the way it should be. Here’s an example of what a page.cshtml might look like:

@page
@{
    Layout = "_Layout";
}

@section Head{
    <style>
        .buttonWrap { margin-top: 20px }
    </style>
}

<div>
    <h1>Simple Demo</h1>

    Message from vue: <span v-text="appMessage"></span><br />

    <div class="buttonWrap">
        <button type="button" v-if="displayButton1">Button 1</button>
        <button type="button" v-if="displayButton2">Button 2</button>
    </div>
</div>


@section Scripts {
    <script type="text/javascript">
        //Note that these are regular javascript variables not Vue 
        //properties so they are not reactive.
        var displayButton1 = true;
        var displayButton2 = false;
    </script>
}

Example 1

The first thing to notice is that the page doesn’t have to do any setup for vue other than specifying to use _layout.cshtml. Then notice that Vue is being used in two ways on this page.

  1. The appMessage data property is being displayed on the page
  2. The v-if vue directive is used to display only one of the two buttons.

Page Mixins

But what if the page needs to have some page specific vue data or functionality? Vue Mixins to the rescue! Vue has this cool concept called a Mixin. In a Mixin you can define most anything that can be defined via the options passed when the Vue instance is created including: data, methods, computed properties, event hooks, and such.

So the approach I chose for implementing page specific vue data and functionality was to create a pageMixin on that page and push that mixin into a global mixin array which the _layout.cshtml page utilizes when it creates the vue instance. A simplified example of that might look something like this:

_Layout_Example2.cshtml

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="~/css/site.css" />
    @RenderSection("Head", required: false)
</head>
<body>
    <div id="vueAppTemplate">
        @RenderBody()
    </div>

    <script src="https://unpkg.com/vue@2.5.16/dist/vue.min.js" asp-fallback-src="/js/vendor/vue-2.5.16.min.js" crossorigin="anonymous"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)

    <script type="text/javascript">
        var app = new Vue({
            el: '#vueAppTemplate',
            mixins: mixinArray,    //defined in site.js
            data: {
                appMessage: 'Hello World'
                //add more data properties here
            },
            methods: {
                //add methods here
            }
        });
    </script>
</body>
</html>

site.js

var mixinArray = [];

Example2.cshtml

@page
@{
    Layout = "_Layout_Example2";
}

@section Head{
    <style>
        .buttonWrap { margin-top: 20px }
    </style>
}

<div>
    <h1>Example 2</h1>
    Message from vue: <span v-text="appMessage"></span><br />

    <div class="buttonWrap">
        <button type="button" v-if="displayButton1">Button 1</button>
        <button type="button" v-if="displayButton2">Button 2</button>
    </div>

    <div class="buttonWrap">
        Button 1 Visibility: <button type="button" v-on:click="displayButton1=!displayButton1">Toggle 1</button><br />
        Button 2 Visibility: <button type="button" v-on:click="displayButton2=!displayButton2">Toggle 2</button><br />
    </div>
</div>


@section Scripts {
    <script type="text/javascript">
        //Note that these are regular javascript variables not Vue properties 
        //so they are not reactive.
        var pageMixin = {
            data: function () {
                return {
                    //These are reactive.  Changing them will change whether 
                    //the related button is displayed
                    displayButton1: true,
                    displayButton2: false
                };
            },
            methods: {
                //vue methods can be added here
            }
        };

        mixinArray.push(pageMixin);
    </script>
}

In this, example 2, we can see how a mixin can be used to provide page specific Vue data. We can also see that data used in a simple way to create reactivity.

Example 2

Then the “Toggle 1” button is clicked it will toggle whether Button 1 is visible or not. As you can see the code to make this happen is trivial and begins to show the power of the reactivity system.

Some care does need to be taken so that the names of methods and data properties don’t conflict between the mixin and the vue instance options. In cases where the do conflict the Vue instance options will prevail.

Mixin for Group of Pages

Taking this a step farther, it’s even possible to have the page add multiple mixins to the global mixin array. So let’s say that a particular group of web pages all need access to come common vue methods that have been defined for functionality on those pages. Let’s call that group of pages account pages. It’s perfectly reasonable to have a accountMixin that is declared in a .js file that is included in each of those files via a script tag and then in a javascript blog on those pages they add the accountMixin to the mixin array just like they add the pageMixin to the mixin array. I think you can start to see that this approach is fairly powerful.

Again, it’s important to avoid have duplicate names for data, methods, computed properties, etc amongst the mixins used by the page and the options used to create the vue instance. If there is a name collision amongst the mixins, then the last one in the mixin array wins. This works out to be kind of a feature in that it makes it possible for a method in one mixin to be overridden by another mixin provided that other mixin is loaded afterwards into the mixin array. In practice, even on a large webstite, I haven’t had much of an issue avoiding name collisions and I’ve yet to need to override a method in one mixin with the method in another but this approach does make that a possibility if needed.

Vue Components

Its also easy to specify a vue component for use globally by including it's declaration in the _layout.cshtml file, or to specify a view component to be used on a page by declaring it on that page or declaring it in a partial view that is included in the page. Perhaps in the future I will do a blog article on this.

In Closing

I hope that this blog post has helped you see how this non-traditional approach to integrating Vue with Razor can create interesting possibilities. Once this architecture has been implemented, each page has the power of Razor and the power of Vue at its disposal. I hope you will give this approach a try and let me know what you think. This blog doesn’t currently have commenting ability but you can tweet me your thoughts at @dotnetcore. Have fun with it.

The code for this blog post is available on github.

Ron Clabo
Founder & President
Ron is the founder and President of GiftOasis. He is an Internet savvy entrepreneur who has extensive experiences in e-commerce, direct marketing and software development. He is passionate about small businesses including makers, designers, and artisans. Ron is as comfortable developing software as he is marketing products and analyzing business models.
Unique gifts made by small businesses in North Carolina
© 2019 GiftOasis LLC, All Rights Reserved