路由是什麼?爲什麼要使用路由?路由有什麼功能?可以寫出路由跳轉的源碼嗎?
寫完路由部分,四連打擊讓我深思,一起來學習一下vue的翅膀Vue-Router吧~
這是一個基於 vue & axios & mock & token & stylus & Cube-UI的電商項目demo,面向 vue 初學者,場景雖簡單,但五臟俱全。涵蓋非常多的vue及其相關技術的基本操作。有詳細的註釋,幫助大家快速上手 vue 。
Github地址
- vue-jingdong (不要吝惜你的小星星哦)
- 話不多說上效果圖
正文部分
作爲VUE的初學者,創建一個項目後的demo裏面就有home頁面和about頁面的跳轉。在寫這個小項目的時候,我也是照葫蘆畫瓢沒有過多的思考。寫完路由部分的時候,反過來思考,心中也有許多疑問。路由是什麼?爲什麼要使用路由?路由有什麼功能?可以寫出路由跳轉的源碼嗎?
帶着這些問題我查閱了很多資料和文章,發現它們都比較散,往往只講述一個小點。如果你也有相同的問題就跟隨我來逐一擊破吧~
路由是什麼?
- 路由:即瀏覽器中的哈希值(# hash)與展示視圖內容(template)之間的對應規則。
在計算機網絡原理中,路由指的是根據上一接口的數據包中的IP地址,查詢路由錶轉發到另一個接口,它是決定一個端到端的網絡路徑。所以說路由就是用來解析URL以及調用對應的控制器的。
- vue中的路由是:hash 和component的對應關係。Vue路由即vue-router,在web開發中,“router”是指根據url分配到對應的處理程序。
通俗的來說,在web開發中,路由就是一套映射規則(一對一的對應規則),由開發人員制定規則。
當URL中的哈希值(# hash)發生改變後,路由會根據制定好的規則,展示對應的視圖內容。
爲什麼要使用路由?
在傳統的web開發中每一個請求地址都會請求服務器來進行處理,但是用戶有些操作則無需請求服務器,直接頁面端修改下邏輯就能達到目的,在這種方式下最好的方法是使用路由,因爲使用路由時,URL會隨着改變,用戶瀏覽一個網頁時可以直接複製或收藏當前頁面的URL給別人,這種方式對於搜索引擎和用戶來說都是友好的。
在 Web app 中,通過一個頁面來展示和管理整個應用的功能。
SPA(單頁應用程序)往往是功能複雜的應用,爲了有效管理所有視圖內容,前端路由 應運而生!
路由有什麼功能?
這個問題,我是通過Vue Router開發者文檔,進行了解的,但是我看完之後還是有點懵懵懂懂,沒有特別理解,之後我是通過查閱一些文章對路由的功能有了更深入的瞭解,下面跟隨作者一起結合項目來看看吧~
基本使用
安裝:npm i -S vue-router或者創建vue項目時就選擇了router
<!-- app.vue -->
<template>
<div id="app">
<!-- 5 路由入口 指定跳轉到只定入口 -->
<div id="nav">
<router-link to="/login">登陸</router-link> |
<router-link to="/register">註冊</router-link>
</div>
<!-- 7 路由出口:用來展示匹配路由視圖內容 -->
<router-view/>
</div>
</template>
// 3 創建兩個組件 作者就不寫代碼啦vue項目一開始的demo有創建。
//router中的index.js
import Register from '../views/Register.vue'
import Login from '../views/Login.vue'
Vue.use(VueRouter)
// 4 創建路由對象
const routes = [
{
path: '/',
name: 'login',
redirect: '/login'
},
// 路徑和組件一一對應
{
path: '/register',
name: 'register',
component: Register
},
{
path: '/login',
name: 'login',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
//項目路由懶加載,秒開
component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue')
},
]
<!-- main.js -->
// 1 導入 vue.js
import Vue from 'vue'
import './cube-ui'
import App from './App.vue'
// 2 導入 路由文件
import router from './router'
import store from './store'
import axios from 'axios'
import 'amfe-flexible'
import steaxios from './setaxios'
steaxios();
Vue.config.productionTip = false
Vue.prototype.$http=axios//掛載axios
new Vue({
router,
store,
// 6 將路由實例掛載到vue實例
render: h => h(App)
}).$mount('#app')
通過代碼註釋中步驟,就完成路由基本功能的實現啦!
重定向
在這個小項目中,我是直接使用重定向,讓頁面一開始在根路徑的時候就跳轉到登陸頁面。
{
// 將path 重定向到 redirect
path: '/',
name: 'login',
redirect: '/login'
},
重定向還有兩種方式
- 重定向的目標可以是一個命名的路由:
{ path: '/', redirect: { name: 'login' }}
- 重定向的目標可以是一個方法,動態返回重定向目標:
{ path: '/', redirect: to => {
// 方法接收 目標路由 作爲參數
// return 重定向的 字符串路徑/路徑對象
}}
注:導航守衛並沒有應用在跳轉路由上,而僅僅應用在其目標上。
懶加載(延遲加載)
- 定義:懶加載也叫延遲加載,即在需要的時候進行加載,隨用隨載。
- 作用:路由懶加載可以幫我們在進入首屏時不用加載過度的資源,從而減少首屏加載速度。也就是按需加載。使用第三方組件庫依賴過大,會給首屏加載帶來很大的壓力,一般解決方式是按需求引入組件。如果引用組件的組件,也是跳轉到相應路由,再進行加載需要使用的組件,可以提高性能。
- 實現懶加載的三種方式:
一、Vue異步組件技術:
{
path: '/home',
name: 'Home',
component: resolve => reqire(['path路徑'], resolve)
}
二、es6提案的import()
const Home = () => import('path路徑')
//本項目使用的方式
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue')
},
三、webpack提供的require.ensure()
{
path: '/home',
name: 'Home',
component: r => require.ensure([],() => r(require('path路徑')), 'demo')
}
總結來自:
作者:小夢喲
鏈接:https://juejin.im/post/5e4c0b856fb9a07ccb7e8eca
來源:掘金
可以手寫懶加載源碼嗎?
這個問題我也想了一下,實現懶加載肯定是要直接操作DOM。作爲前端小白的我能力有限,暫時無法完成,但是找到了一篇文章可以供大家參考。
懶加載的弊端
所有的東西都是一把雙刃劍,有利也有弊,只能按需使用,切不可過度使用。
大項目中的路由懶加載不適用於開發環境(會導致保存代碼時熱更新代碼時間很長20s左右)
解決辦法
在 router/ 文件夾中加入2個文件夾:
_import_development.js
module.exports = file => require('@/' + file + '.vue').default
_import_production.js
module.exports = file => resolve => require(['@/' + file + '.vue'], resolve)
index.js
const IMPORT = require('./_import_' + process.env.NODE_ENV + '.js');
組件引入時寫入:
component: IMPORT('pages/mainPage') // 組件
嵌套子路由
在我的小項目中也使用了嵌套路由。嵌套路由顧名思義,路由中又包含子路由。例如:
/botnav/index /botnav/list
+------------------+ +-----------------+
| botnav | | botnav |
| +--------------+ | | +-------------+ |
| | index | | +------------> | | list | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
想要在botnav頁面中,切換index,list等多個頁面時,嵌套路由就派上用場啦,它的書寫規則如下:
注:childern子路由path一定不要加上‘\’,寫代碼時,寫路由路徑寫習慣了,很容易犯這個小錯誤
{
path: '/botnav',
name: 'botnav',
component: () => import('../views/Botnav.vue'),
children:[
{
path: 'index',
name: 'index',
component: () => import('../views/index.vue')
},
{
path: 'list',
name: 'list',
component: () => import('../views/List.vue')
},
{
path: 'search',
name: 'search',
component: () => import('../views/Search.vue')
},
{
path: 'cart',
name: 'cart',
component: () => import('../views/Cart.vue')
},
{
path: 'mine',
name: 'mine',
component: () => import('../views/Mine.vue')
},
]
}
實現效果:
這幾個botnav就可以在同一個頁面切換啦。
過渡動效
在寫項目時,爲了增加用戶體驗感,加上路由切換時的過渡動效是必不可少的,下面先看一下過度動效的效果展示:
是基本的動態組件,所以我們可以用 組件給它添加一些過渡效果:
<template>
<div>
<transition :name="transitionName">
<router-view class="Router"></router-view>
</transition>
<cube-tab-bar
v-model="selectedLabelDefault"
:data="tabs"
@click="clickHandler"
@change="changeHandler"
class="botnav">
</cube-tab-bar>
</div>
</template>
<script>
export default {
data () {
return {
transitionName:'slide-right',
selectedLabelDefault: '首頁',
tabs: [{
label: '首頁',
icon: 'cubeic-home'
}, {
label: '分類',
icon: 'cubeic-tag'
}, {
label: '搜索',
icon: 'cubeic-search'
}, {
label: '購物車',
icon: 'cubeic-mall'
}, {
label: '我的',
icon: 'cubeic-person'
}]
}
},
methods: {
clickHandler (label) {
// if you clicked home tab, then print 'Home'
console.log(label)
},
//點擊與自身不同的其他導航
changeHandler (label) {
// if you clicked different tab, this methods can be emitted
switch(label){
case '首頁':
this.$router.push('/botnav/index');
break;
case '分類':
this.$router.push({path:'/botnav/list'});
break;
case '搜索':
this.$router.push({path:'/botnav/search'});
break;
case '購物車':
this.$router.push({path:'/botnav/cart'});
break;
case '我的':
this.$router.push({path:'/botnav/mine'});
break;
}
}
}
}
</script>
<style lang="stylus" scoped>
.cube-tab-bar.botnav
position fixed
bottom 0
left 0
z-index 1000
width 100%
background #fff
font-size 25px
.cube-tab div
font-size 16px
padding-top 3px
i
font-size 20px
<!--以下代碼實現過渡效果-->
.Router
position absolute
width 100%
transition all 0.8s ease
.silde-left-ender,.slide-right-leave-active
opacity 0
-webkit-transform translate(100%,0)
transform translate(100%,0)
.slide-left-leave-active,.silde-right-enter
opacity 0
-webkit-transform translate(-100%,0)
transform translate(-100%,0)
</style>
以上代碼建立了一個botnav,用包裹,並給transition起了一個名字,利用css中的leave和enter來完成頁面切換,在裏面寫上要實現的效果。這裏我引用VUE.js中的一張圖片來直觀的解釋:
本項目中用silde-right替換了v-。
路由還有其他很多功能,我就不一一介紹啦,大家有興趣可以去看看官方文檔。下面我們來解答最後一個疑問,?可以寫出路由跳轉的源碼嗎?
可以寫出路由跳轉的源碼嗎?
想要知道路由的源碼怎麼寫?一定要知道路由是怎麼實現的。我淺顯的理解是,對於路由的hush模式,我們切換一個頁面,就是對一個視圖的展示。需要對這個視圖進行展示,一定想要獲取這個頁面的huas值,然後去對這個視圖進行註冊,然後進行展示。這樣利用路由切換頁面就成功啦。我不知道我的理解是否正確,希望大佬指出。下面我對我的思路進行源碼的書寫,先放上效果圖。
我們先寫三個鏈接:
<div id="nav">
<a href="#/page1">page1</a>
<a href="#/page2">page2</a>
<a href="#/page3">page3</a>
</div>
<div id="container"></div>
然後我們定義一個hashRouter方法,並實現進入首頁時,返回空。
class HashRouter {
//初始化時執行
registerIndex(cb = () => {}) {
this.routers.index = cb;
}
}
接下來我們進行獲取hash和對頁面視圖進行註冊:
class HashRouter {
constructor() {
this.routers = {}; //配置, 是設計關鍵點 routes
//存儲路由配置的 hash key val Component
window.addEventListener('hashchange', () => {
let hash = window.location.hash.slice(1),//獲取頁面hash
handler;
if(!hash) {
handler = this.routers.index;
} else {
handler = this.routers[hash];
}
handler.call(this);//將hash拋出去
})
}
// hahschange 後執行哪個函數
// vue 組件 函數式組件 callback
register(hash, callback){
this.routers[hash] = callback;//進行視圖註冊
}
//初始化時執行
registerIndex(cb = () => {}) {
this.routers.index = cb;
}
}
最後我們將註冊後的視圖,插入到id=“container”,讓頁面顯示
let router = new HashRouter(); //路由對象, 類型爲hash
let container =
document.getElementById('container'); // 根組件
router.registerIndex(() => container.innerHTML = '首頁')
router.register('/page1', function ()
{
console.log(this.routers);
container.innerHTML = '/page1';
}) // 路由 對應執行事 //.vue template部分
router.register('/page2', () => container.innerHTML = '/page2');
router.register('/page3', () => container.innerHTML = '/page3');
路由簡單實現就完成啦,如有錯誤還望大佬指出。
寫在後面
引用小金魚的一句話,當然紙上學來終覺淺,絕知此事要躬行。學完理論就需要去實踐,大家看完之後一定要自己去操作一下,不然似懂非懂,第二天就忘啦。在此作者再次附上GitHub的地址vue-jingdong (不要吝惜你的小星星哦)。這是一個系列文章,未完待續,期待與你一起學習。如果對你有幫助的話,不妨送上你的star,送人玫瑰,手留餘香,謝謝!