Vue學習 -- router路由
技術標籤:vuerouterhistorypushStatehash
使用Vue開發專案,肯定對它的路由系統不會陌生。
學習router
直觀感受
頁面切換沒有A標籤那種明顯的跳轉以及新頁面開啟時的重新整理等
概念
就是隻有一個Web頁面的應用,只加載單個HTML頁面。在使用者與應用程式互動時,動態更新該頁面的Web應用程式。我們稱之為SPA(單頁應用程式)
區別
1、傳統多頁面程式:每次請求伺服器返回的都是一個完整的頁面
2、 單頁應用程式:只有第一次會載入頁面, 以後的每次請求, 僅僅是獲取必要的資料.然後, 由頁面中js解析獲取的資料, 展示在頁面中
優勢
1 減少了請求體積,加快頁面響應速度,降低了對伺服器的壓力
2 更好的使用者體驗,讓使用者在web app感受native app般的流暢
缺點
因為技術使用了ajax,導致頁面不利於SEO,但是可以通過其他技術去規避
(SEO原則:搜尋引擎的蜘蛛只識別href的一般超連結,而不識別JavaScript程式碼,遇到一般超連結就會爬進去,遇到JavaScript不會爬進去。即,搜尋引擎抓不到AJAX動態載入的內容。)
History模式
HTML5 History API提供了一種功能,能讓開發人員在不重新整理整個頁面的情況下修改站點的URL,就是利用 history.pushState API 來完成 URL 跳轉而無須重新載入頁面;
新建vue-router-learn
自取的demo名稱,具體demo地址
目錄結構
vue-router-learn
├── README.md
├── .gitignore
├── node_modules
├── index.html
├── package.json
├── src
│ ├── main.js
│ └── pages
└── webpack.config.js
想知道這樣的目錄樹如何自動生成,戳這裡 安裝tree
index.html
<body>
<div id="app">
{{ message }}
</div>
<!-- 注意這裡的 script地址 -->
<script src="/dist/build.js"></script>
</body>
大家可能好奇,剛才的根目錄上也沒有 dist 檔案啊?不要著急往下看
package.json
{
"name": "vue-router-learn",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --hot --open",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
"predeploy": "npm run build"
},
"dependencies": {
"vue": "^2.3.3"
},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-loader": "^7.0.0",
"babel-preset-env": "^1.6.1",
"cross-env": "^4.0.0",
"css-loader": "^0.28.1",
"file-loader": "^0.11.1",
"surge": "^0.19.0",
"vue-loader": "^12.0.3",
"vue-template-compiler": "^2.3.3",
"webpack": "^2.5.1",
"webpack-dev-server": "^2.4.5"
}
}
//下載所有依賴
cnpm i
//啟動專案執行
npm run dev
這裡會用到webpack-dev-server ,其中有這麼一句解釋:
It uses webpack-dev-middleware under the hood, which provides fast in-memory access to the webpack assets.
main.js
import Vue from 'vue'
const routes = {
'/': 'Home',
'/about': 'About'
}
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
currentRoute: window.location.pathname
},
computed: {
ViewComponent () {
const matchingView = routes[this.currentRoute];
return matchingView
? require('./pages/' + matchingView + '.vue')
: require('./pages/404.vue')
}
},
render (h) { return h(this.ViewComponent) }
})
其中如果不借助 webpack 直接使用import會報錯:
Cannot use import statement outside a module
因為import 是es6的語法,而瀏覽器並不支援es6語法,當然你也可以在script 上新增type=“model”,或者頻繁的使用script
<script src="./node_modules/vue/dist/vue.js" ></script>
<script src="./src/main.js" ></script>
但顯然不是我們想要的。所以還是藉助webpack比較好
pages 存放頁面
目前有三個頁面 404、home、about
//404頁
<template>
<h1>這是404頁</h1>
</template>
//home 頁
<template>
<section>
<nav>
<a href="/">home頁</a>
<a href="/about">about頁</a>
</nav>
<h1>這是home頁</h1>
</section>
</template>
//about 頁
<template>
<section>
<nav>
<a href="/">home頁</a>
<a href="/about">about頁</a>
</nav>
<h1>這是about頁</h1>
</section>
</template>
效果
這是我們已經完成了一個簡易的頁面切換,當點選home/about 按鈕時,URL會發生改變,然後通過window.location.pathname獲得對應的router,最後通過render展示對應的元件,不太瞭解main.js寫法的可以戳這裡–看template部分
繼續改造
但是他照樣是通過A標籤跳轉的,並且每次都會重新整理頁面,所以咱們還需要藉助 history.pushState API
router.js
export default {
'/': 'Home',
'/about': 'About'
}
//main.js
//頂部加一行 import 引入 router檔案
//保持不變
import routes from './router'
新建my-router-link
仿照router路由,新建一個my-router-link元件
<template>
<a
:href="to"
:class="[{ active: isActive },'a']"
@click="go"
>
<slot></slot>
</a>
</template>
<script>
import routes from '../router'
export default {
props: {
to: {
type:String,
default:''
}
},
computed: {
isActive () {
return this.to === this.$root.currentRoute
}
},
methods: {
go (event) {
event.preventDefault()
this.$root.currentRoute = this.to
window.history.pushState(
null,
routes[this.to],
this.to
)
}
}
}
</script>
<style scoped>
.a{
color: #333;
}
.active {
color: cornflowerblue;
}
</style>
引用my-router-link
// home頁面
<template>
<section class="about-wrap">
<nav>
<my-router-link to="/">home頁</my-router-link>
<my-router-link to="/about">about頁</my-router-link>
</nav>
<h1>這是home頁</h1>
</section>
</template>
<script>
import MyRouterLink from '../components/my-router-link.vue'
export default {
components:{
MyRouterLink
}
}
</script>
//about 頁面同上
//只是把h1標籤內容替換成 這是about頁,方便切換頁面時,有直觀區別
效果
可惜csdn 沒辦法新增動效,所以看著和上一張效果圖一樣,其實是有區別大,我在這裡先解釋一下,感興趣的小夥伴自己執行一下就知道了。
區別 這時點選tab按鈕,URL改變,頁面切換,但是不會像以前一樣重新整理了。
history.pushState
每執行一次都會增加一條歷史記錄,一共接收3個引數
history.pushState(data,title,url)
-
data:要設定的history.state的值,可以是任意型別的值,可根據此值進行判斷執行想要的操作。
-
title:現在大多數瀏覽器不支援或者忽略這個引數,最好用null代替。
-
url:位址列的值,若不需要可用空來代替。
history.replaceState(擴充套件知識)
replaceState()是用來修改當前的歷史記錄(history實體),而不是建立一個新的歷史記錄,所以當執行完history.replaceState()後,點選返回按鈕照樣會返回上一個一面。
當需要更新一個state物件或者當前history實體時,可以用replaceState()來實現。
popstate 監聽
其實到這裡新增路由已經差不多了,但是點選瀏覽器返回時,會發現頁面沒有反應,這裡還需要對路由進行一下監聽
改造後的main.js
//main.js
import Vue from 'vue'
import routes from './router'
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
currentRoute: window.location.pathname
},
computed: {
ViewComponent () {
const matchingView = routes[this.currentRoute];
return matchingView
? require('./pages/' + matchingView + '.vue')
: require('./pages/404.vue')
}
},
render (h) { return h(this.ViewComponent) }
})
window.addEventListener('popstate', () => {
//當點選瀏覽器返回按鈕時,更新currentRoute,改變引用的pages頁面
app.currentRoute = window.location.pathname;
})
history 重新整理404問題
因為重新整理是實實在在地去請求伺服器的,history模式最終的路由都體現在url的pathname中,這部分是會傳到伺服器端的,因此需要服務端對每一個可能的path值都作相應的對映
// -e filename 如果 filename存在,則為真
location /{
root /**/**/檔案目錄;
index index.html index.htm;
if (!-e $filename) {
rewrite ^/(.*) /index.html last;
break;
}
}
hash模式
hash即URL中"#"字元後面的部分
1、使用瀏覽器訪問網頁時,如果網頁URL中帶有hash,頁面就會定位到id(或name)與hash值一樣的元素的位置;
2、hash還有另一個特點,它的改變不會導致頁面重新載入;
3、hash值瀏覽器是不會隨請求傳送到伺服器端的;
4、通過window.location.hash屬性獲取和設定hash值。
window.location.hash值的變化會直接反應到瀏覽器位址列(#後面的部分會發生變化),同時,瀏覽器位址列hash值的變化也會觸發window.location.hash值的變化,從而觸發onhashchange事件。
繼續改造
hash和history 主要修改路由部分,其他檔案都可以保持不變,因此咱們複用上邊的專案,加以修改
修改my-router-link
//只需要修改 go 方法,將原有 History 部分註釋掉
methods: {
go (event) {
event.preventDefault()
this.$root.currentRoute = this.to
//History 模式
// window.history.pushState(
// null,
// routes[this.to],
// this.to
// )
//Hash模式
window.location.hash = this.to
}
}
修改main.js
//註釋掉History部分,新增hashchange 方法
//History 模式
// window.addEventListener('popstate', () => {
// app.currentRoute = window.location.pathname;
// })
//Hash模式
window.addEventListener('hashchange',(e) =>{
let pathname = e.newURL.split('/#')[1]
app.currentRoute = pathname;
},false);
1、當URL的片段識別符號更改時,將觸發hashchange事件(跟在#符號後面的URL部分,包括#符號)
2、hashchange事件觸發時,事件物件會有hash改變前的URL(oldURL)和hash改變後的URL(newURL)兩個屬性
具體效果就不截圖了,和上邊實現的效果一樣。