Goal: Make a navigation tabbar
An analysis of.
Our goal is to make a navigation tabbar, request
- This navigation can be used not only on one page, but also across multiple pages
- The style of each page may be different
- Each page icon, text may be different
- The number of navigations per page may vary
To achieve this, we need to disassemble the function:
- Extract a generic tabBar and then define slots in it to put tabbarItems as needed,
- TabBarItem contains images, text. Images and text are also slots, and different tabbarItems may display different images with different text.
- The tabBarItem data structure needs to be defined as a JSON that specifies the url to jump to
Two. Framework implementation
1. How do we usually achieve this?
Step 1: Define an HTML section in app. vue with the id of the outer div as tabBar and the class attribute of the inner div as tabBarItem.
<template>
<div id="app">
<div id="tabBar">
<div class="tabBarItem">Home page</div>
<div class="tabBarItem">classification</div>
<div class="tabBarItem">The shopping cart</div>
<div class="tabBarItem">my</div>
</div>
</div>
</template>
Copy the code
Step 2: Define the body style.
In general, the body style is defined separately in the main.css file. Put it in assets
body {
margin: 0px;
padding: 0px;
}
Copy the code
Having defined the main.css file, we need to import it into the app.vue file
<style>
@import "./assets/main.css";
</style>
Copy the code
Import CSS file styles using @import ‘file path’ and import JS files using import ‘file path’
Step 3: Define the tabBar style
TabBar uses Flex Flex layout.
#tabBar {
display: flex;
}
.tabBarItem {
flex: 1;
text-align: center;
}
Copy the code
This code specifies the layout used by tabBar. It has an elastic layout based on the number of child elements. In the child elements we set flex: 1 for each element to mean that the space is allocated equally as it is available.
Step 4: Define additional styles
<style>
@import "./assets/main.css";
#tabBar {
display: flex;
background-color: #e6e6e6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -3px 1px darkgrey;
}
.tabBarItem {
flex: 1;
text-align: center;
}
</style>
Copy the code
In addition to layout styles, there are bottom alignment, navigation shadows and so on.
Summary: This navigation is not generic, if we want to reuse, we need to copy the HTML content, and also copy the CSS style. We can take the common part as a component
2. Abstract the tabBarItem component
Step 1: Create a new component tabBarItem in Components
This is a simple extraction that takes the functionality we just put in app.vue and extracts it into a single component
<template>
<div id="tabBar">
<div class="tabBarItem">Home page</div>
<div class="tabBarItem">classification</div>
<div class="tabBarItem">The shopping cart</div>
<div class="tabBarItem">my</div>
</div>
</template>
<script>
export default {
name: "tabBarItem"
}
</script>
<style scoped>
#tabBar {
display: flex;
background-color: #e6e6e6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -3px 1px darkgrey;
}
.tabBarItem {
flex: 1;
text-align: center;
}
</style>
Copy the code
Then, introduce the components in app.vue
<script>
import TabBar from "./components/TabBar"
export default {
name: 'App'.components: {
TabBar
}
}
</script>
Copy the code
In Vue, you can call a component using its abbreviation, as follows:
<div id="app">
<tab-bar></tab-bar>
</div>
Copy the code
This makes tabBarItem even more reusable.
3. Improve the tabBarItem component
We know that tabBar has text as well as pictures. When we mouse click, there are corresponding pictures or mosquito style changes. Now let’s do that, change the image and the text. Step 1: Place two images in the tabBarItem, one unclicked and one clicked. Bring your own pictures. Any pictures
<div class="tabBarItem">
<slot name="item-pic"></slot>
<slot name="item-pic-active"></slot>
<div ><slot name="item-name"></slot></div>
</div>
Copy the code
Add a slot to store the second image. Pass two images in the call
<tab-bar-item>
<img slot="item-pic" src=".. /.. /assets/img/tabBar/1.jpeg">
<img slot="item-pic-active" src=".. /.. /assets/img/tabBar/4.jpeg">
<div slot="item-name">Home page</div>
</tab-bar-item>
Copy the code
Here you pass in two images and specify the slot position for each image
Then let’s look at the results:
It worked. It worked. But we want: mouse not click, display figure 1; Click the mouse and figure 2 will appear. This is easy to implement, using an isActive variable to modify the TabBarItem component
<template>
<div class="tabBarItem">
<div v-if=! "" isActive"><slot name="item-pic"></slot></div>
<div v-else><slot name="item-pic-active"></slot></div>
<div><slot name="item-name"></slot></div>
</div>
</template>
<script>
export default {
name: "TabBarItem".data() {
return {
isActive: false}}}</script>
Copy the code
Define a variable isActive in the component script, and then use V-if on the slot to do the trick. Notice how v-if and v-else are written.
We have a convention here that we usually don’t write v-if or V-else inside the slot, because that will be replaced later. So, wrap a div around it
Let’s look at the effect: When we set isActive:false, it looks like this
When we set isActive:true, the effect is as follows:
As you can see, I’ve just switched the order of the four images. The specific picture is not important, the important thing is that the effect is good.
Step 2: Change color when text is active.
This is even easier.
<template>
<div class="tabBarItem">
<div v-if=! "" isActive"><slot name="item-pic"></slot></div>
<div v-else><slot name="item-pic-active"></slot></div>
<div v-bind:class="{active:isActive}"><slot name="item-name"></slot></div>
</div>
</template>
<style scoped>..active {
color: red; }...</style>
Copy the code
Bind directly to a class style, and when the text isActive (isActive:true), the text is red to see the effect:
This completes the tabBarItem wrapper
Realization of navigation routing function
Now that the tabBar navigation is complete, we click on the home page, which should show the home page component content. Clicking on the category should display the category component content. Let’s implement this part of the function. This is the routing function of navigation.
The first step is to install the routing component
npm install vue-router --save
Copy the code
Vue-router is a runtime dependency, so the –save parameter is required
Step 2: Create the Router folder and create the index.js file
// Step 1: import vue and vue-router packages
import Vue from 'vue'
import Router from 'vue-router'
// Step 2: Install vue-Router plug-in
Vue.user(Router)
// Step 3: Create Router components
const route = [{
}]
const vueRouter = new Router({
route: route
})
// Step 4: Export Route component
export default vueRouter
Copy the code
Step 3: Introduce vueRouter in the main.js file
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h= > h(App)
})
Copy the code
Step 4: Plan the project structure
In general, we have all the common component modules under the Components directory. For page-related modules, we will create a separate folder, the folder name can be views or Pages or other folders, business related pages are stored in this folder. Our project directory structure is as follows:
We created 4 directories under views to store the routing content of each navigation component.
Step 5: Add a route
Next, configure the route for the navigation
// Import the component first
const Home = () = > import('.. /views/home/Home')
const Cart = () = > import('.. /views/cart/Cart')
const Category = () = > import('.. /views/category/category')
const Profile = () = > import('.. /views/profile/profile')
// Then configure the route
const routes = [{
path: "".redirect: "/home"}, {path: "/home".components: Home
},{
path: "/category".components: Category
},{
path: "/cart".components: Cart
},{
path: "/profile".components: Profile
}]
const router = new VueRouter({
routes
})
Copy the code
Four routes are configured to route to corresponding components. When an empty route is configured, the /home route is located. Now that the route is configured, click events are configured for the button. Our tabBarItem component wraps like this
<template>
<div class="tabBarItem" v-on:click="clickItem">
<div v-if=! "" isActive"><slot name="item-pic"></slot></div>
<div v-else><slot name="item-pic-active"></slot></div>
<div v-bind:class="{active:isActive}"><slot name="item-name"></slot></div>
</div>
</template>
Copy the code
We add a click event at the component level, but the jump url is not fixed, and we pass it in as a parameter.
v-on:click="clickItem"
Copy the code
Data is passed between components using the props property
<script>
export default {
name: "TabBarItem".props: ["path"].data() {
return {
isActive: false}},methods: {
clickItem() {
this.$router.push(path);
}
}
}
</script>
Copy the code
Define a props property in the component to receive using an itemUrl. Add the parameter item-URL =”/home” to the TabBar. The other three components are called the same way.
<tab-bar-item path="/home" >
<img slot="item-pic" src=".. /.. /assets/img/tabBar/1.jpeg">
<img slot="item-pic-active" src=".. /.. /assets/img/tabBar/4.jpeg">
<div slot="item-name">Home page</div>
</tab-bar-item>
Copy the code
Finally, add a component display area in app.vue
<template>
<div id="app">
<router-view></router-view>
<tab-bar></tab-bar>
</div>
</template>
Copy the code
Let’s see what happens
Step 6: Style the checked/unchecked buttons
<script>
export default {
name: "TabBarItem".props: ["path"].computed: {
isActive(){
return this.$route.path.indexOf(this.path) ! = = -1}},data() {
return {
// isActive: false}},methods: {
clickItem() {
this.$router.push(this.path); }}}</script>
Copy the code
Added a calculation attribute. If the route has the same path as the current forward route, the route is selected. Otherwise, the route is not selected. The effect is as follows:
Step 7: Extract the navigation text style
Now, we set the text to be red when navigation is active, but… Not all navigation is red when activated, so we need to set it dynamically. That is, a parameter is passed from the caller through the component. As follows:
<tab-bar-item path="/home" activeStyle="blue"> <img slot="item-pic" src=".. /.. /assets/img/tabBar/1.jpeg"> <img slot="item-pic-active" src=".. /.. 4 / assets/img/tabBar/jpeg "> < div slot =" item - the name "> home page < / div > < / TAB bar - item >Copy the code
Add a property activeStyle=”blue”. Accordingly, we need to add a prop property where the component definition is located
props: {
path: String,
activeStyle: {
type: String,
default: 'red'
}
},
Copy the code
Add the activeStyle style to the Prop property and set the default style red.
computed: { isActive(){ return this.$route.path.indexOf(this.path) ! == -1 }, isActiveStyle() { return this.isActive ? {color: this.activeStyle}:{} } },Copy the code
Add a property isActiveStyle to the calculated properties. If the navigation is active, the style will be displayed. Otherwise, there is no style to see the effect.
Step 8: Further componentize
Let’s take a look at the app.vue file
<template>
<div id="app">
<router-view></router-view>
<tab-bar>
<tab-bar-item path="/home" activeStyle="blue">
<img slot="item-pic" src="./assets/img/tabBar/1.jpeg">
<img slot="item-pic-active" src="./assets/img/tabBar/4.jpeg">
<div slot="item-name">Home page</div>
</tab-bar-item>
<tab-bar-item path="/category" activeStyle="green">
<img slot="item-pic" src="./assets/img/tabBar/2.jpeg">
<img slot="item-pic-active" src="./assets/img/tabBar/3.jpeg">
<div slot="item-name">classification</div>
</tab-bar-item>
<tab-bar-item path="/cart" activeStyle="pink">
<img slot="item-pic" src="./assets/img/tabBar/3.jpeg">
<img slot="item-pic-active" src="./assets/img/tabBar/2.jpeg">
<div slot="item-name">The shopping cart</div>
</tab-bar-item>
<tab-bar-item path="/profile" activeStyle="purple">
<img slot="item-pic" src="./assets/img/tabBar/4.jpeg">
<img slot="item-pic-active" src="./assets/img/tabBar/1.jpeg">
<div slot="item-name">my</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from "./components/tabBar/TabBar"
import TabBarItem from "./components/tabBar/TabBarItem";
export default {
name: 'App'.components: {
TabBar,
TabBarItem
}
}
</script>
<style>
@import "./assets/main.css";
</style>
Copy the code
In the template part, there is a lot of content, usually the content of app. vue is very concise, so we can also abstract this part of the component to extract the file to MainTabBar, after the extraction pay attention to the image file and the path of the Vue component
<template>
<tab-bar>
<tab-bar-item path="/home" activeStyle="blue">
<img slot="item-pic" src=".. /.. /assets/img/tabBar/1.jpeg">
<img slot="item-pic-active" src=".. /.. /assets/img/tabBar/4.jpeg">
<div slot="item-name">Home page</div>
</tab-bar-item>
<tab-bar-item path="/category" activeStyle="green">
<img slot="item-pic" src=".. /.. /assets/img/tabBar/2.jpeg">
<img slot="item-pic-active" src=".. /.. /assets/img/tabBar/3.jpeg">
<div slot="item-name">classification</div>
</tab-bar-item>
<tab-bar-item path="/cart" activeStyle="pink">
<img slot="item-pic" src=".. /.. /assets/img/tabBar/3.jpeg">
<img slot="item-pic-active" src=".. /.. /assets/img/tabBar/2.jpeg">
<div slot="item-name">The shopping cart</div>
</tab-bar-item>
<tab-bar-item path="/profile" activeStyle="purple">
<img slot="item-pic" src=".. /.. /assets/img/tabBar/4.jpeg">
<img slot="item-pic-active" src=".. /.. /assets/img/tabBar/1.jpeg">
<div slot="item-name">my</div>
</tab-bar-item>
</tab-bar>
</template>
<script>
import TabBar from "./TabBar";
import TabBarItem from "./TabBarItem";
export default {
name: "MainTabBar".components: {
TabBar,
TabBarItem
}
}
</script>
<style scoped>
</style>
Copy the code
Then, MainTabBar is introduced in app.vue
<template>
<div id="app">
<router-view></router-view>
<main-tab-bar></main-tab-bar>
</div>
</template>
Copy the code
The effect is the same as before
Alias the file
When we have a lot of paths, as we did when we abstracted the components above, we find that the file location changes, and many paths change with it. In VUE, you can alias the path so that you don’t have to change the path after you change the file location. In the build/webpack base. Conf. Js file, there was a resolve option
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
}
},
Copy the code
- Extensions: Indicates that you can omit the extension name when importing a path
- Alias: Indicates an alias for the path. Resolve (‘ SRC ‘) indicates an alias for the SRC path.
This way, we can give the other folders an alias as well.
resolve: {
extensions: ['.js'.'.vue'.'.json'].alias: {
The '@': resolve('src'),
'components': resolve('@/components'),
'assets': resolve('@/assets'),
'router': resolve('@/router'),
'views': resolve('@/views')}},Copy the code
When given an alias, such as SRC /components, the path can use @/components. For subsequent files that use this path, just use @/components
In use, it is also divided into several scenarios
- Import the path in the component using import
- No import, such as image path
- Import path in routing navigation
1. Import components using import
Before we introduced MainTabBar in app.vue, we introduced the script like this:
import MainTabBar from "./components/tabBar/MainTabBar";
Copy the code
Now that we have configured the path alias, we can omit the./ and start with components
import MainTabBar from "components/tabBar/MainTabBar";
Copy the code
The same is true for style style references
<style>
@import "assets/main.css";
</style>
Copy the code
Let’s just go ahead and omit the dot/PI.
2. Introduce images and other non-imports
For example, when we set the navigation icon in the maintabbar. vue component, there is a lot of SRC, which is what we wrote before
<tab-bar-item path="/profile" activeStyle="purple"> <img slot="item-pic" src=".. /.. /assets/img/tabBar/4.jpeg"> <img slot="item-pic-active" src=".. /.. / assets/img/tabBar / 1. Jpeg "> < div slot =" item - the name "> I < / div >Copy the code
After the route alias is defined, we can write it as follows:
<tab-bar-item path="/home" activeStyle="blue">
<img slot="item-pic" src="~assets/img/tabBar/1.jpeg">
<img slot="item-pic-active" src="~assets/img/tabBar/4.jpeg">
<div slot="item-name">Home page</div>
</tab-bar-item>
Copy the code
The alias assets is used, but must be preceded by a ~.
3. The path in a route
So far, I’ve found that by introducing components into the route, you can’t use aliases, but you can use the @ symbol to represent SRC
//const Home = () => import('@/views/home/Home')
import Home from '@/views/home/Home';
const Cart = () = > import('@/views/cart/Cart')
const Category = () = > import('@/views/category/category')
const Profile = () = > import('@/views/profile/profile')
Copy the code
Once the alias is used, an error is reported. No matter it’s here or not. Still need to explore
That’s also a problem
Note: after we modify the webpack.base.conf.js configuration file, we need to restart the service. Otherwise it will not take effect