How to create a Vue.js 2 / Bootstrap 4 project with Vite
Giannis Koutsaftakis
Posted on August 25, 2021
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
npm 7+ (extra double-dash is needed)
npm init vite@latest my-vue-app -- --template vanilla
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
- 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
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
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>
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')
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>
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";`
}
}
}
}
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-.*/
]
})
]
}
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...
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);
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
src/views/Home.vue
<template>
<div class="container">
<div class="text-center">
<h1>This is the home page</h1>
</div>
</div>
</template>
src/views/About.vue
<template>
<div class="container">
<div class="text-center">
<h1>This is the about page</h1>
</div>
</div>
</template>
Develop and Build
In order to start developing we run the command
npm run dev
This will start the dev server on http://localhost:3000
For bundling our app for production we use the following command
npm run build
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!
Posted on August 25, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.