DEV Community

Cover image for How to create a Vue.js 2 / Bootstrap 4 project with Vite
Giannis Koutsaftakis
Giannis Koutsaftakis

Posted on

How to create a Vue.js 2 / Bootstrap 4 project with Vite

Vite is a lightning-fast dev environment and a pre-configured bundler into one. While it was first created to facilitate Vue.js 3 development, it can also be used for React, Svelte, Vanilla JS, and Vue.js 2.

There are plenty of tutorials for using Vite with Vue.js 3 and a lot of ready-made starter templates as well. We will be focusing on Vue.js 2 and see how we can create a base for a new project that:

  • Uses Bootstrap 4.6 CSS for layout and styling
  • Uses vue-router for client-side routing
  • Supports global SCSS variables inside Vue components
  • Is IE11 compatible
  • Removes unused CSS rules from the production bundle

Let's get right to it then!

Create a new Vite project

Although Vite contains many template presets out-of-the-box, it doesn't have one for Vue.js 2. We'll use the vanilla preset and then add the necessary plugins for Vue.js 2 development on top.

Navigate to your projects' directory and run:

Make sure you replace my-vue-app with your project's name.

npm 6.x

npm init vite@latest my-vue-app --template vanilla
Enter fullscreen mode Exit fullscreen mode

npm 7+ (extra double-dash is needed)

npm init vite@latest my-vue-app -- --template vanilla
Enter fullscreen mode Exit fullscreen mode

Install plugins needed for development

npm i -D vite-plugin-vue2 @vitejs/plugin-legacy vite-plugin-html vue-template-compiler sass@~1.32.13 postcss @fullhuman/postcss-purgecss autoprefixer
Enter fullscreen mode Exit fullscreen mode
  • vite-plugin-vue2 - Vue.js 2 plugin for Vite
  • @vitejs/plugin-legacy - Support for legacy browsers (IE11)
  • vite-plugin-html - Minification and EJS template-based functionality for index.html
  • vue-template-compiler - Pre-compiles Vue.js 2 templates into render functions
  • sass - Pre-processor for SCSS, we need version 1.32 to avoid a SASS deprecation warning that affects Bootstrap 4
  • postcss - Transforms styles with JS plugins
  • @fullhuman/postcss-purgecss - PostCSS plugin that removes unused selectors from our CSS
  • autoprefixer - PostCSS plugin that adds vendor prefixes to CSS rules, also needed by Bootstrap

Install Dependencies

Install Vue.js, Vue Router, and Bootstrap

npm i vue vue-router bootstrap@4.6.0
Enter fullscreen mode Exit fullscreen mode

We are using Bootstrap 4 since we want our project to be compatible with IE11

Setup file structure

It's time to adjust our project's structure so that it resembles that of a vue-cli project.

Remove style.css and move main.js out of the root folder and inside the src folder. Then create the following file/folder structure.

Don't worry about the files' contents for now, as we are going to fill them up as we go, just create empty files making sure they are named correctly.

├── src
│   │
│   ├── components
│   │   └── <-- store your project's components here
│   │ 
│   ├── router
│   │   └── index.js
│   │ 
│   ├── scss
│   │   ├── app.scss
│   │   └── variables.scss
│   │ 
│   ├── views
│   │   ├── About.vue
│   │   └── Home.vue
│   │ 
│   ├── App.vue
│   └── main.js
│
├── favicon.svg
├── index.html
├── postcss.config.js
└── vite.config.js
Enter fullscreen mode Exit fullscreen mode

index.html, main.js, and App.vue

Vite uses index.html as the entry point of the application, We'll replace the contents of index.html with the markup below. Notice the EJS style variables title and description as we're going to set them in vite.config.js next.

We include src/main.js as the only script tag and Vite will resolve our JS source code.

index.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, shrink-to-fit=no">
    <meta name="description" content="<%- description %>">
    <link rel="icon" type="image/svg+xml" href="favicon.svg" />
    <title><%- title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%- title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

new Vue({
  router,
  render: (h) => h(App)
}).$mount('#app')
Enter fullscreen mode Exit fullscreen mode

src/App.vue

<template>
  <div id="app">
    <ul class="nav nav-pills container pt-2">
      <li class="nav-item">
        <router-link to="/" exact exact-active-class="active" class="nav-link">
          Home
        </router-link>
      </li>
      <li class="nav-item">
        <router-link to="/about" class="nav-link">
          About
        </router-link>
      </li>
    </ul>
    <router-view />
  </div>
</template>

<script>
import '@/scss/app.scss'
</script>
Enter fullscreen mode Exit fullscreen mode

Vite config

The config file for Vite resides in the project's root. Here we're initializing the plugins for Vue.js 2 and IE11 compatibility as well as setting the title and description for our project.

We're also setting an alias of @ for the src folder and injecting SCSS variables globally so that they are accessible from inside Vue components.

