Vue is a Javascript library and development framework primarily for building user interfaces.

Filed under

Getting Started

In this tutorial, we will learn how to use single file components in VueJS and use webpack to bundle our application. We will be using the NPM package management tool and various other tools that are themselves installed and managed using NPM. To be able to use these tools and therefore to properly follow this material, you must first install the latest NPM and Node.JS:

Once installed, you should be ready to move forward.

Single File Components

Single file components combines the structure, styling and behaviour of a page(or component) into a single file. Here's an example of a single file component:

<template>
	<h1>{{ message }}</h1>
</template>

<script>
export default {
	data() {
		return {
			message : "Develop the impossible!"
		}
	}
}
</script>

<style scoped>
h1 {
	font-size: 16px;
	text-align: center;
}
</style>

As you can see above, single file components allows us to define the following:

  • template - HTML code template

  • script - JavaScript logic

  • style - CSS styling

This file would be compiled into Javascript via webpack. Single file components make it easy to write CSS on each component that would not leak to other components by using <style scoped>. If you remove scoped, the CSS defined here would be declared globally.

Setup

Let's create our project directory. Create a new directory for your VueJS app. Then inside that directory, copy the project structure as illustrated below:

vuejs-sampleapp/
	src/
		app.js
	index.html
	package.json

Modify our index.html file and place the following:

<!DOCTYPE html>
<html>
	<head>
		<title>VueJS Sample App</title>
	</head>
	<body>
		<div id="app"></div>
	</body>
</html>

Now, let's add dependencies in our package.json file as shown below:

{
	"name": "vuejs-sampleapp",
	"description": "VueJS Sample App",
	"version": "1.0.0",
	"scripts": {
		"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
		"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
	},
	"dependencies": {
		"vue": "^2.5.11"
	},
	"devDependencies": {
		"babel-core": "^6.26.0",
		"babel-loader": "^7.1.2",
		"babel-preset-env": "^1.7.0",
		"babel-preset-stage-3": "^6.24.1",
		"cross-env": "^5.0.5",
		"webpack": "^3.6.0",
		"webpack-dev-server": "^2.9.1"
	}
}

Then execute the command npm install where the package.json file is located.

Webpack

Now that we have all our dependencies installed, let's now create our configuration file for webpack. Create a file called webpack.config.js and place the following:

var path = require('path')
var webpack = require("webpack")

module.exports= {
	// since we need to load index.js file as our entry point
	entry : "./src/app.js",
	output : {
		// This is where the output of webpack's result will go
		path: path.resolve(__dirname, './dist'),
    	publicPath: 'dist/',
		filename : "build.js"
	},
	module : {
		rules : [
		]
	}
}

Then modify our index.html file and link the output file(the one specified in our config file) as shown below:

<script src="dist/build.js"></script>

You could place the above code after the body tag in our HTML file.

Let's create a file called App.vue inside our src folder and place the following code:

<template>
<h1>{{ greeting }}</h1>
</template>

<script>
export default {
	data() {
		return {
			greeting : "This is awesome!"
		}
	}
}
</script>

<style scoped>
h1 {
	font-size: 36px;
}
</style>

Now this App component doesn't really much do anything, it will display a greeting inside an h1 tag but as of now, our application doesn't know about our App component. To render our App component inside our div with id #app (which we wrote in our index.html file), modify our index.js file and place the following:

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

new Vue({
	el : "#app",
	render: function (createElement) {
		return createElement(App);
	},
	components : { App }
})

Now since we are using the single file components(.vue), webpack doesn't know how to read those files. How should we handle vue files? Let's install the list of packages below by executing each of the command specified below:

npm install css-loader@^0.28.7 --save-dev
npm install vue-loader@^13.0.5 --save-dev
npm install vue-template-compiler@^2.4.4 --save-dev

or you could just include this 3 packages in our package.json under devDependencies section as shown below:

"devDependencies": {
	"babel-core": "^6.26.0",
	"babel-loader": "^7.1.2",
	"babel-preset-env": "^1.7.0",
	"babel-preset-stage-3": "^6.24.1",
	"cross-env": "^5.0.5",
	"css-loader": "^0.28.7",
	"vue-loader": "^13.0.5",
	"vue-template-compiler": "^2.4.4",
	"webpack": "^3.6.0",
	"webpack-dev-server": "^2.9.1"
}

Once included in package.json, you could just execute the command npm install to install the 3 packages.

  • css-loader - allow us to write CSS styles in our components

  • vue-loader - enables webpack to load our vue files

  • vue-template-compiler - this allows us to write the template section in our component and compile it to render functions

