天天看點

Vue學習 -- router路由直覺感受概念差別History模式hash模式

使用Vue開發項目,肯定對它的路由系統不會陌生。

學習router

  • 直覺感受
  • 概念
  • 差別
    • 優勢
    • 缺點
  • History模式
    • 建立vue-router-learn
      • 目錄結構
      • index.html
      • package.json
      • main.js
      • pages 存放頁面
      • 效果
    • 繼續改造
      • router.js
      • 建立my-router-link
      • 引用my-router-link
      • 效果
    • history.pushState
    • history.replaceState(擴充知識)
    • popstate 監聽
      • 改造後的main.js
    • history 重新整理404問題
  • hash模式
    • hash即URL中"#"字元後面的部分
    • 繼續改造
      • 修改my-router-link
      • 修改main.js

直覺感受

頁面切換沒有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>
           

效果

Vue學習 -- router路由直覺感受概念差別History模式hash模式

這是我們已經完成了一個簡易的頁面切換,當點選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頁,友善切換頁面時,有直覺差別
           

效果

Vue學習 -- router路由直覺感受概念差別History模式hash模式

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

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