{"id":81179,"date":"2018-10-09T16:04:01","date_gmt":"2018-10-09T16:04:01","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=81179"},"modified":"2022-05-02T20:47:53","modified_gmt":"2022-05-02T20:47:53","slug":"introduction-to-vue-js-with-a-single-page-application-spa-in-visual-studio","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/javascript\/introduction-to-vue-js-with-a-single-page-application-spa-in-visual-studio\/","title":{"rendered":"Introduction to Vue.js with a Single Page Application (SPA) in Visual Studio"},"content":{"rendered":"<p>Get off my lawn! The crotchety old man inside me gets more and more crotchety each time I deal with the modern JavaScript development experience. I remember the good ol\u2019 days when JavaScript was simple and straightforward. You could toss it in a file and use it in your browser. Nowadays you need a package manager to find all your libraries, a transpiler to make your code work across browsers, and a module bundler to weave the disparate threads of confusion into a beautiful tapestry of perplexity. It can be confusing and frustrating to get started, or at least that was my experience the first time I used Vue JS. Developing in Vue JS is straightforward once you\u2019ve got all your packaging setup and working but getting to that point can be cumbersome.<\/p>\n<p>In this article, I will walk you through how to set up a Vue.js single page application in Visual Studio with a minimal amount of configuration. There is a tendency for JavaScript frameworks and libraries to rely on scaffolding tools for setup and configuration of an application. While this does get you up and running more quickly, it also alleviates you of understanding the settings and often leaves you with a bloated configuration. I was on a project that started out this way, and I had the satisfaction of eventually trimming the 20+ configuration files I started out with down to a much more manageable set of three.<\/p>\n<p>As such, you are going to build this from the ground up to ensure a thorough understanding of the underpinnings of the application. This includes:<\/p>\n<ul>\n<li>Package Management (Node JS)<\/li>\n<li>Module Bundling (Webpack)<\/li>\n<li>Linting (ESLint)<\/li>\n<li>Transpiling (Babel)<\/li>\n<li>Managing Configuration Settings for various Environments<\/li>\n<li>Routing<\/li>\n<\/ul>\n<p>Our objective is to build the groundwork for a .NET web application that exposes a single page application (SPA) built using Vue.js. The backend will not be implemented but is assumed to consist of Web API controllers that expose data to the application. This will rely on IIS Express to host the application, which allows you to just hit F5 from within Visual Studio to launch the application. With some minor modifications, you can also use the configurations outlined in this article with the webpack development server, or the server that you prefer.<\/p>\n<h2>Install Node JS<\/h2>\n<p>All the required JavaScript libraries are available as packages, and you will need a tool called Node Package Manager to help pull those packages down and use them. Node Package Manager is included with <a href=\"https:\/\/nodejs.org\/en\/about\/\">Node JS<\/a>, which is a JavaScript runtime engine for your computer. You can download Node JS from their website <a href=\"https:\/\/nodejs.org\">https:\/\/nodejs.org<\/a> and run the installer. After installing Node JS, you should be able to open PowerShell or a Command Prompt, type <code>npm<\/code><strong>,<\/strong> and get an output with usage instructions. If that is the case, you can close out PowerShell or the Command Prompt. If not, you will need to troubleshoot the Node JS installation.<\/p>\n<p>If you already have Node JS installed, then please ensure you are running version 8.11.2 or higher, and that Node Package Manager (NPM) is version 6.1.0 or higher. You can determine which versions are installed using the following commands:<\/p>\n<ul>\n<li>node -v<\/li>\n<li>npm version<\/li>\n<\/ul>\n<p>Some earlier editions of Node JS require additional configuration to run the scripts outlined in this article, so ensuring you have the latest editions will save you some trouble.<\/p>\n<h2>Install NPM Task Runner<\/h2>\n<p>Task Runner Explorer is a built-in Visual Studio tool that allows you to execute <a href=\"https:\/\/gulpjs.com\/\">Gulp<\/a> and <a href=\"https:\/\/gruntjs.com\/\">Grunt<\/a> tasks. It can also be extended to support NPM tasks by installing the NPM Task Runner extension, which you will want to use to run NPM tasks later in this article. To install, complete the following:<\/p>\n<ul>\n<li>Click <em>Tools &gt; Extensions and Updates<\/em> to display the Extension and Updates dialog<\/li>\n<li>Click <em>Online<\/em> from the tree on the left<\/li>\n<li>Type <em>NPM Task Runner<\/em> into the search box in the upper right-hand side of the dialog<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"959\" height=\"672\" class=\"wp-image-81196\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-57.png\" \/><\/p>\n<ul>\n<li>The dialog should update the list of extensions. and <em>NPM Task Runner<\/em> should appear in the list<\/li>\n<li>Click on the <em>NPM Task Runner<\/em> item to select it and show the download button (if you see a green checkbox<\/li>\n<li>Click the <em>Download<\/em> button (this button does not appear until you select the item).<\/li>\n<li>You may see the screen flicker slightly, and the <em>NPM Task Runner<\/em> item will be updated with a clock in the upper right corner: <br \/>\n<img loading=\"lazy\" decoding=\"async\" width=\"435\" height=\"87\" class=\"wp-image-81197\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-58.png\" \/> <br \/>\nAnd the following message will appear at the bottom of the dialog:<\/li>\n<\/ul>\n<p>\n<img loading=\"lazy\" decoding=\"async\" width=\"760\" height=\"45\" class=\"wp-image-81198\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-59.png\" \/><\/p>\n<ul>\n<li>Close all instances of Visual Studio and wait for the <em>VSIX Installer<\/em> to launch.<\/li>\n<li>When the <em>VSIX Installer<\/em> launches, click the <em>Modify<\/em> (or OK) button to install <em>NPM Task Runner.<\/em><\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"456\" height=\"350\" class=\"wp-image-81199\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-60.png\" \/><\/p>\n<ul>\n<li>When the installer finishes, click the <em>Close<\/em> button to close out the installer.<\/li>\n<\/ul>\n<p>At this point, the extension is installed and should display NPM tasks once they have been defined in the project.<\/p>\n<h2>Create a .NET Core Web Application<\/h2>\n<p>Next, you\u2019ll need to create a Visual Studio solution. I\u2019ve opted for a .NET Core Web Application because it provides a cleaner separation for the JavaScript application code and the files that are ultimately deployed to the web server. However, you can accomplish the same thing in another project type; you\u2019ll just want to make sure not to deploy your source application files to the web server.<\/p>\n<p>To make the web application<\/p>\n<ul>\n<li>Open Visual Studio<\/li>\n<li>Click <em>File New Project<\/em> to open the New Project dialog<\/li>\n<li>Click <em>Installed Visual C# Web<\/em> from the tree on the left to show the web project templates<\/li>\n<li>Choose the <em>ASP.NET Core Web Application<\/em> project template<\/li>\n<li>Give the application a name and make sure the location is appropriate<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"956\" height=\"671\" class=\"wp-image-81200\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-61.png\" \/><\/p>\n<ul>\n<li>Click <em>OK<\/em> to display the New ASP.NET Core Web Application dialog<\/li>\n<li>Choose the <em>API<\/em> tile to indicate you want to build a Web API application\n<ul>\n<li>You can choose Web Application or Web Application (Model-View-Controller) if you plan on using functionality from those template types.<\/li>\n<\/ul>\n<\/li>\n<li>Ensure <em>Enable Docker Support<\/em> is NOT checked.<\/li>\n<li>You can opt to <em>Configure for HTTPS<\/em> if you want to run the application over HTTPS.<\/li>\n<li>You can opt for whatever authentication is appropriate for your application.<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"804\" height=\"567\" class=\"wp-image-81201\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-62.png\" \/><\/p>\n<ul>\n<li>Click <em>OK<\/em> to create the project.<\/li>\n<\/ul>\n<h2>Setup Project Structure<\/h2>\n<p>Next, you need to set up the project structure. Since you are not going to be building a full-blow SPA in this article, the project structure is minimal.<\/p>\n<ul>\n<li>Add a new folder to the project called <em>App<\/em>. All the JavaScript application files are housed under this folder.<\/li>\n<li>In the App folder, add a new folder called <em>Build<\/em>. All the build and configuration files are housed under this folder.<\/li>\n<li>In the App folder, add a new folder called <em>Pages<\/em>. This stores the page components to demonstrate that routing is working.<\/li>\n<li>Add a new folder to the <em>wwwroot<\/em> folder called <em>js<\/em>. This stores the compiled JavaScript application files (i.e., the bundle)<\/li>\n<\/ul>\n<p>When it is all said and done, your project should look something like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"409\" height=\"337\" class=\"wp-image-81202\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-63.png\" \/><\/p>\n<h2>Create the package.json File<\/h2>\n<p>Now you need to define all of the JavaScript packages that the application is going to use and to do that you are going to create a <em>package.json<\/em> file. This is a configuration file for Node Package Manager (NPM) that contains project information, package dependency information, and NPM script definitions. To add the file:<\/p>\n<ul>\n<li>Right click on the project in Solution Explorer and <em>Add New Item <\/em>to display the <em>Add New Item <\/em>dialog<em>.<\/em><\/li>\n<li>Type <em>npm Configuration File<\/em> in the search textbox in the upper right-hand corner of the dialog<\/li>\n<li>This should display an <em>npm Configuration File<\/em> item with a default name of <em>package.json<\/em>.\n<ul>\n<li>If it does not, you can also type <em>JSON File<\/em> to add a JSON file (which you will need to name <em>package.json<\/em>)<\/li>\n<li>If a JSON file is not available for some reason, you can add a text file and name it <em>package.json<\/em><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>After creating the file, open it up, and delete any content in the file. Then paste the following content into the <em>package.json<\/em> file and save it:<\/p>\n<pre class=\"lang:c# theme:vs2012\">{\r\n  \"version\": \"1.0.0\",\r\n  \"name\": \"spademo\",\r\n  \"private\": true,\r\n  \"scripts\": {\r\n    \"dev\": \"cross-env NODE_ENV=development webpack --config App\/Build\/webpack.config.js\",\r\n    \"prod\": \"cross-env NODE_ENV=production webpack --config App\/Build\/webpack.config.js\",\r\n    \"watch\": \"cross-env NODE_ENV=watch webpack --config App\/Build\/webpack.config.js\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"babel-core\": \"6.26.3\",\r\n    \"babel-loader\": \"7.1.5\",\r\n    \"babel-preset-es2015\": \"6.24.1\",\r\n    \"css-loader\": \"1.0.0\",\r\n    \"cross-env\": \"5.2.0\",\r\n    \"eslint\": \"5.5.0\",\r\n    \"eslint-friendly-formatter\": \"4.0.1\",\r\n    \"eslint-loader\": \"2.1.0\",\r\n    \"file-save\": \"0.2.0\",\r\n    \"html-webpack-plugin\": \"3.2.0\",\r\n    \"path\": \"0.12.7\",\r\n    \"vue-loader\": \"15.4.1\",\r\n    \"vue-style-loader\": \"4.1.2\",\r\n    \"vue-template-compiler\": \"2.5.17\",\r\n    \"webpack\": \"4.17.2\",\r\n    \"webpack-cli\": \"3.1.0\",\r\n  },\r\n  \"dependencies\": {\r\n    \"vue\": \"2.5.17\",\r\n    \"vue-router\": \"3.0.1\"\r\n  }\r\n}<\/pre>\n<p>\nI will break down the contents of this JSON file. The first three lines contain project information that is optional because you are not publicly publishing this package. They were included for the sake of completeness.<\/p>\n<ul>\n<li><strong>version<\/strong> \u2013 this is the version of the application.<\/li>\n<li><strong>name <\/strong>\u2013 the name of the application. This should be lowercase, not start with a dot or underscore, may not contain URL unsafe characters, and must be less than 214 characters long.<\/li>\n<li><strong>private <\/strong>\u2013 marks the package as private so it is not accidentally published.<\/li>\n<\/ul>\n<p>The next section contains NPM script definitions that allow you to quickly run scripts by name. The name of the script appears on the left (as the JSON property name) and the command to execute appears as a string on the right (as the JSON property value). The names are entirely arbitrary, so you can call a script whatever you want. Obviously, it makes more sense to name it something sensible. There are three scripts defined in this file:<\/p>\n<ul>\n<li><strong>dev<\/strong> \u2013runs webpack in development mode which packages up the application with additional debugging information. Without this debugging information, debugging the packaged application files is extremely difficult.<\/li>\n<li><strong>prod <\/strong>\u2013 runs webpack in production mode which packages up the application with production optimizations.<\/li>\n<li><strong>watch <\/strong>\u2013 runs webpack in development mode and continuously monitors the source files for changes. When a change occurs, the package is automatically re-bundled to incorporate the change.<\/li>\n<\/ul>\n<p>Notice that all three scripts are extremely similar. They all start by calling <code>cross-env<\/code> which is a package that allows setting up environment variables from the windows command line. Each script then sets the <code>NODE_ENV<\/code> environment variable to a different value, which is stored in the <code>NODE_ENV<\/code> environment variable. The scripts then call webpack (another package) and tell webpack to use the configuration file located at <em>App\/Build\/webpack.config.js<\/em> (which will be defined later on in this article). The configuration settings for webpack use the <code>NODE_ENV<\/code> to determine which configuration values to use (which is covered in more detail later as well).<\/p>\n<p>After the script section, you can see the <code>dependencies<\/code> and <code>devDependencies<\/code> sections where the packages the application depends are identified. There are two sections to differentiate between build-time dependencies (<code>devDependencies<\/code>) and run-time dependencies (<code>dependencies<\/code>). Package dependencies are defined the same way in both sections with the package name on the left (as the JSON property name) and the version of the package appearing on the right (as the JSON property value). All the packages in this file use explicit versions, but you can also prefix a version with <code>^<\/code> to automatically use the most recent major version (middle number), or <code>~<\/code> to use the most recent minor version release (third number).<\/p>\n<h2>Package Dependency Information<\/h2>\n<p>You can see all the dependencies in the <em>package.json<\/em> file, but what do they all do? Fortunately, finding out what a package does is extremely simple. Google tends to be the best option because if you type <em>npm [package]<\/em> into the search, it normally brings up the package page from the npm website with all the details on what the package does and how to use it. If Google fails you, you can also go directly to the <a href=\"http:\/\/www.npmjs.com\/\">NPM website<\/a> and search the packages there. But here is a breakdown of the packages in this application to keep you from having to look them all up:<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>Package<\/strong><\/p>\n<\/td>\n<td>\n<p><strong>Description<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>babel-core<\/p>\n<\/td>\n<td>\n<p>Babel is a transpiler used to convert ES2015 JavaScript syntax which has limited browser support into ES5 syntax which has more universal browser support.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>babel-loader<\/p>\n<\/td>\n<td>\n<p>Webpack loader used to run script found within the &lt;script&gt; section of a Vue single-file component through the Babel transpiler. This is basically a bridge between Webpack and Babel.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>babel-presets-es2015<\/p>\n<\/td>\n<td>\n<p>Babel configuration presets for es2015 (ES6) to ES5 JavaScript conversion<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>css-loader<\/p>\n<\/td>\n<td>\n<p>Webpack loader for bunding CSS files together. This is a peer dependency for the vue-loader packages and is required even if you are not bundling CSS.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>cross-env<\/p>\n<\/td>\n<td>\n<p>Allows environment variables to be set in the windows command line interface. This is used by the scripts defined in the package.json scripts section.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>eslint<\/p>\n<\/td>\n<td>\n<p>Provides linting support for JavaScript. Linting is the process of checking code for errors. Without this, Webpack just packs up all your code as-is without checking to make sure there are no mistakes.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>eslint-friendly-formatter<\/p>\n<\/td>\n<td>\n<p>Formats lint errors produced by eslint in a more user-friendly manner.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>eslint-loader<\/p>\n<\/td>\n<td>\n<p>Webpack loader used to run script found within the &lt;script&gt; section of a Vue single-file component through eslint to check for errors. This is basically a bridge between Webpack and eslint.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>file-save<\/p>\n<\/td>\n<td>\n<p>Provides a simple way to write text to a file. Used within the webpack configuration file to write application settings to an external file.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>path<\/p>\n<\/td>\n<td>\n<p>Provides common file-path routines and methods like combining and resolving paths.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>vue-loader<\/p>\n<\/td>\n<td>\n<p>Webpack loader for managing Vue single-file components. This is what is responsible for splitting the &lt;template&gt; &lt;script&gt; and &lt;style&gt; sections of a Vue file.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>vue-style-loader<\/p>\n<\/td>\n<td>\n<p>Webpack loader for extracting CSS from the &lt;style&gt; section of a Vue single-file component. This is a peer dependency for the vue-loader package and is required even if you are not working with CSS in Vue files.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>vue-template-compiler<\/p>\n<\/td>\n<td>\n<p>Allows vue templates to be pre-compiled into render functions to avoid runtime-compilation overhead. This is used by vue-loader.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>vue<\/p>\n<\/td>\n<td>\n<p>Vue JS library.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>vue-router<\/p>\n<\/td>\n<td>\n<p>Vue Router library used for single-page-application routing.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Pulling Down the Packages<\/h2>\n<p>Once the package dependencies have been defined, you need to pull them down into the development environment. You can do this manually by opening a PowerShell or command prompt, navigating to the directory containing the <em>package.json<\/em> file, and typing <em>npm install<\/em> on the command line. Or you can do it through the Visual Studio Task Runner Explorer. To use the Task Runner Explorer, do the following:<\/p>\n<ul>\n<li>If Task Runner Explorer is not visible then you will need to show it by clicking <em>View Other Windows Task Runner Explorer<\/em><\/li>\n<li>In Task Runner Explorer, expand the <em>package.json<\/em> node if it is not already expanded<\/li>\n<li>Expand the <em>Defaults<\/em> node if it not already expanded<\/li>\n<li>Double click the <em>install<\/em> node, and Task Runner Explorer will run <em>npm<\/em> install<\/li>\n<li>A new tab displays showing the output from <em>npm <\/em>install. It may take a short while to download and extract all the packages to your development environment.<\/li>\n<li>When it is finished, review the output to determine if it processed successfully. There should be no errors. Sometimes you may see a <em>Process terminated<\/em> with code 0.<\/li>\n<\/ul>\n<p>When you run <em>npm install<\/em>, NPM reads the <em>package.json<\/em> file and begins building a list of the packages on which your application depends. It then determines if any of those packages have dependencies, and then if any of those dependencies have their own dependencies, and so on it goes until <em>npm<\/em> has recursively built out a dependency tree based on your initial set of required packages. <em>Npm<\/em> then downloads all of those packages into the <em>node_modules <\/em>folder in the same directory where your <em>package.json<\/em> file resides. As such, the total number of packages that are actually pulled down can be far greater than the number defined in your <em>package.json<\/em> file. For example, the 15 packages defined here result in more than 500 packages after accounting for all the dependencies. This ends up being 50 MB worth of data (unpacked) and more than 13,000 individual files. Although this may seem overwhelming, these files are not included in your project, and you really don\u2019t need to interact with them for the most part.<\/p>\n<div class=\"note\">\n<p><em>NOTE: any time you add a new package to your <em>packages.json<\/em> file, you will need to run <em>npm<\/em> <em>install<\/em> (either manually or from the <em>Task Runner Explorer<\/em>) to pull the package and any dependencies down.<\/em><\/p>\n<\/div>\n<h2>Creating the Vue Application<\/h2>\n<p>Next, you need an application to compile. As mentioned before, this is not a full-blown tutorial about how to use Vue or implement an SPA, so my discussions about the files here will be brief. The application consists of four types of files<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1092\" height=\"652\" class=\"wp-image-81203\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-64.png\" \/><\/p>\n<ul>\n<li><strong>HTML File<\/strong> \u2013 this is the single page of the single page application. Static files that are not part of the build process can be placed in the <em>wwwroot<\/em> folder. You can also opt to keep them under the <em>App <\/em>folder and copy them over during the build process if you prefer.<\/li>\n<li><strong>JavaScript Application Files<\/strong> \u2013 these are JavaScript files that drive the application<\/li>\n<li><strong>Vue Files<\/strong> \u2013 single file Vue components representing the application and some dummy pages to prove out the routing.<\/li>\n<li><strong>JavaScript Build Files<\/strong> \u2013 these drive the build process that packages the JavaScript application and will be discussed in more detail later.<\/li>\n<\/ul>\n<p>In the following sections I will show you the content of these files and provide a bit of commentary briefly outlining what each one does, so go ahead and make placeholders for all these files in your project. To add a file, right click on the folder that is to contain the file, then select <em>Add New Item &#8230;<\/em> from the context menu. This displays the New Item dialog where you can select a file type. You probably will not have a Vue file type, so you can create a JavaScript file and simply rename the file with the <em>.vue<\/em> extension. Any default content created when the file is added should be removed and replaced by the content shown outlined in this article.<\/p>\n<h3>Index.html<\/h3>\n<p>As mentioned before, this is the single page of the single page application. Replace any content in the index.html file with the following content.<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n    &lt;meta charset=\"utf-8\" \/&gt;\r\n    &lt;title&gt;SPA Demo&lt;\/title&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n    &lt;div id=\"app\"&gt;\r\n    &lt;\/div&gt;\r\n    &lt;script src=\"\/js\/app-settings.js\"&gt;&lt;\/script&gt;\r\n    &lt;script type=\"text\/javascript\" src=\"\/js\/bundle.js\"&gt;&lt;\/script&gt;&lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>There are three key takeaways from this file. First, Vue needs a target element to bind to, and in this case, that\u2019s going to be the <code>&lt;div id=\"app\"&gt;<\/code>. Below that you\u2019ll then reference two files that are not yet in the project:<\/p>\n<ul>\n<li><strong>app-settings.js <\/strong>\u2013 this file contains application settings. You can think of it like the web.config of your compiled JavaScript application.<\/li>\n<li><strong>bundle.js <\/strong>\u2013 this file contains the compiled \/ packaged JavaScript application<\/li>\n<\/ul>\n<p>Both these files are created during the application packaging process, so they do not exist yet. When they are created, however, they will reside in the <em>wwwroot\/js<\/em> folder.<\/p>\n<h3>Main.js<\/h3>\n<p>This is the entry point into the JavaScript application, so this is the file that you\u2019re going to point webpack at when it\u2019s set up to create the application bundle.<\/p>\n<pre class=\"lang:c# theme:vs2012\">import Vue from 'vue';\r\nimport App from '.\/App.vue';\r\nimport Router from '.\/router';\r\nnew Vue({\r\n    el: '#app',\r\n    template: '&lt;App\/&gt;',\r\n    components: { App },\r\n    router: Router\r\n});<\/pre>\n<p>The first three lines import modules that are needed to set up the application. <code>Vue<\/code> is the <em>Vue.js<\/em> library which is one of the <code>devDependencies<\/code> the is set up in the <em>packages.json<\/em> file and pulled down into the development environment by running <em>npm install<\/em><strong>. <\/strong>Next is the <code>App<\/code> component which is defined in <em>App.vue<\/em>. And finally, <code>Router<\/code> imports routing information defined in the router.js file that lets Vue know what to display when navigating to different URLs.<\/p>\n<p>After the imports section, you can see that a new Vue instance is initialized and then tell it to bind to the <code>#app<\/code> element which is defined in the <em>index.html<\/em> page. The template and component properties of the <code>Vue<\/code> constructor object tell <code>Vue<\/code> that you want to fill the <code>#app<\/code> element in <em>index.html<\/em> with the <code>App<\/code> component defined in <em>App.vue<\/em>. Last, the <code>router<\/code> property allows passing the routing information defined in router.js to the Vue instance.<\/p>\n<h3>Router.js<\/h3>\n<p>This file exists to initialize the <code>Vue<\/code> router and define which components to display at what URLs.<\/p>\n<pre class=\"lang:c# theme:vs2012\">import Vue from 'vue';\r\nimport VueRouter from 'vue-router';\r\nimport PageA from '.\/Pages\/PageA.vue';\r\nimport PageB from '.\/Pages\/PageB.vue';\r\nconst routes = [\r\n    { path: '\/', component: PageA },\r\n    { path: '\/pagea', component: PageA },\r\n    { path: '\/pageb', component: PageB },\r\n]\r\nVue.use(VueRouter);\r\nconst router = new VueRouter({ mode: 'history', routes: routes });\r\nexport default router;<\/pre>\n<p>Once again, start off with imports. This time you import the <code>Vue<\/code> library, the <code>Vue Router<\/code> library that manages routing for <code>Vue <\/code>(and which is also a <code>devDependency<\/code> in <em>packages.json<\/em>), and two <code>Vue<\/code> components with the creative names <em>PageA <\/em>and <em>PageB<\/em> (which are imported from <em>PageA.vue<\/em> and <em>PageB.vue<\/em> respectively). Next is an array of routes which defines URLs and which component to display, followed by a call to <code>Vue.use(VueRouter)<\/code> which initializes routing within <code>Vue<\/code>. After that, you create a new <code>VueRouter<\/code> instance and pass in the routes defined as well as tell the router how to manage page state information (in this case I\u2019m using \u2018history\u2019 mode). And then return that initialized <code>VueRouter<\/code> instance as the default export of the module.<\/p>\n<h3>App.vue<\/h3>\n<p>This is the top-level component in the application. In the application, it is an extremely component:<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;template&gt;\r\n    &lt;div&gt;\r\n        Hello from App Vue: <strong>{{<\/strong> message <strong>}}<\/strong>\r\n        &lt;router-link to=\"\/pagea\"&gt;Go to A&lt;\/router-link&gt;\r\n        &lt;router-link to=\"\/pageb\"&gt;Go to B&lt;\/router-link&gt;\r\n        &lt;router-view&gt;&lt;\/router-view&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n&lt;script&gt;\r\n    export default {\r\n        data() {\r\n            return {\r\n                message: 'Message from App.vue',\r\n            };\r\n        }\r\n    };\r\n&lt;\/script&gt;<\/pre>\n<p>The component displays the message <em>Message from App.vue<\/em> when rendered on a page. It also has a <code>&lt;router-view&gt;<\/code> component where the component associated with a URL in the router will be displayed. To demo that functionality, there are two <code>&lt;router-link&gt;<\/code> components that allow users to switch back and forth from <em>PageA <\/em>to <em>PageB<\/em>. You can find out more details about routing in Vue from the <a href=\"https:\/\/router.vuejs.org\/\">Vue Router<\/a> page.<\/p>\n<h3>PageA.vue \/ PageB.vue<\/h3>\n<p>The page components are also extremely simple components that are really just around to demo that the routing works correctly.<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;template&gt;\r\n    &lt;div&gt;\r\n        Page A - <strong>{{<\/strong> message <strong>}}<\/strong>\r\n    &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n&lt;script&gt;\r\n    export default {\r\n        name: 'PageA',\r\n        data() {\r\n            return {\r\n                message: 'Data from Page A'\r\n            };\r\n        }        \r\n    };\r\n&lt;\/script&gt;<\/pre>\n<p>This content can be copied into both <em>PageA.vue<\/em> and <em>PageB.vue<\/em>, but you will obviously want to update any text in the <em>PageB.vue<\/em> to say <em>Page B<\/em>. Like the <em>App.vue<\/em>, this component just displays a message indicating where the text is defined.<\/p>\n<h2>What is Webpack?<\/h2>\n<p>Webpack is a module bundler with a pipeline for applying transformations to application code. To use it, you specify one or more entry points into your application \u2013 in this case, the only entry point is the <em>Main.js<\/em> file. Webpack then reads that file and locates any JavaScript modules that are referenced within it. Then it recursively reads those modules and locates any JavaScript modules on which they depend, and so on and so forth until it has a picture of all the code you used in the application. It then bundles all the code from all those modules into a bundle containing all the code (there are options for splitting up bundles into multiple files that I will not get into here). In this case, this looks something like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1457\" height=\"517\" class=\"wp-image-81204\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-65.png\" \/><\/p>\n<p>Most browsers support ES6 and the concept of JavaScript modules, so you don\u2019t necessarily need to use a bundler when building a modern JavaScript application. However, you will probably end up using a bundler for Vue applications for the following three reasons:<\/p>\n<ul>\n<li><strong>Single-file Vue component support<\/strong>. Developers tend to really appreciate how .vue files allow you to define HTML, script, and CSS for a given component. However, JavaScript cannot natively interpret a <em>.vue<\/em> file. Webpack contains a loader that reads a <em>.vue<\/em> file, breaks it down into its constituent parts, and then packages it as a JavaScript module that can be referenced like normal.<\/li>\n<li><strong>ES5 support<\/strong>. You will probably want to write your application using the latest JavaScript syntax. However, the current version of IE (at the time this was written) and many older versions of browsers do not have ES6 support. Webpack allows you to add a plugin to transpile your code from ES6 into an ES5 format (you will do this using Babel).<\/li>\n<li><strong>Optimization<\/strong>. Unoptimized bundles initialize about three times faster than the same non-bundled code. Optimized bundles initialize about four times faster (<a href=\"https:\/\/docs.google.com\/document\/d\/1ovo4PurT_1K4WFwN2MYmmgbLcr7v6DRQN67ESVA-wq0\/pub\">Source<\/a>). For smaller applications, the differences will be negligible, but for larger applications, you will see some benefit.<\/li>\n<\/ul>\n<h2>The Build Process<\/h2>\n<p>With the application files in place, you can finally focus on the build process, which is going to look like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"391\" height=\"586\" class=\"wp-image-81205\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-66.png\" \/><\/p>\n<ul>\n<li><strong>NPM Script<\/strong> \u2013 these are the scripts defined in the <em>package.json<\/em> file. They are used to set the NODE_ENV variable to indicate the type of build to run.<\/li>\n<li><strong>NODE_ENV <\/strong>\u2013 this is an environment variable that stores the value defined in the NPM script.<\/li>\n<li><strong>config.js<\/strong> \u2013 this is a JavaScript module that exports a JavaScript object containing configuration settings for webpack that vary depending on the <code>NODE_ENV <\/code>value.<\/li>\n<li><strong>Webpack.config.js<\/strong> \u2013 this is a JavaScript module that uses the configuration settings from the <code>config.js<\/code> module to set up and export a webpack configuration object.<\/li>\n<li><strong>Webpack <\/strong>\u2013 this is a command line utility that reads a webpack configuration and packages an application according to those settings.<\/li>\n<\/ul>\n<div class=\"note\">\n<p><em>NOTE:Node JS uses the Common JS module syntax, not the ES6 module syntax. This means that the module imports and exports syntax is a little bit differently here than in the Vue application.<\/em><\/p>\n<\/div>\n<h3>Config.js<\/h3>\n<p>As mentioned before, this file is a Common JS module containing configuration settings that can vary depending on the <code>NODE_ENV<\/code> setting. Although you could put these configuration settings directly in the webpack configuration file, splitting them out like this has some advantages. The webpack configuration tends to be lengthier and more complicated, so this keeps the document size more manageable. Second, it provides a separation that helps keep people from changing configuration settings in webpack that they should stay away from. Following is the content for the <em>config.js<\/em> file:<\/p>\n<pre class=\"lang:c# theme:vs2012\">const configuration = {\r\n    \/\/ Application settings that can be configured from one environment to the next.  These \r\n    \/\/ are output to the app-settings.js file that is then referenced as a plugin within \r\n    \/\/ webpack so they can be changed manually if needed.\r\n    appSettings: {\r\n        settingA: 5,\r\n        settingB: 10\r\n    },\r\n    \/\/ Identifies the type of build that has been requested.  Primarily used to vary \r\n    \/\/ settings for different types of builds.\r\n    buildTarget: process.env.NODE_ENV || 'development',\r\n    \/\/ Specifies whether webpack should watch for changes on the file system and \r\n    \/\/ automatically repack bundles when changes occur.\r\n    watch: false,\r\n    \/\/ Specifies the webpack mode\r\n    webpackMode: 'development'\r\n};\r\n\/*******************************************************************************\r\n * Define watch-only settings\r\n ******************************************************************************\/\r\nif (configuration.buildTarget === 'watch') {\r\n    configuration.watch = true;\r\n}\r\n\/*******************************************************************************\r\n * Define production-only settings\r\n ******************************************************************************\/\r\nif (configuration.buildTarget === 'production') {\r\n    \/\/ Define production-only settings\r\n    configuration.webpackMode = 'production';\r\n}\r\n\/\/Export the configuration\r\nmodule.exports = configuration;<\/pre>\n<p>This script starts off by creating an object and storing it in the <code>configuration<\/code> variable. This is the configuration object that will be ultimately exported from this module, and the default values are set up for a development build. The <code>appSettings<\/code> defined in this configuration are intended for environment-specific settings that will be rendered out to the app-settings.js file referenced from the index.html page. The<code> buildTarget<\/code> property stores a value indicating the requested build type. Notice that the value comes from <code>process.env.NODE_ENV<\/code> which will have the <code>NODE_ENV<\/code> value specified in the <em>npm<\/em> script. The <code>process<\/code> object is natively available and provides a variety of process-related metadata including access to environment variables (via the <code>env<\/code> property). Next, there is the <code>watch<\/code> property, which is set to false, and the <code>webpackMode<\/code> property which is set to <em>development<\/em>.<\/p>\n<p>After the configuration object definition, there are two if statements that check to see if the <code>buildTarget<\/code> value is watch or production. Within the if statements, the configuration object is altered to account for settings needed for that type of build. If the <code>buildTarget<\/code> is set to watch, then notice the watch property is flipped to true. If the <code>buildTarget<\/code> is production, then the <code>webpackMode<\/code> is set to production. There are only a few configuration settings here because I wanted to keep it simple. You will likely encounter scenarios in your webpack build that will need to change based on the build type, and when that happens just create a variable for them in the configuration object, update them accordingly in the appropriate if statement, and then use the configuration setting value in the webpack configuration file. Also, know that you can modify your <code>appSettings<\/code> object with environmental specific values as well.<\/p>\n<p>Exporting the module works a bit differently with Common JS modules than ES6 modules. In ES6, you use the export keyword. For Common JS, you have to assign the exported object to <code>module.exports<\/code> which is what you see being done on the last line of the script.<\/p>\n<h3>Webpack.config.js<\/h3>\n<p>Finally, it\u2019s time to add the webpack configuration file which drives the webpack process. Like the config.js file, this is a Common JS module. This file is passed into webpack from the <em>npm<\/em> scripts defined in <em>packages.json<\/em> file.<\/p>\n<pre class=\"lang:c# theme:vs2012\">const config = require('.\/config');                         \/\/ Configuration settings\r\nconst path = require('path');                               \/\/ Path library used for building file locations\r\nconst fileSave = require('file-save');                      \/\/ Utility for writing files to disk\r\nconst webpack = require('webpack');\t\t\t     \/\/ Webpack library\r\nconst VueLoaderPlugin = require('vue-loader\/lib\/plugin');   \/\/ Plugin used for loading single-component vue files\r\n\/\/ Output the application settings file\r\nif (config.appSettings != null) {\r\n    \/\/ NOTE: the replace regex on the next line removes the quotes from properties.  It is rudimentary and can be removed \r\n    \/\/ if it causes issues (because the quotes are technically OK I just think they look bad).\r\n    var appSettingsOutput = JSON.stringify(config.appSettings, null, 4).replace(\/\\\"([^(\\\")\"]+)\\\":\/g, \"$1:\");\r\n    fileSave(path.join(path.resolve(__dirname, \"..\/..\/wwwroot\/js\"), \"app-settings.js\"))\r\n        .write(\"window.appSettings = \" + appSettingsOutput);\r\n}\r\nconst webpathConfig = {\r\n    mode: config.webpackMode,   \/\/ Specifies whether to use built-in optimizations accordingly (options: production | development | none)\r\n    entry: \".\/App\/main.js\",     \/\/ Specifies the entry point of the application where webpack begins the packaging process.  \r\n    output: {\r\n        path: path.resolve(__dirname, \"..\/..\/wwwroot\/js\"),  \/\/ Specifies the output directory for any wepback output files\r\n        filename: \"bundle.js\",                              \/\/ Specifies the file name template for entry chunks (TODO: figure out what an entry point chunk is),\r\n        publicPath: \"\/js\/\"                                  \/\/ Specifies the page-relative URL to prefix before assets to ensure they can be resolved by a browser.  (Notice this value is injected into index.html to refer to the bundle.js file created by webpack).\r\n    },\r\n    resolve: {\r\n        alias: {\r\n            vue: 'vue\/dist\/vue.js'  \/\/ This is required to avoid the error 'You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.'\r\n        }\r\n    },\r\n    module: {\r\n        rules: [\r\n            { test: \/\\.vue$\/, loader: 'vue-loader' },                                   \/\/ Specifies that files with the .vue extension should be processed by vue-loader which is what breaks up a single-file vue component into its constituent parts.\r\n            { test: \/\\.js$\/, loader: 'babel-loader', query: { presets: ['es2015'] } },  \/\/ Specifies that .js files should be run through the babel-loader for ES2015 to ES5 conversion.\r\n            { test: \/\\.css$\/, use: ['vue-style-loader', 'css-loader'] },                \/\/ Specifies that CSS should be included in the bundle from .CSS files as well as processed from the &lt;style&gt; section of vue single-file vue component.\t\r\n            \/\/ ESLint rules\r\n            {\r\n                test: \/\\.(js|vue)$\/,\r\n                exclude: \/node_modules\/,\r\n                loader: 'eslint-loader',\r\n                enforce: 'pre',\r\n                include: [path.resolve(__dirname, \"App\")],\r\n                options: {\r\n                    eslintPath: path.join(__dirname, '..\/node_modules\/eslint'),\r\n                    fix: true,\r\n                    formatter: require('eslint-friendly-formatter'),\r\n                    emitError: false,\r\n                    emitWarning: false,\r\n                    failOnError: true,\r\n                    failOnWarning: false\r\n                }\r\n            }\r\n        ]\r\n    },\r\n    plugins: [\r\n        \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\r\n        \/\/ Required per manual configuration section of the Vue Loader configuration instructions \r\n        \/\/ located at https:\/\/vue-loader.vuejs.org\/guide\/#vue-cli\r\n        \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\r\n        new VueLoaderPlugin(),  \r\n        \r\n        new webpack.DefinePlugin({\r\n            'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development') },\r\n            'appSettings': \"window.appSettings\"\r\n        }),\r\n    ],\r\n    watch: config.watch,                \/\/ Flag indicating whether webpack should monitor files and update bundles automatically with any changes\r\n    watchOptions: {                     \/\/ Specifies watch options for the watching mechanism.\r\n        ignored: ['node_modules']       \/\/ Specifies directories to ignore (optimization).\r\n    }\r\n};\r\nmodule.exports = webpathConfig;<\/pre>\n<p>That\u2019s probably a lot to take in, but I will break it down into more manageable sections.<\/p>\n<h4>Imports<\/h4>\n<p><code>Imports<\/code> appear on the first few lines of the file. You\u2019ll notice that importing a Common JS module uses the <code>require<\/code> method with a parameter identifying the desired module. The result of the <code>require<\/code> method is then stored off in a variable which can be used in the script. The <code>config <\/code>variable stores the config.js module with the configuration settings. Any of the properties defined on the configuration object from the config.js can be referenced within this script from the <code>config <\/code>variable. The <code>path<\/code> variable stores a reference to the path module which gives access to common path functions. The <code>fileSave <\/code>variable stores the method returned from the file-save module. Everything up to this point has been a normal object, but you can return methods from modules as well. You will use this method in a minute to write text to a file. The <code>webpack<\/code> variable stores a reference to the webpack module. And last, the <code>VueLoaderPlugin<\/code> stores the <code>vue-loader<\/code> plugin which allows webpack to process <em>.vue<\/em> files.<\/p>\n<h4>Managing Configuration Settings<\/h4>\n<p>The next section is designed to help manage configuration settings for a compiled application. If you define configuration settings within your application, then they end up packaged up inside the <em>bundle.js<\/em> file. In production builds, the bundle file is often optimized, minimized, and maybe even uglyfied. This makes locating and modifying configuration settings in a compiled application extremely difficult. Although you can update the settings and recompile the application, I\u2019ve found most QA managers prefer to build an application once and then push that build from DEV to QA to Production because it reduces the possibility of something changing in the build and introducing bugs. To avoid this, you must create a file containing application settings that can be easily updated, then make that available for use in the compiled application.<\/p>\n<p>If you remember back to the <code>configuration <\/code>object defined in the config.js file, it has an <code>appSettings<\/code> properties intended to store environment specific configuration settings. All you need to do is write those settings out to a file (in this case app-settings.js) with a bit of modification. This is how the object is defined in the <em>config.js<\/em>.<\/p>\n<pre class=\"lang:c# theme:vs2012\">appSettings: {\r\n    settingA: 5,\r\n    settingB: 10\r\n}<\/pre>\n<p>And this is what you should write out to the app-setting.js file:<\/p>\n<pre class=\"lang:c# theme:vs2012\">window.appSettings = {\r\n    settingA: 5,\r\n    settingB: 10\r\n};<\/pre>\n<p>\nWebpack allows you to define your own plugins that reference existing variables. You can essentially tell webpack that, when it comes across something called <code>appSettings<\/code> (or whatever you decided to call it), that it\u2019s supposed to reference <code>window.appSettings<\/code>. This allows you to define configuration settings externally but still refer to them from within the packaged code (you\u2019ll see this configured later). The only caveat is that app-settings.js has to be loaded before the bundle.js so that <code>window.appSettings<\/code> is defined when the bundled code runs. Here is the section of code from the example above that writes out the file:<\/p>\n<pre class=\"lang:c# theme:vs2012\">\/\/ Output the application settings file\r\nif (config.appSettings != null) {\r\n    \/\/ NOTE: the replace regex on the next line removes the quotes from properties.  It is rudimentary and can be removed \r\n    \/\/ if it causes issues (because the quotes are technically OK I just think they look bad).\r\n    var appSettingsOutput = JSON.stringify(config.appSettings, null, 4).replace(\/\\\"([^(\\\")\"]+)\\\":\/g, \"$1:\");\r\n    fileSave(path.join(path.resolve(__dirname, \"..\/..\/wwwroot\/js\"), \"app-settings.js\"))\r\n        .write(\"window.appSettings = \" + appSettingsOutput + \";\");\r\n}<\/pre>\n<p>\nThe first thing this script does is check if <code>appSettings<\/code> is defined. If not, it skips the file writing process. If there are settings to write, it calls <code>JSON<\/code><strong>.<\/strong><code>stringify<\/code> on <code>config<\/code><strong>.<\/strong><code>AppSettings<\/code> to get the textual representation of the <code>appSettings<\/code> object. Unfortunately, <code>JSON<\/code><strong>.<\/strong><code>stringify<\/code> puts quotes around the property names of objects. There is nothing technically wrong with this, but it\u2019s not quite as easy to read. So this method calls <strong>.<\/strong><code>replace<\/code> on the string and uses a regular expression to remove the quotes from the property names. As mentioned before, <code>fileSave<\/code> is actually a method, and you call this method and pass in the name of the file to which you want to write. That file name is constructed by calling <code>path<\/code><strong>.<\/strong><code>resolve<\/code> to get the full path to the <em>wwwroot\/js<\/em> folder in the web application, then calling <code>path<\/code><strong>.<\/strong><code>join<\/code> to join that path and the app-setting.js filename. It then calls <strong>write<\/strong> on the file-save instance returned by the <code>fileSave<\/code> method, which writes out the content of the app-setting.js file. Notice that you are adding the <code>window.appSettings<\/code><strong> =<\/strong> in this string.<\/p>\n<h4>Webpack Configuration Object<\/h4>\n<p>Webpack configurations can get extremely complicated if they need to, and all the documentation to make them complicated can be found on the <a href=\"https:\/\/webpack.js.org\/configuration\/\">webpack website<\/a>. My objective was to make this as simple as possible while still getting all the major components of Vue development in place. I think it\u2019s better to start simple and get more complex, as compared to starting off too complex. As such, I fully expect you will need to add a few bits of configuration here and there to accommodate the specifics of your own application. So, let\u2019s get started looking at this minimal configuration.<\/p>\n<p>The first thing you\u2019ll see is the <a href=\"https:\/\/webpack.js.org\/concepts\/mode\/\"><code>mode<\/code><\/a> property. This can be set to <em>development<\/em>, <em>production<\/em>, or <em>none<\/em>. When no value is set (e.g., null), the value defaults to <em>production<\/em>. This setting automatically loads certain webpack plugins that optimize the webpack build for development or production builds. Notice that you set the mode property value using <code>config<\/code><strong>.<\/strong><code>webPackMode<\/code> because it\u2019s a setting controlled from the <em>config.js<\/em> file.<\/p>\n<p>Next, there is the <a href=\"https:\/\/webpack.js.org\/configuration\/entry-context\/#entry\"><code>entry<\/code><\/a> property. This defines the webpack entry point for the application. There is a single entry point, <em>Main.js<\/em>, so was in the string <em>.\/App\/main.js<\/em>.<\/p>\n<p>The <a href=\"https:\/\/webpack.js.org\/configuration\/output\/\"><code>output<\/code><\/a> property is an object containing settings for the webpack output. Within this object, there are three properties that are set.<\/p>\n<ul>\n<li><a href=\"https:\/\/webpack.js.org\/configuration\/output\/#output-path\"><code><strong>path<\/strong><\/code><\/a> \u2013 this contains the path where bundled files should be written. In this case, write them to <em>wwwroot\/js<\/em> in the project. This value MUST be an absolute path, so you will use <code>path.resolve<\/code> to get an absolute path from the relative one.<\/li>\n<li><a href=\"https:\/\/webpack.js.org\/configuration\/output\/#output-filename\"><code><strong>filename<\/strong><\/code><\/a> \u2013 this is the filename used to save the bundle. Only one file is produced, so just name it <em>bundle.js<\/em>. If, however, you have multiple entry points you can also specify some tokens in the string (name, id, hash, chunkhash, contenthash, etc.) to keep the names unique.<\/li>\n<li><a href=\"https:\/\/webpack.js.org\/configuration\/output\/#output-publicpath\"><code><strong>publicPath<\/strong><\/code><\/a> \u2013 this is the public URL of the output directory. This is important for referencing resources static resources from the server (like images). Since the <em>wwwroot <\/em>folder in the project represents the root of the application, and you\u2019re putting the files in the <em>js<\/em> folder beneath the root, use the value <em>is<\/em> for the public path.<\/li>\n<\/ul>\n<p>After the output property, you\u2019ll see the <a href=\"https:\/\/webpack.js.org\/configuration\/resolve\/\"><code>resolve<\/code><\/a> property, which controls how modules are resolved. Webpack does a good job defining defaults for the most part, but you\u2019ll run into situations where you need to modify them. One such situation is that Vue ships with two packages. One contains the template compiler, and one does not. By default, webpack references the one that does not have the template compiler, which results in the error <strong>You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions or use the compiler-included build.<\/strong> To get around that, you need to reference a different version of Vue. This is accomplished through the <a href=\"https:\/\/webpack.js.org\/configuration\/resolve\/#resolve-alias\"><code>alias<\/code><\/a> property on the <code>resolve<\/code> object, which allows providing a name (i.e., alias) that points to a particular resource. In the case, the alias <code>vue<\/code> refers to the compiler-included build of Vue which is located at <em>vue\/dist\/vue.js<\/em> in the <em>node_modules<\/em> folder. You can also use the alias property to setup named references to folders in your application. This can be helpful if you have a complicated directory structure and helps you avoid having to reference files using long relative paths that have to backtrack through your application directory structure (e.g. <em>..\/..\/..\/App\/Components\/<\/em>).<\/p>\n<p>Under the resolve property is the <a href=\"https:\/\/webpack.js.org\/configuration\/module\/\"><code>module<\/code><\/a> property, which controls how modules in webpack behave. What is a module in webpack? Any file that webpack processes can be considered a module. A JavaScript file is a module. A <em>.vue<\/em> file is a module. Even images and CSS files can be modules. Basically, Webpack starts processing code starting from the entry point you give it. As it encounters module references in your code (e.g., import or require statements in JavaScript, URLs in CSS, image references in HTML pages, etc.) it determines if it can package that module and does so. Webpack does not, however, natively know about every file type \u2013 it requires developers to create extensions called loaders to help Webpack know how to package up different types of files. For example, Webpack doesn\u2019t natively support <em>.vue<\/em> files, but <code>vue-loader<\/code> plugs into Webpack and enables Webpack to process a <em>.vue<\/em> file correctly.<\/p>\n<p>Loaders are configured from the <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-rules\"><code>rules<\/code><\/a> property in the module object. In general, a rule identifies the <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-loader\"><code>loader<\/code><\/a> to use when processing a module and defines a <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#condition\"><code>test<\/code><\/a><code> <\/code>to determine whether a given file meets the conditions required to be processed by that loader. the configuration contains four rules, three of which are extremely simple:<\/p>\n<pre class=\"lang:c# theme:vs2012\">{ test: \/\\.vue$\/, loader: 'vue-loader' } <\/pre>\n<p>The first rule tests the filename of the module, and if it ends with <em>.vue<\/em>, it uses the <code>vue-loader<\/code> to process the module. As mentioned before, the <code>vue-loader<\/code> is what allows webpack to process <em>.vue<\/em> files. The string <em>vue-loader<\/em> resolves back to the <code>vue-loader<\/code> package pulled down from the <em>package.json<\/em> file.<\/p>\n<pre class=\"lang:c# theme:vs2012\">{ test: \/\\.js$\/, loader: 'babel-loader', options: { presets: ['es2015'] } } <\/pre>\n<p>The second rule tests the filename of the module and if it ends with .js uses the babel-loader to process the file. This loader is responsible for transpiling ES6\/ES2015 code into the ES5 syntax that most browsers support. You will notice there is an <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-options-rule-query\"><code>options<\/code><\/a> property on this rule. The <code>option<\/code> property allows you to pass loader-specific options to the loader. In this case, the babel-loader should use the es2015 rule presets to transpile code. The string <em>babel-loader<\/em> resolves back to the <code>babel-loader<\/code> package pulled down in the <em>package.json<\/em> file, and the es2015 preset is available because of the &#8216;babel-preset-es2015&#8217; package from the <em>packages.json<\/em> file.<\/p>\n<pre class=\"lang:c# theme:vs2012\">{ test: \/\\.css$\/, use: ['vue-style-loader', 'css-loader'] }<\/pre>\n<p>The third rule tests the filename of the module and if it ends with <em>.css<\/em> it uses both the &#8216;vue-style-loader&#8217; and the &#8216;css-loader&#8217; loaders to process the file. Although not used in this project, these provide the ability to package CSS files and styles defined within single-file Vue component. The <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-use\"><code>use<\/code><\/a> property accepts an array of loaders and can be used to process a single module with multiple loaders.<\/p>\n<pre class=\"lang:c# theme:vs2012\">{\r\n  test: \/\\.(js|vue)$\/,\r\n  loader: 'eslint-loader',\r\n  enforce: 'pre',\r\n  include: [path.resolve(__dirname, \"App\")],\r\n  options: {\r\n    eslintPath: path.join(__dirname, '..\/node_modules\/eslint'),\r\n    formatter: require('eslint-friendly-formatter'),\r\n    emitWarning: true,\r\n    failOnError: true\r\n  }\r\n}<\/pre>\n<p>The fourth and final rule tests the filename of the module, and if it ends in either <em>.js<\/em> or <em>.vue<\/em>, then it uses the <code>eslint-loader<\/code> to process the file. This is the loader that checks JavaScript code for syntax errors, so you know about them up front instead of at runtime. There are some extra properties on this rule that should be discussed, the first of which is the <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-enforce\"><code>enforce<\/code><\/a> property which manages when the loader execution order. The value <em>pre<\/em> ensures that <code>ESLint <\/code>runs on the code before any other loaders process it. Next is the <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-include\"><code>include<\/code><\/a> property, which allows you to specify an array of paths indicating which files you want to be processed by the loader. Webpack expects a full path, so build a full path from the relative path using <code>path.join<\/code>. Alternatively, you can use the <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-exclude\"><code>exclude<\/code><\/a> property if you want to exclude certain files. The <a href=\"https:\/\/webpack.js.org\/configuration\/module\/#rule-options-rule-query\"><code>options<\/code><\/a> property contains <a href=\"https:\/\/www.npmjs.com\/package\/eslint-loader\">eslint-loader configuration settings<\/a>. The <code>eslintPath<\/code> identifies the location of the <code>eslint<\/code> module which this loader uses to perform the linting. The <code>formatter<\/code> property allows you to specify a formatter for outputting the errors identified by <code>eslint<\/code>. In this configuration, you use the &#8216;eslint-friendly-formatter&#8217; which is available from the <em>eslint-friendly-formatter<\/em> package specified in the <em>packages.json<\/em> file. Next is the <code>emitWarning<\/code> setting, which you\u2019ll want to enable. This ensures that warnings are output when running the watch build, so at least some message appears if you write bad code to a file that webpack is watching. Finally, you\u2019ll want to enable the <code>failOnError<\/code> property to ensure your build stops if an error is encountered.<\/p>\n<p>Next up on the webpack configuration object is the <a href=\"https:\/\/webpack.js.org\/configuration\/plugins\/\"><code>plugins<\/code><\/a> property which allows customizing the webpack build process. the webpack has two plugins, the first of which is:<\/p>\n<pre class=\"lang:c# theme:vs2012\">new VueLoaderPlugin()<\/pre>\n<p>This plugin is required for vue-loader to function appropriately. Behind the scenes, it gathers rules defined for other modules (like JavaScript or CSS modules) and ensures they are applied to the modules built from the <em>.Vue<\/em> files. If you fail to include it, you\u2019ll see the error <em>vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.<\/em><\/p>\n<p>The next plugin is:<\/p>\n<pre class=\"lang:c# theme:vs2012\">new webpack.DefinePlugin({\r\n  'appSettings': \"window.appSettings\"\r\n})<\/pre>\n<p>Webpack allows you to define global constants using the <a href=\"https:\/\/webpack.js.org\/plugins\/define-plugin\/\"><code>DefinePlugin<\/code><\/a> method, which is used here to define the constant <code>appSettings<\/code><strong>. <\/strong>You can set these constants to any valid JavaScript expression, and in this case, it\u2019s set it to <code>window.appSettings<\/code>. This means that whenever you refer to <code>appSettings <\/code>in the application, it ends up actually referring to the data stored in <code>window.appSettings<\/code>. You may recall that <code>window.appSettings<\/code> are defined in the <em>app-settings.js<\/em> file. It contains configuration settings that must to be able to be modified without recompiling the application. This is the mechanism that bridges the gap between that external file and the compiled codebase.<\/p>\n<p>Finally, you have the <a href=\"https:\/\/webpack.js.org\/configuration\/watch\/#watch\"><code>watch<\/code><\/a> and <a href=\"https:\/\/webpack.js.org\/configuration\/watch\/#watchoptions\"><code>watchOptions<\/code><\/a> properties. The watch property is a <code>Boolean<\/code> flag that specifies whether you want webpack to monitor the source files for the build and automatically repackage if any of the source files change. Here, set the value to <code>config.watch<\/code>, which is managed by the configuration code in the <em>config.js<\/em> file. The <code>watchOptions<\/code> property contains settings that allow you to control various aspects of how source filed are watched. You can set up how often to poll for changes, and how long to wait to compile once changes are noticed. In this case, you\u2019ll want to use the <a href=\"https:\/\/webpack.js.org\/configuration\/watch\/#watchoptions-ignored\"><code>ignored<\/code><\/a> property to avoid watching the <em>node_modules<\/em> folder because it shouldn\u2019t change very often and it can be quite large.<\/p>\n<h2>Configuring .NET Core for a Single Page Application<\/h2>\n<p>There are two final tasks to complete before compiling and running the application. The first task is to return index.html regardless of the URL requested to ensure that the user always lands on the single page app. When the app loads, Vue Router reads the URL from the browser and display the appropriate component based on the route configuration defined in the <em>routes.js<\/em> file. The second task is to enable the application to serve static files, which are needed in order to serve up <em>index.html<\/em>, <em>bundle.js<\/em>, and any other static resources you need for the application. Conveniently, both of these tasks can be accomplished in the same method:<\/p>\n<ul>\n<li>Open <em>Startup.cs<\/em> in the root of the project<\/li>\n<li>Add the following line to the top of the file: <code>using System.IO;<\/code><\/li>\n<li>Locate the <code>Configure<\/code> method<\/li>\n<li><a id=\"post-81179-_gjdgxs\"><\/a> If you enabled HTTPs support for the application, then look for the following line of code: <code>app.UseHttpsRedirection();<\/code><\/li>\n<li>If you did not, then locate this line of code instead: <code>app.UseMvc();<\/code><\/li>\n<\/ul>\n<p>Before either of those lines of code, add the following code:<\/p>\n<pre class=\"lang:c# theme:vs2012\">       app.Use(async (context, next) =&gt;\r\n       {\r\n         await next();\r\n         var path = context.Request.Path.Value;\r\n         if (context.Response.StatusCode == 404 &amp;&amp; !Path.HasExtension(path) &amp;&amp; !path.StartsWith(\"\/api\"))\r\n         {\r\n           context.Request.Path = \"\/index.html\";\r\n           await next();\r\n         }\r\n       });\r\n       app.UseStaticFiles();<\/pre>\n<p>\nThe call to <code>app.Use<\/code> adds a delegate to the .NET Core middleware pipeline. The delegate has two parameters. The first is a reference to the <code>HttpContext<\/code> for the request, which is not used here. The second is a method delegate named <code>next<\/code> that, when called, starts execution of the next delegate in the middleware pipeline. In the middleware delegate, the first thing to do is call <code>await next();<\/code> which allows the request to process like normal. When it is finished, control returns to the method, and you can check the status of the request. If the request results in a <em>404 not found <\/em>error, and the path has no extension, and the path does not start with <em>\/api<\/em>, then you assume this is a request for a URL managed by the single page application. So rewrite the path of the request to point to <em>\/index.html<\/em>, and then process the request again by calling <code>await next()<\/code> again.<\/p>\n<p>The call to <code>app.UseStaticFiles()<\/code> is a bit more concise, and as it says, it configures the application to serve static files.<\/p>\n<p>With that in place, you\u2019re ready to run the application.<\/p>\n<h2>Setting the Startup URL for the Application<\/h2>\n<p>You may need to set the startup URL for your application so that it points to the root of the project. To configure the startup URL:<\/p>\n<ul>\n<li>Right click on your project<\/li>\n<li>Select <em>Properties<\/em> from the context menu<\/li>\n<li>Click on <em>Debug <\/em>from the pane on the left of the project properties page<\/li>\n<li>Look for the label <em>Launch browser<\/em>: and confirm that the URL is empty<\/li>\n<\/ul>\n<p>When you hit F5, IIS Express should launch the application at the project root.<\/p>\n<h2>Compiling and Running the Application<\/h2>\n<p>Visual Studio has no idea that your JavaScript application needs to be compiled. As such, if you hit F5, then Visual Studio is just going to serve up whatever files are there. Since we\u2019ve never run the application, that means that there is no <em>app-settings.js<\/em> and no <em>bundle.js<\/em>, so nothing is going to show up. If you have compiled the application before but have updated it, then IIS Express is going to be serving up an old version of your application. You have a couple of options to overcome this:<\/p>\n<p><strong>Manually Build your Application<\/strong><\/p>\n<p>You can manually build your application from the command line or using <em>Task Runner Explorer<\/em>. From the command line, you just have to navigate to the directory containing your <em>package.json<\/em> file and run the command <code>npm run dev<\/code> (or prod or watch) to run the command associated with the NPM script defined in the <em>package.json<\/em> file. In <em>Task Runner Explorer<\/em>, you just double click on the script you want to execute:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"837\" height=\"272\" class=\"wp-image-81206\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-67.png\" \/><\/p>\n<p>This launches a tab with the output from the script execution which you can review to determine if there are any errors. If you run the dev or prod scripts, the script will eventually terminate and let you know the process exit code. If you run the watch script, it will not exit because it remains running to watch the source files for changes. To stop watching, just close the tab.<\/p>\n<div class=\"note\">\n<p><em>NOTE: if you cannot see the <em>Task Runner Explorer<\/em>, you can display it by clicking <em>View &#8211; Other Windows &#8211; Task Runner Explorer.<\/em><\/em><\/p>\n<\/div>\n<p><strong>Use Build Events to Automatically Compile your Application<\/strong><\/p>\n<p>If you don\u2019t want to manually compile the application, you can setup build events to compile your application. To do this:<\/p>\n<ul>\n<li>Right click on your project and select <em>Properties<\/em> from the context menu. This displays the project properties window.<\/li>\n<li>Click on the <em>Build Events<\/em> tab on the left-hand side of the window<\/li>\n<li>In the Pre-build event command line, enter the following text: <code>npm run dev<\/code>\n<ul>\n<li>You can specify any NPM script you want here in place of <code>dev<\/code><\/li>\n<li>Do NOT use the <code>watch<\/code> script here (or any script that does not terminate without user intervention). The watch script runs indefinitely while waiting for files to change and will hang the build process.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\nEach time you build your application, it runs that script and compiles your application. The only drawback to this is that your application will get compiled every time you compile your project, even if you haven\u2019t made any changes to the source files.<\/p>\n<p><strong>Run Watch Script on Project Startup<\/strong><\/p>\n<p>You may have already figured this out, but you can run the watch script manually and just leave it running. This ensures that your application package is updated any time you change a source file and avoids the overhead of recompiling the JavaScript application when you\u2019ve only made changes to the .NET project. If this is the way you want to work, then to make things easier you can set up a binding that automatically launches the watch script when the project opens. To do this:<\/p>\n<ul>\n<li>In Task Runner Explorer, right click on the watch script<\/li>\n<li>From the context menu select <em>Bindings Project Open<\/em><\/li>\n<\/ul>\n<p>You\u2019ll notice in the <strong>Bindings<\/strong> tab in the Task Runner Explorer window there is new entry indicating that the watch script from the <em>package.json<\/em> file is bound to the <code>Project Open<\/code> event. The next time you open the project, the watch script will run automatically.<\/p>\n<p>After you compile the application, you can hit F5 to launch it. And I\u2019m being liberal with the term \u2018application\u2019 because it\u2019s a single page that lets you flip between two routes, but it demonstrates that the fundamentals for application development are in place.<\/p>\n<h2>Conclusion<\/h2>\n<p>Getting started with Vue can be difficult because of the number of pieces that have to come together before you can even get started. In this article you have learned about package management with Node JS and NPM. You\u2019ve setup a build process with NPM scripts. You can code using the latest JavaScript syntax, but your application will be transpiled for compatibility with older or less capable browsers. Your JavaScript code will be linted for syntax errors at build time so you can avoid hitting them at runtime. You have a configuration mechanism in place that allows you to move your code from environment to environment without needing to recompile to account for setting changes. And you\u2019ve got a single page Vue app with routing ready to go. There are several considerations that will need to be accounted for in your application, but at least you have a solid foundation on which to build.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Vue.js is a versatile JavaScript Framework that has many benefits for the developer building .NET web applications. It is, however, not easy to get started with this framework. In this article, Damon Armstrong provides an introduction to Vue.js.&hellip;<\/p>\n","protected":false},"author":46738,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[146044],"tags":[],"coauthors":[7575],"class_list":["post-81179","post","type-post","status-publish","format-standard","hentry","category-javascript"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81179","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/46738"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=81179"}],"version-history":[{"count":13,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81179\/revisions"}],"predecessor-version":[{"id":88796,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81179\/revisions\/88796"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=81179"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=81179"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=81179"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=81179"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}