Create a modular app with Webpack and VueJs
Antonio Valdez ArceReading Time: 3 minutes
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":
Select Babel, Typescript, CSS Preprocessors and Linter/Formatter.
Then select the following options:
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!
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: <code>${__dirname}/src/modules/greeting/main.ts</code>, template: <code>${__dirname}/public/greeting.html</code>, filename: 'greeting.html', title: 'Demo - Greeting', }, 'search-form': { entry: <code>${__dirname}/src/modules/search-form/main.ts</code>, template: <code>${__dirname}/public/search-form.html</code>, 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: