18. Why Use the Vue CLI?
The previous tutorials taught us how to create a vue app in a single HTML element. There, Vue could control only that HTML element.
The Vue CLI will allow us to modify the entire web site, use modern JavaScipt code, creates a live dev server, and allows us to optimize our code.
19. How to Use the Vue CLI?
Must install node.js, namely npm, to install the Vue CLI.
To open the terminal in VSC type ctrl+`
After installing node, execute the node -v
in the terminal. If installation is successful, you should see a version number.
Type the following to install the Vue CLI (command line interface) globally on the computer.
$ npm install -g @vue/cli
In your terminal, change your working directory to your projects directory.
Now create a Vue project using the following command.
$ vue create app2
Arrow down and choose Manually select features
. Turn the Linter off and accept the remaining default values.
After the script creates the project, do not run npm run serve.
Change directory to the directory that the CLI created.
$ cd app2
20. New Project Walkthrough
The CLI created index.html with a div having an id of app. This is where Vue will dynamically inject templates.
The src
folder is where we will put our code. It already has some files and directories in it.
> assets > components HelloWorld.vue App.vue main.js
The main.js file contains the following.
import {createApp} from 'vue' import App from './App.vue' createApp(App).mount('#app')
This kickstarts the application. createApp
is imported from the vue library in the node modules.
App.vue contains a template, script, and style. The script exports an object. We import that object as App
.
Then in main.js, we create a Vue app using the App
object and mount the app as the root component in the div
that has an id
named app
in public/index.html.
Inside package.json are scripts. One is named serve. During development, if we want to test our app, we can run the serve script to start a web server on localhost to view and debug the app on localhost.
$ npm run serve
If we want to host our code on a shared-hosting server like Godaddy we do not need to run the serve script. Instead we need to build a distribution of the app using the following command. The script will create a new directory named dist
which will contain the code which can be loaded on the shared hosting server.
Before we run the build script however, we need to make one change. The build script assumes that we will upload the distribution code to the root folder of our server (public_html). This is likely not where you want to instal the app.
If you plan on accessing the app from somewhere other than public_html
folder we need to change configure vue.config.js
. Edit your vue.config.js file so that it looks like the following:
module.exports = defineConfig({ transpileDependencies: true, publicPath: "./" })
Now run the build script.
$ npm run build
The build script creates a directory named dist which contains a minified version of your app. Copy the code in dist to your where your production code resides on your laptop, then upload the production code to your server.
21. Vue Files & Templates
Every Vue component (file ending in .vue) must have a template
element. They can also have optional script
and style
elements. The .vue file must specify the template
element first.
The template
element can have multiple root elements (previously, it could only have one).
Inside App.vue, we see the following template and script elements.
<template> <img alt="Vue logo" src="./assets/logo.png"> <msg="Welcome to Your Vue.js App"/> </template> <script> import from './components/HelloWorld.vue' export default { name: 'App', components: {} } </script>
The HelloWorld.vue file contains another component. The App
component imports the HelloWorld
component from HelloWorld.vue and injects it in the App
template which is injected into the app div in index.html.
We can use the techniques we saw in the last section by modifying the object that is exported. Here we return an object with a data()
function and use the data in the template.
<template> <h1>{{ title }}</h1> </template> <script> export default { name: 'App', data() { return { title: 'My first Vue App' } } } </script>
22. Template Refs
In JavaScript if you need a DOM element you use the queryselector. In Vue we use don’t use queryselector, we use template refs.
Template refs allow us to store a reference to a DOM element.
<template> <input type="text" ref="name"> <button @click="handleClick">click me</button> </template>
Now in the app object we add the event listener.
methods: { handleClick() { this.$refs.name.classList.add('active') this.$refs.name.focus() } }
23. Multiple Components
It is not a good idea to put all of your code in one Vue component.
We structure our Vue application in components. These components form a tree.
App.Vue // root component Header.vue Article.vue Content.vue Comments.vue Footer.vue
Vue components should start with a capital letter and end with .vue.
Let’s remove the HelloWorld component and create a new component named Modal with the following template.
<template> <div class="backdrop"> <div class="modal"> <p> modal content </p> </div> </div> </template>
We want to use the Modal component in the App component.
In App.vue we need to import the Modal from Modal.vue.
<script> import Modal from '.components/Modal.vue' export default { name: 'App', components: { Modal }, ... } </script>
Now inside the App template we can add the Modal subcomponent as an HTML element.
<template> <Modal/> </template>
24. Component Styles & Global Styles
By default, all styles declared in the component files are loaded into the head of the document and are global.
If we want styles to apply only to the component in which they’re defined we add the scoped
attribute to the style
element.
<style scoped> ... </style>
If you need global styles we can create a global.css file in the assets directory.
Then in main.js we simply import global.css.
import './assets/global.css'
25. Passing Data with Props
Suppose we want to pass data from the App component to the Modal component. For example, pass the title and modal content into the Modal component. To do this we define props in the Modal component for the title and modal content. Props are exported in the script tag of the component.
<script> export default { props: ['title', 'content'] } </script>
Props can be used in the template of the component in which they are defined, just like data properties.
<template> <div class="backdrop"> <div class="modal"> <h1>{{ title }}</h1> <p>{{ content }} </p> </div> </div> </template>
Now in the Modal element that we declared in the App component we can specify the values for the props.
<Modal title="New Modal Title" content="New Modal Content" />
In the App component we can also bind data properties to the props.
<template> ... <Modal :title="modalTitle" :content="modalContent" /> </template> <script> ... export default { data() { return { modalTitle: 'Binded Title', modalContent: 'Binded Data' } } } </script>
We can also add a dynamic class based on the value of a prop. In Modal.vue we add the following code. Here, we add the class name sale
if the value of theme
is equal to the string sale.
<template> <div class="backdrop"> <div class="modal" :class="{sale: theme === 'sale'}"> <h1>{{ title }}</h1> <p>{{ content }} </p> </div> </div> </template> <script> export default { props: ['title', 'content', 'theme'] } </script>
In the app component we set the theme property to sale.
<Modal :title="modalTitle" :content="modalContent" theme="sale"/>
26. Emitting Custom Events
To show the modal we’ll do a few things. First, we’ll create a boolean data property (initialized to false) that indicates if the modal should be displayed. Then, we’ll use the conditional v-if directive to display the modal based on the value of the boolean data property. Last, we’ll create a button to toggle the data property’s boolean value to true.
data() { return { ... showModal: false } }, methods: { ... toggleShowModal() { this.showModal = !this.showModal } } <Modal v-if="showModal" ... /> <button @click="toggleShowModal">show modal</button>
Now once the modal is displayed we need a way, from the modal component, to toggle the ShowModal data property in the App component. To do this we’ll create a custom event that can be fired from the child component (modal) and listed to in the parent component (app)
First, we’ll create a method named closeModal
and register it as an event handler for the click event on the backdrop
of the modal. Next, we’ll add the closeModal
method to the modal component’s export object. In the method we’ll fire off a custom event named close
.
<div class="backdrop" @click="closeModal"> export default { ... methods: { closeModal() { this.$emit('close') } } }
Then on the Model element in the app component we register an event listener for the close event.
<Modal v-if="showModal" ... @close="toggleShowModal"/>
27. Click Event Modifiers
Currently if we click on the modal itself, it closes since the modal is a child of the backdrop. To prevent this we’ll add a self
event modifier to the close event handler in the modal component so that the event (click) handler is only executed for mouse clicks on the backdrop and not its children.
<div class="backdrop" @click.self="closeModal">
28. Slots
Sometimes we want to pass templates from one component to another. To do so, we’ll use slots.
A slot is defined with a slot element in the component that will receive template elements. For example, we can define a slot in the modal component’s template element.
<template> <div class="backdrop" @click.self="closeModal"> <div class="modal" :class="{sale: theme === 'sale'}"> <slot></slot> </div> </div> </template>
The parent component can then send HTML elements to fill the slot. For example, the App component can fill the slot by specifying HTML elements in between opening and closing Modal elements.
<Modal v-if="showModal" ... @close="toggleShowModal"> <h1>Slot Title</h1> <p>Slot Description</p> </Modal>
If the parent component doesn’t pass anything into the slot nothing is displayed. You can specify default content between the slot tags. The default content is only displayed if the parent does pass content.
<slot> <h1>Default Title</h1> </slot>
We can also pass in named slots. A named slot is a slot element with a name attribute.
<template> <div class="backdrop" @click.self="closeModal"> <div class="modal" :class="{sale: theme === 'sale'}"> <slot></slot> <div> <slot name="links"></slot> </div> </div> </div> </template>
The parent component can specify a content for a named slot using a template
element.
<Modal v-if="showModal" ... > ... <template v-slot:links> <a href="#">sign up now</a><br> <a href="#">more info</a> </template> </Modal>
29. Challenge- Reusing Components
- create an extra button to open a different modal
- use the same modal component, but pass in a different template slot
- use a different method (e.g. toggleModal2) to toggle on the modal.
30. Using Teleport
One of the new features in Vue 3 allows you to place (teleport) outside the V3 app some of the HTML content that is defined inside a component file.
For example we can place the modals outside the app div. Inside index.html, lets create a div below the app div and give it an id named modals.
<div id="app"></div> <!-- built files will be auto injected --> <div id="modals"></div>
Now inside the appcomponent where we create instances of the modal component, we wrap the modal elements in <teleport> tags and specify where we want to load the modals by including a to attribute. The value of the true attribute is a CSS selector specifying the id of the div in index.html.
<teleport to="#modals"> <Modal ...> ... </Modal> </teleport>
Note that the stying of the modals may have changed since the modals no longer exist in the app element.
© 2022, Eric.