目標: 作一個導航tabbarjavascript
咱們的目標是作一個導航tabbar, 要求css
要想實現上面的狀況, 須要進行功能拆解:html
<template> <div id="app"> <div id="tabBar"> <div class="tabBarItem">首頁</div> <div class="tabBarItem">分類</div> <div class="tabBarItem">購物車</div> <div class="tabBarItem">個人</div> </div> </div> </template>
一般body樣式, 咱們將其單獨定義到main.css文件中. 放在assets目錄下vue
body { margin: 0px; padding: 0px; }
定義好了main.css文件, 須要將其引入到App.vue文件中java
<style> @import "./assets/main.css"; </style>
引入css文件樣式使用的是@import '文件路徑', 而引入js文件使用的是import '文件路徑'webpack
tabBar採用的是Flex彈性佈局的佈局方式.web
#tabBar { display: flex; } .tabBarItem { flex: 1; text-align: center; }
上面這段代碼就指定了tabBar採用的佈局方式. 它會根據子元素的個數進行彈性佈局. 在子元素中咱們設置每一個元素的flex: 1
表示的是在空間可用的狀況下, 平均分配空間.vue-router
<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>
這裏面除了有佈局樣式,還有底部對齊, 導航的陰影等等.npm
總結: 這樣的導航不通用, 若是咱們要複用, 須要拷貝html內容, 還要拷貝css樣式. 咱們能夠把公共部分提成一個組件json
這個提取比較簡單, 就是將咱們剛剛在App.vue中的功能提取出一個單獨的組件
<template> <div id="tabBar"> <div class="tabBarItem">首頁</div> <div class="tabBarItem">分類</div> <div class="tabBarItem">購物車</div> <div class="tabBarItem">個人</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>
而後, 在App.vue中引入組件
<script> import TabBar from "./components/TabBar" export default { name: 'App', components: { TabBar } } </script>
vue裏面, 可使用組件的簡稱調用組件, 以下所示:
<div id="app"> <tab-bar></tab-bar> </div>
這樣, tabBarItem的可複用性就更強了.
咱們知道tabBar除了有圖片, 還有文字. 當咱們鼠標點擊的時候還有對應的圖片或者蚊子樣式的變化.
下面咱們來實現, 改變圖片和文字.
第一步: 在tabBarItem中放兩張圖片, 一張是未點擊的, 另外一張是點擊後的圖片. 圖片自備, 什麼圖均可以
<div class="tabBarItem"> <slot name="item-pic"></slot> <slot name="item-pic-active"></slot> <div ><slot name="item-name"></slot></div> </div>
如上代碼: 比以前多了一個slot, 用來存放第二張圖片的.
在調用
<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">首頁</div> </tab-bar-item>
這裏就傳了兩張圖片, 並指定每張圖片的插槽位置
而後咱們來看看效果:
效果出來了,達到預期. 但咱們但願:鼠標不點擊,顯示圖一; 鼠標點擊, 顯示圖二.
這個容易實現, 使用一個isActive變量便可
修改TabBarItem組件
<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>
在組件腳本中定義一個變量isActive, 而後對插槽使用v-if便可實現效果. 注意v-if和v-else的寫法.
這裏咱們有一個約定,一般不在插槽的裏面寫v-if或者v-else, 由於這部份內容後面會被替換掉. 因此, 在外層包一個div
下面來看看效果:
當咱們設置isActive:false, 效果以下
當咱們設置isActive:true, 效果以下:
能夠看出, 我這裏面就是把四張圖片調換了一下順序. 具體什麼圖片不重要, 重要的是效果出來了就好.
這個就更簡單了.
<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>
直接綁定一個class樣式, 當文字被激活時, 也就是isActive:true的時候, 文字顯示紅色
來看看效果:
以上就實現了tabBarItem的封裝
如今tabBar導航已經完成了, 接下來, 咱們點擊首頁, 應該展現首頁組件內容. 點擊分類應該展現分類組件內容.下面咱們來具體實現這部分功能. 這就是導航的路由功能.
npm install vue-router --save
vue-router是一個運行時依賴, 因此須要加上--save參數
// 第一步: 引入vue 和 vue-router包 import Vue from 'vue' import Router from 'vue-router' // 第二步: 安裝Vue-router插件 Vue.user(Router) // 第三步: 建立Router組件 const route = [{ }] const vueRouter = new Router({ route: route }) // 第四步: 導出Route組件 export default vueRouter
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) })
一般, 咱們在components目錄下放的都是全部公共組件模塊. 那麼頁面相關的模塊, 咱們會在單首創建一個文件夾, 文件夾的名字能夠叫views或者pages或者其餘, 業務相關的頁面都放着這個文件夾裏面. 咱們的項目目錄結構以下:
咱們在views下面又建立了4個目錄, 分別來存放每個導航組件的路由內容.
接下來要爲導航配置路由
// 先引入組件 const Home = () => import('../views/home/Home') const Cart = () => import('../views/cart/Cart') const Category = () => import('../views/category/category') const Profile = () => import('../views/profile/profile') // 而後配置路由 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 })
這裏配置了4個路由, 分別路由到對應的組件, 當空路由的時候, 定位到/home路由.
路由配好了, 接下來爲按鈕配置點擊事件.
咱們的tabBarItem組件封裝之後是這樣
<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>
咱們在組件級別上增長一個點擊事件, 但跳轉的url是不固定的, 咱們要經過參數傳過來.
v-on:click="clickItem"
組件間傳遞數據使用props屬性
<script> export default { name: "TabBarItem", props:["path"], data() { return { isActive: false } }, methods: { clickItem() { this.$router.push(path); } } } </script>
在組件中定義一個props屬性, 使用itemUrl進行接收. 而後在組件調用的地方傳遞過來參數就能夠了,
在TabBar中增長參數item-url="/home", 其餘三個組件調用方式相同.
<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">首頁</div> </tab-bar-item>
最後在App.vue中增長組件展現區域
<template> <div id="app"> <router-view></router-view> <tab-bar></tab-bar> </div> </template>
來看看效果
<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>
增長一個計算屬性, 當路由和當前跳轉路由的路徑一致時,處於選中狀態, 不然處於未選中狀態. 效果如圖:
如今, 咱們設置了當導航激活的時候, 文字顯示紅色, 可是...並非全部的導航激活的時候都是紅色, 因此,咱們須要將其動態設置. 也就是, 經過組件從調用方傳遞一個參數過來.以下所示:
<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">首頁</div> </tab-bar-item>
增長一個屬性activeStyle="blue", 對應的, 咱們須要在組件定義的位置增長一個prop屬性
props: { path: String, activeStyle: { type: String, default: 'red' } },
在prop屬性中增長 activeStyle的樣式, 而且設置了默認樣式red.
computed: { isActive(){ return this.$route.path.indexOf(this.path) !== -1 }, isActiveStyle() { return this.isActive ? {color: this.activeStyle}:{} } },
在計算屬性中增長 一個屬性isActiveStyle, 若是當前導航處於激活狀態, 則顯示樣式, 不然沒有任何樣式
來看看效果, 主要注意文字顏色 變化:
咱們來看看App.vue文件
<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">首頁</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">分類</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">購物車</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">個人</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>
在模板的部分, 內容特別多, 一般App.vue的內容是很簡潔的, 因此, 咱們還能夠將這部分組件進行抽象
將文件抽取到MainTabBar中, 抽取之後注意圖片文件以及vue組件的路徑
<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">首頁</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">分類</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">購物車</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">個人</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>
而後, 在App.vue中引入MainTabBar就能夠了
<template> <div id="app"> <router-view></router-view> <main-tab-bar></main-tab-bar> </div> </template>
效果和原來是同樣
當咱們的路徑不少的時候, 好比上面咱們抽象組件時候, 就發現, 文件位置換了, 不少路徑都要跟着變. 在vue中能夠設置路徑的別名, 這樣咱們就不用在更換了文件位置之後更換路徑了.
在build/webpack.base.conf.js文件中, 有一個resolve選項
resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@': resolve('src'), } },
resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@': resolve('src'), 'components': resolve('@/components'), 'assets': resolve('@/assets'), 'router': resolve('@/router'), 'views': resolve('@/views') } },
起別名的時候, 好比src/components, 路徑就可使用@/components.
後面使用到這個路徑的文件, 直接使用@/components就能夠了
在使用的時候, 也分爲幾種場景
咱們在App.vue中引入了MainTabBar
以前咱們引入腳本是這麼寫的:
import MainTabBar from "./components/tabBar/MainTabBar";
如今咱們配置了路徑別名之後, 能夠省去前面的./, 直接以components開頭
import MainTabBar from "components/tabBar/MainTabBar";
在style樣式引用中一樣有效
<style> @import "assets/main.css"; </style>
咱們直接將./省略.
好比咱們在MainTabBar.vue組件中設置導航圖標的時候, 有不少的src, 以前咱們都是這麼寫的
<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">個人</div>
在定義了路由別名之後, 咱們可使用以下寫法:
<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">首頁</div> </tab-bar-item>
也就是使用別名assets, 可是須要在前面加一個~符合.
到目前爲止, 我發如今路由中引入組件, 不能使用別名, 可是可使用@符號來表明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')
一旦使用別名, 就會報錯. 不管是到仍是不帶都不行. 還須要繼續探究
這也是一個問題
注意: 當咱們修改了配置文件webpack.base.conf.js之後, 要從新啓動服務才行. 不然不生效