1. 程式人生 > 其它 >Vue學習 -- router路由

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)兩個屬性

具體效果就不截圖了,和上邊實現的效果一樣。