Then modify our webpack.config.js file and place the following inside the rules section:

{
	// enables us to load vue file or single file components
	test : /\.vue$/,
	loader : "vue-loader"
}

Then run the app by executing this command:

npm run dev

It will run the app in development mode and it will open the page (http://localhost:8080) in the browser. Now you have a working VueJS app using single file components, but we want to create more components and play a little bit more with vue files.

Create a folder inside our src directory and named it as components. Then create a file inside the components directory called Box.vue and place the following code:

<template>
	<div class="box">
		<div class="box-header">
			<strong v-if="headerText">{{ headerText }}</strong>
		</div>
		<div class="box-body"></div>
		<div class="box-footer"></div>
	</div>
</template>

<script>
export default {
	name: 'Box',
	props: [ 'headerText' ]
}
</script>

<style scoped>
.box {
	position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    word-wrap: break-word;
    background-color: #fff;
    background-clip: border-box;
    border: 1px solid rgba(0,0,0,.125);
    border-radius: .25rem;
}
.box-header {
	padding: .75rem 1.25rem;
    margin-bottom: 0;
    background-color: rgba(0,0,0,.03);
    border-bottom: 1px solid rgba(0,0,0,.125);
}
.box-body {
	float: left;
    padding: 1.25em;
    position: relative;
    width: 100%;
}
.box-footer {
	border-top: 1px solid rgba(0,0,0,.125);
	padding: .25rem 1.25rem;
}
</style>

In the <script> section above, you must export an object in JavaScript. This would represent the Vue instance of this component. Then in our App component template section, modify it to use the Box component as shown below:

<template>
	<div>
		<h1>{{ greeting }}</h1>
		<Box headerText="Header 1" />
	</div>
</template>

<script>
export default {
	data() {
		return {
			greeting : "This is awesome!"
		}
	}
}
</script>

<style scoped>
h1 {
	font-size: 36px;
}
</style>

But if you open your browser's Javascript console, you will see this error:

[Vue warn]: Unknown custom element: <Box> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

As the error says, we still need to register custom components in our application. You can register our Box component by importing the Box.vue file as shown below:

<script>
import Box from "./components/Box.vue"

export default {
	data() {
		return {
			greeting : "This is awesome!"
		}
	},
	components : {
		Box
	}
}
</script>

Then we also have added as shown above the components section where we have listed Box as one of the components. But having multiple components that you want to access across different components will really be painful when doing this again and again. If you want to declare certain components globally, you can do it by registering that component in our entry point file. Since we created a folder named components, we want all components on that folder to be registered globally, to do this, first, create a file called index.js inside our components directory and place the following:

import Vue from "vue"

import Box from "./Box.vue"

Vue.component("Box", Box)

export {
	Box
}

Here, we have imported our Box component, and register the component by calling:

Vue.component("Box", Box)

Then in our app.js file, import the index.js file that is located in our components directory. Your app.js file should look like this by now:

import Vue from 'vue'
import App from "./App.vue"
import { index } from "./components/index.js"

new Vue({
	el : "#app",
	render: function (createElement) {
		return createElement(App);
	},
	components : { App }
})

Then you can now remove the import statement in our App.vue and components section as our Box component is now registered globally. You could still add more components and register it globally, by modifying our index.js file and import your newly created components.

Using slot

Using <slot> tag, you could place certain elements inside a component on a certain place. To demonstrate this properly, remember our Box component? You can notice that we have a <div class="box-body"></div> inside but there is nothing inside that div. Modify our App.vue so that the way Box component is called is as shown below:

<Box headerText="Header 1>
	This should be place inside our box-body div
</Box>

Then modify our Box component and add a slot tag inside our div:

<div class="box-body">
	<slot></slot>
</div

If you refresh our page, you could see that the text "This should be place inside our box-body div" is actually placed inside our div "box-body". This is because when the component renders, the <slot> component is actually replaced by "This should be place inside our box-body div".

You could also use multiple slots, to do this, you should specify the name attribute of the slot tag. Modify our Box component so you could have two slots, one for the body, and one for a footer as shown below:

<div class="box-body">
	<slot name="body"></slot>
</div
<div class="box-footer">
	<slot name="footer"></slot>
</div>

Then in our App.vue, modify the way Box component is called so it will be like the code shown below:

<Box headerText="Header 1">
	<p slot="body">This should be place inside our box-body div</p>
	<p slot="footer">This is the box footer</div>
</Box>

If you refresh the page, you should see by now that we have create a simple box which contains a header, body, and footer using single file components.

Congratulations!


Twitter Facebook LinkedIn Youtube Slideshare Github