Vue SFC’s in an ASP.NET MVC app

Kevin Cocquyt
4 min readFeb 8, 2019

--

Inspired by Cristi Jora and this article: https://medium.com/corebuild-software/vue-js-and-net-mvc-b5cede22862, I went on and tried to add the same functionalities in our boilerplate solution for future projects.

Between the time the article was posted and when I started my implementation, webpack went from v3 to v4 and Vue introduced the vue-template-compiler (additional to the vue-loader). Luckilly, most of the workings stayed the same… Writing SFC’s as well as adding them to your page with the created custom tags. The biggest challenge was moving over the webpack configuration (and doing something extra in my case) and that’s what I’m going to talk about.

When webpack went from v3 to v4, lots of the configuration settings were made easier (coming out of the box, like production mode which minifies your code), so you will see a ‘more or less’ slimmed down version of the configuration file as used in the mentioned article.

const path = require("path");
const fs = require("fs");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const appBasePath = "./js/components/";
const jsEntries = {};
fs.readdirSync(appBasePath).forEach(name => {
var indexFile = `${appBasePath}${name}/index.js`;
if (fs.existsSync(indexFile)) {
jsEntries[name] = indexFile;
}
});
module.exports = {
entry: jsEntries,
output: {
path: path.resolve(__dirname, "../dist/js/components"),
filename: "[name].js"
},
resolve: {
alias: {
vue$: "vue/dist/vue.esm.js"
}
},
module: {
rules: [
{
test: /\.vue$/,
use: {
loader: "vue-loader",
options: {
js: "babel-loader"
}
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
},
{
test: /\.(css|scss)$/,
use: ["vue-style-loader", "style-loader", "css-loader", "sass-loader"]
}
]
},
plugins: [new VueLoaderPlugin()],
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all"
}
}
}
},
devtool: "source-map"
};

As you can see, it starts out with the same loop creating the entries (see mentioned article for the detailed information). For output, I chose a subfolder ‘components’ within my ‘dist’ folder as it’s only used for compiling Vue components. After that, do not (!) forget the ‘resolve’ setting as, because I thought it wouldn’t be, it cost me half a day to figure out why my compiled .js files were not rendering my components. The ‘module’ settings that follow are more or less the same, except for some options here and there (adding an additional package or a simplified Babel 7 setup). ‘Plugins’ is a new part and introduced since one of the latest vue-loader updates. And, maybe the part where I’m most happy about is the optimization (see separate section underneath).

Optimization

When creating a .js file, all of the included SFC’s and imported node modules are bundled and minified into one big .js file. As I don’t need every component on every page, I create a .js file per page using the jsEntries logic. The downside with that is that every .js file would be bundled up with the Vue runtime meaning an extra +/- 100 KB per file. Luckilly, you can do some optimizations by creating an extra file (‘vendors.js’ in my case), containing all the common node modules that are used in the Vue components making for only one (cachable) file. That file can then be added in your _Layout page and the other .js files can be added per page resulting in a lot less KB’s that have to be downloaded by your users and making the components load faster too.

Without decoupling the common node modules, every page file would be +104KB

Cache busting

When you add the JS files to your webpages, those will be cached by the browser and not be updated when a new version arrives (as it’s cached on the name, which did not change). As you’re not using the built-in .NET MVC bundling module, this also means the automated cache busting technique is not applied (changing/concatenating your JS files with a hash which changes when you change the JS files). For that, you will have to implement your own style of cache busting and more information can be found here: https://madskristensen.net/blog/cache-busting-in-aspnet/

I prefer the solution with the assembly version (https://madskristensen.net/blog/cache-busting-in-aspnet/#546c01e8-b3c7-4b63-a863-a47de3dc1507) even when it means a querystring is added as it’s not best practice for Google Page Speed. Standard .NET MVC bundling is doing the exact same thing after all…

Build process

In case you want to use this in a CI/CD pipeline, this is how you could integrate this. I’m using Azure DevOps for this and in a Build pipeline, you can add an extra Task to execute an NPM command. Add this and set the command to “npm run build:components”. This will execute the webpack compilation and output the resulting JS files to a /dist folder. Those JS files are then referenced from the HTML. No standard ASP.NET MVC bundling (or minification) is used here as these things are done via webpack.

And that’s about it if you want to add Vue SFC’s to your existing ASP.NET MVC projects. I had a blog post before about Vue components (non-SFC) but it bothered me that I couldn’t use the styling bit of SFC’s so I searched on and finally came up with a solution I’m happy about.

For those interested, I added the necessary files to a github repo so you can just copy-paste the stuff from there: https://github.com/KevinCocquyt39/mvc-vue-webpack4

Happy coding!

--

--

Kevin Cocquyt

Loves hockey, his wife and all things related to web/app technologies…