Create a modular app with Webpack and VueJs

Create a modular app with Webpack and VueJs following this post. VueJs is a very popular frontend framework nowadays because of its simplicity, excellent documentation and vibrant community. It is great to write web applications that would make requests to a backend such as a REST API. But sometimes we'd like to integrate features into an existing website. And we want to do it gradually, say, the search form first, then some widget in the home page, and so on, so forth. In this case, we need to use an additional tool, Webpack. Creating a modular Webpack application is simple, it helps us to break down our code into modules and build bundles of code for each module. In this way, we can include our modules separately in our web site where we need them.

To demonstrate this, I'll guide you step by step by writing a demo application.

Requirements

  • VueJs basic knowledge
  • Terminal basic knowledge

Install Vue CLI

Firstly we need to globally install the Vue CLI package which will guide us through our project creation and configuration:

$ yarn global add @vue/cli

For more information about Vue CLI refer to its documentation: https://cli.vuejs.org/guide/installation.html

Create the Vue project

We are going to use Vue CLI, consequently, it'll be easier to create our project:

$ vue create demo-app

The wizard will run and will ask to choose our options. For this demo, I chose a very simple installation.

When you're asked to pick a preset, select "Manually select features":

Manually select features - create a modular app with Webpack

Select Babel, Typescript, CSS Preprocessors and Linter/Formatter.

Then select the following options:

Pick the simplest options - create a modular app with Webpack

Use class-style component syntax? Yes
Use Babel alongside TypeScript for auto-detected polyfills? Yes
CSS pre-processor: Sass/SCSS (with node-sass)
Linter / formatter config: Airbnb
Additional lint features: Lint on save
Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
Save this as a preset for future projects? No

Subsequently, the project will be created with the options we chose.

Run and test our brand new VueJs project

Go to the project directory:

$ cd demo-app
$ ~/demo-app> yarn serve

If everything went well, now can access our demo with the url http://localhost:8080/. Success!

Run and test our brand new VueJs project. Check out the demo page. create a modular app with Webpack.

Add Webpack to the project

First we stop the local server with Ctrl + C and then we use Yarn to add the dependencies:

$ ~/demo-app> yarn add webpack webpack-cli

Create two modules

In order to do that, we need to modify a little bit our directory structure. By default, when the project is created, we get some boilerplate code which looks like this:

First thing, we clean the `src` directory by removing the following:

$ ~/demo-app> rm ./src/components/ -r
$ ~/demo-app> rm /src/App.vue
$ ~/demo-app> rm /src/main.ts

Next, we create a new directory inside /src called modules:

$ ~/demo-app> mkdir ./src/modules

That is the place where our modules code will reside. This is the code for our demo modules:

Module 1: Greeting

import Vue from 'vue';
import Greeting from './Greeting.vue';

Vue.config.productionTip = false;

new Vue({
  render: h => h(Greeting),
}).$mount('#greeting');
<template>
  <div id="greeting-module">
    <b>This is my greeting:</b>
    <hello-world></hello-world>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

import HelloWorld from '@/modules/greeting/components/HelloWorld.vue';

@Component({
  components: {
    HelloWorld,
  },
})
export default class Greeting extends Vue {}
</script>

<style scoped>
</style>
<template>
  <div class="hello-world">
    <h1>Hello</h1>
    <p>
      world!
    </p>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
}
</script>

<style scoped lang="scss">
</style>

Module 2: SearchForm

import Vue from 'vue';
import SearchForm from './SearchForm.vue';

Vue.config.productionTip = false;

new Vue({
  render: h => h(SearchForm),
}).$mount('#searchForm');
<template>
  <div id="search-form-module">
    <search-input></search-input>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

import SearchInput from '@/modules/search-form/components/SearchInput.vue';

@Component({
  components: {
    SearchInput,
  },
})
export default class Greeting extends Vue {}
</script>

<style scoped>
</style>
<template>
  <div class="search-input">
    <b>Search:</b>
    <p>
      <input type="search" name="criteria" />
      <button type="submit">search</button>
    </p>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class SearchInput extends Vue {}
</script>

 

Modular app with Webpack is almost ready. Just add the configuration.

We need to create a file called vue.config.js:

$ ~/demo-app> touch vue.config.js

Next, we add two pages to our configuration, one per module. This will give us the ability to run and test our project:

module.exports = {
  filenameHashing: false,
  pages: {
    greeting: {
      entry: `${__dirname}/src/modules/greeting/main.ts`,
      template: `${__dirname}/public/greeting.html`,
      filename: 'greeting.html',
      title: 'Demo - Greeting',
    },
    'search-form': {
      entry: `${__dirname}/src/modules/search-form/main.ts`,
      template: `${__dirname}/public/search-form.html`,
      filename: 'search-form.html',
      title: 'Demo - Search Form',
    },
  },
};

As you can see in the code block, we disable the file name hashing for our demo (line 2). And we refer to a template file for each of our pages (lines 6 and 12).

Now, let’s create our page templates so we don't run into missing file errors. You can copy the template from the existing file ./public/index.html:

$ ~/demo-app> cp ./public/index.html ./public/greeting.html
$ ~/demo-app> cp ./public/index.html ./public/search-form.html

Then, we need to change the IDs of the target html element. The ID should match the ID defined in the main.ts file of every module:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>demo-app</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but demo-app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="greeting"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>demo-app</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but demo-app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="searchForm"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

After doing that, we don't need the file ./public/index.html anymore, so we go ahead and delete it:

$ ~/demo-app> rm ./public/index.html

At this point, we are ready to test our demo app. We can run our dev server once again:

$ ~/demo-app> yarn serve

If we go to the address http://localhost:8080/, we will get a 404 error Cannot GET /. This is because our demo app has no longer only one point of entry, it has two! So, now we can access every one of our modules separately using the pages we configured earlier:

http://localhost:8080/greeting.html
http://localhost:8080/search-form.html

Hurray! our two modules are alive.

Build the application

It's time to use our modules, perhaps in an existing application. So let's build the demo app to create the includable files:

First, stop the dev server (you can use ctrl+c).

Then run this command:

$ ~/demo-app> yarn build

Once the build is done, we get this output:

All the build files get created by default in the ./dist directory.

dist/js/chunk-vendors.js is the common code for both modules. It contains VueJs and all the dependencies needed to run them. It should be included in every page that we want to use any of our modules.

dist/js/greeting.js is the file containing the code for the Greeting module, and it should be included in every page we want to use it on. Also, the page where it's included should contain an element with the ID "greeting".

dist/js/search-form.js is the file containing the code for the Search Form module, and it should be included in any page we want to use it. Also, the page where it's included should contain an element with the ID "search-form".

Further considerations

  • Version control for you JS files, for clearing the browser cache whenever a new version of you module is released.
  • Create a core module to place the resources that are shared across all modules.
  • Implement individual Routing and Store management for every module.

You can find the working code for this demo app here:

https://github.com/KiweeEu/vue-modular-app-demo

 

FacebookTwitterPinterest

Antonio Valdez Arce

Senior Full Stack Developer

More than 10 years of experience in the IT industry has given me the opportunity to develop the ability to learn fast. From developing desktop applications to pushing pixels in a web site, this business demands the professional to keep updated and wider the range of specialisation, that's why I constantly try to stay in contact with new technologies and encourage my peers to do so.