vite.config.js

import { minifyHtml, injectHtml } from 'vite-plugin-html'
import legacy from '@vitejs/plugin-legacy'
const path = require('path')
const { createVuePlugin } = require('vite-plugin-vue2')

module.exports = {
  plugins: [
    createVuePlugin(),
    minifyHtml(),
    injectHtml({
      injectData: {
        title: 'ProjectName',
        description: 'A single page application created using Vue.js'
      }
    }),
    legacy({
      targets: ['ie >= 11'],
      additionalLegacyPolyfills: ['regenerator-runtime/runtime']
    })
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '/src'),
      '~bootstrap': 'bootstrap'
    }
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "./src/scss/variables";`
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

PurgeCSS

Bootstrap contains a lot of classes, but since we usually use a small set of the framework a lot of unused styles will be included in our CSS file. Let's configure PurgeCSS so that unnecessary styles will be stripped out of the final build.

postcss.config.js

const IN_PRODUCTION = process.env.NODE_ENV === 'production'

module.exports = {
  plugins: [
    require('autoprefixer'),
    IN_PRODUCTION &&
      require('@fullhuman/postcss-purgecss')({
        content: ['./**/*.html', './src/**/*.vue'],
        defaultExtractor(content) {
          const contentWithoutStyleBlocks = content.replace(/<style[^]+?<\/style>/gi, '')
          return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || []
        },
        whitelist: [],
        whitelistPatterns: [
          /-(leave|enter|appear)(|-(to|from|active))$/,
          /^(?!(|.*?:)cursor-move).+-move$/,
          /^router-link(|-exact)-active$/,
          /data-v-.*/
        ]
      })
  ]
}
Enter fullscreen mode Exit fullscreen mode

Bootstrap SCSS

Our app's SCSS is located in scss/app.scss file. Here we include all Bootstrap SCSS except functions, variables, and mixins, as these will go inside scss/variables.scss so that we can use them inside our Vue.js components without explicitly importing them.

scss/app.scss

// Bootstrap source files (except functions, variables, mixins)
@import "~bootstrap/scss/root";
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/code";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/button-group";
@import "~bootstrap/scss/input-group";
@import "~bootstrap/scss/custom-forms";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/card";
@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/pagination";
@import "~bootstrap/scss/badge";
@import "~bootstrap/scss/jumbotron";
@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/progress";
@import "~bootstrap/scss/media";
@import "~bootstrap/scss/list-group";
@import "~bootstrap/scss/close";
@import "~bootstrap/scss/toasts";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/popover";
@import "~bootstrap/scss/carousel";
@import "~bootstrap/scss/spinners";
@import "~bootstrap/scss/utilities";
@import "~bootstrap/scss/print";

// Other application-wide SCSS rules here...
Enter fullscreen mode Exit fullscreen mode

Our variables.scss contains overrides of Bootstrap SCSS variables as well as our own.

scss/variables.scss

$primary: #42b983;
$body-color: #304455;
$info:    #73abfe;
$gray-100: #f6f6f6;
$text-muted: #4e6e8e;
$gray-900: #273849;
$dark: #273849;

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";

$navbar-dark-color: rgba($white, .7);
Enter fullscreen mode Exit fullscreen mode

Routing and Views

Finally, let's set up our routing and the contents of our two sample views Home and About.

src/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    component: () => import('@/views/About.vue')
  }
]

const router = new VueRouter({
  linkActiveClass: 'active',
  routes
})

export default router
Enter fullscreen mode Exit fullscreen mode

src/views/Home.vue

<template>
  <div class="container">
    <div class="text-center">
      <h1>This is the home page</h1>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

src/views/About.vue

<template>
  <div class="container">
    <div class="text-center">
      <h1>This is the about page</h1>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Develop and Build

In order to start developing we run the command

npm run dev
Enter fullscreen mode Exit fullscreen mode

This will start the dev server on http://localhost:3000

For bundling our app for production we use the following command

npm run build
Enter fullscreen mode Exit fullscreen mode

This will create all the build assets inside the dist folder ready for us to deploy anywhere we like.

Finally
If you found this how-to useful, make sure you check out vue-vite-starter-template, which also includes ESLint, Prettier, semantic-release, Jest for testing, and more...

Thanks for reading!

Top comments (2)

Collapse
 
thecryptowriter profile image
David McNeal

I had to fix an error in the code(maybe due to my system?). Anyhow

The Error Message
src/router/index.js:17:20: error: [plugin: vite:dep-scan] ENOENT: no such file or directory, open 'D:/src/views/About.vue'

So I went into vite.config.js and changed

'@': path.resolve(__dirname, '/src'),

to

'@': path.resolve(__dirname, './src'),

adding a '.' to indicate the project root, rather than my system root:

Collapse
 
warhead5555 profile image
Warhead5555

Nice article. Is it possible to use Vite for an existing vue 2 app?