深入淺出的webpack構建工具--webpack4+vue+router項目架構(十四)


閱讀目錄

一:vue-router是什么?

二:vue-router的實現原理

三:vue-router使用及代碼配置

四:理解vue設置路由導航的兩種方法。

五:理解動態路由和命名視圖

六:理解嵌套路由

七:在nginx上部署vue項目(history模式)

八:vue-router 之 keep-alive

一. vue-router是什么?

vue-router是vue.js官方的路由插件,它和vue.js是集成的。它用於構建單頁面應用,vue的單頁面應用是基於路由和組件的,路由是用於設定訪問路徑的,並且路徑和組件會產生映射起來。在vue-router單頁面應用中,路徑之間的切換,可以理解為組件的切換,路由模塊的本質:是建立起url和頁面之間的映射關系。

二:vue-router的實現原理

vue-router是用於構建單頁面的,單頁面的核心是更新視圖而不會重新請求頁面,這也就是為什么在頁面中不用a標簽的原因,因為頁面只有一個index.html。那么它在實現單頁面前端路由時,提供了兩種模式,hash模式和History模式。默認情況下是使用hash模式。

2.1 hash模式

hash是URL的錨點(#), 代表網頁中的一個位置,如果只改變#后的部分,瀏覽器會滾動到相應的位置,而不會重新加載網頁,並且每次改變#后的部分,都會在瀏覽器的訪問歷史中增加一條記錄,當我們使用后退按鈕的時候,就可以返回到上一個位置。因此hash模式通過錨點值的改變,根據不同的值,渲染指定DOM位置的不同數據。

2.2 history模式

上面是通過hash模式的,頁面的地址就會加上 # 號,比如我們的地址類似這樣的:http://localhost:8080/#/,我們使用history的話,那么訪問頁面的時候就和平常一樣,不帶井號的;如下地址也可以訪問 http://localhost:8080/ ,使用路由history模式的話,只需要在配置路由規則時,加入 'mode': 'history'即可,它是利用html5中的 history.pushState的API來完成的URL跳轉,無需重新加載頁面的。
不過這種配置需要后台開發支持一下的,具體怎么配置,下面會詳細講到。

三:vue-router使用及代碼配置

在vue-router中,定義了兩個標簽<router-link>和<router-view>來對應點擊和顯示,<router-link>是點擊的部分,<router-view>是定義顯示的部分,當我們點擊之后,路由匹配組件的內容后會在<router-view>顯示出來,<router-link>還需要一個屬性 to, 它的含義就是到哪里去的意思。
比如 <router-link to="/home">111</router-link> 的含義是當我們點擊111的時候,它會找到home相對應的組件,然后會在 <router-view>中顯示出來。

理解配置路由

路由一般使用兩個部分組成,path和component,path指路徑,component指的是組件,它是一個對象,比如 { path: '/home', component: home }

比如我們這邊定義兩條路由,如下代碼:

const routes = [
  { path: '/home', component: Home },
  { path: '/xxx', component: XXX }
];

2. 創建router管理
創建router對路由進行管理,它是由構造函數 new vueRouter()創建,接收上面的routes參數。如下代碼:

const router = new vueRouter({
  routes: routes
});

3. 實列注入到vue根實列中

我們接着把上面router實列注入待vue根實列中,我們就可以使用路由了。如下代碼:

new Vue({
  el: '#app',
  router: router,
  render: h => h(Index)
});

在配置代碼之前,我們還是看看我們的項目整個目錄結構如下:(基於第十三篇文章的基礎之上再進行構建的)。

### 目錄結構如下:
demo1                                       # 工程名
|   |--- dist                               # 打包后生成的目錄文件             
|   |--- node_modules                       # 所有的依賴包
|   |--- app
|   | |---index
|   | | |-- views                           # 存放所有vue頁面文件
|   | | |-- components                      # 存放vue公用的組件
|   | | |-- app.js                          # vue入口配置文件
|   | | |-- router.js                       # 路由配置文件
|   |--- views
|   | |-- index.html                        # html文件
|   |--- webpack.config.js                  # webpack配置文件 
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json
|   |--- .babelrc                           # babel轉碼文件

在app/index/views 新建home.vue 和 xxx.vue 文件。
1. home.vue 代碼如下:

<style lang='stylus'>
  .home-container
    width 100%
</style>
<template>
  <div class="home-container">
    <h1>歡迎來到Home</h1>
    <p>{{msg}}</p>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        msg: '我是Home組件'
      }
    }
  }
</script>

xxx.vue 代碼如下:

<style lang='stylus'>
  .xxx-container
    width 100%
</style>
<template>
  <div class="xxx-container">
    <h1>歡迎來到xxx</h1>
    <p>{{msg}}</p>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        msg: '我是XXX組件'
      }
    }
  }
</script>

2. 接着在 app/index/views 新建index.vue, 代碼如下:

<style lang="stylus">
  
</style>

<template>
  <div id="app">
    <header>
      <router-link to='/home'>Home</router-link>
      <router-link to='/xxx'>XXX</router-link>
    </header>
    <!-- 對應組件的內容渲染到router-view中 -->
    <router-view></router-view>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        
      }
    }
  }
</script>

3. 在 app/index/router.js定義router,路徑到組件的映射。 添加如下代碼:

import Vue from 'vue';
import VueRouter from 'vue-router';

// 引入組件 
import home from './views/home';

import xxx from './views/xxx';

// 告訴 vue 使用 vueRouter

Vue.use(VueRouter);

const routes = [
  {
    path: '/home',
    component: home
  },
  {
    path: '/xxx',
    component: xxx
  },
  {
    path: '*', // 其他沒有的頁面都重定向到 home頁面去
    redirect: '/home'
  }
]

var router = new VueRouter({
  routes: routes
});

export default router;

當然上面引入組件的方法,我們還可以懶加載的方式加載,懶加載引入的優點是:當我們訪問頁面的時候才會去加載相關的資源,這樣的話,能提高頁面的訪問速度。
比如如下這樣:component: resolve => require(['@/views/HelloWorld'], resolve) // 使用懶加載

因此我們可以把上面的代碼寫成如下:

import Vue from 'vue';
import VueRouter from 'vue-router';

/*
// 引入組件 
import home from './views/home';

import xxx from './views/xxx';

*/

// 告訴 vue 使用 vueRouter
Vue.use(VueRouter);

const routes = [
  {
    path: '/home',
    component: resolve => require(['./views/home'], resolve)  // 使用懶加載
  },
  {
    path: '/xxx',
    component: resolve => require(['./views/xxx'], resolve)  // 使用懶加載
  },
  {
    path: '*', // 其他沒有的頁面都重定向到 home頁面去
    redirect: '/home'
  }
]
var router = new VueRouter({
  base: '/app/index', // 配置單頁應用的基路徑
  routes: routes
});

export default router;

如上base的含義:應用的基路徑,比如整個單頁應用服務在 /app/index 下,那么base就應該設置為 /app/index.

4. 實列注入到vue根實列中。
把路由注入到根實列中,啟動路由,我們在app/index/app.js 入口文件添加如下代碼:

import Vue from 'vue';

import Index from './views/index';

// 引入路由
import router from './router';

new Vue({
  el: '#app',
  router: router,
  render: h => h(Index)
});

因此要運行上面代碼之前,我們先要 下載 vue-router 到我們項目中來,如下代碼命令:

npm i vue-router --save

下載完成后,我們再來運行打包命令,npm run dev 后,就可以啟動頁面了。

在頁面訪問如下

四:理解vue設置路由導航的兩種方法。

vue設置路由導航的兩種方法有 <router-link :to=''> 和 router.push().

4.1 to的值可以是一個字符串路徑,或者一個描述地址的對象。比如如下都是可以的。

// 字符串
<router-link to='/home'>Home</router-link>

// 對象
<router-link :to="{path: '/home'}">Home</router-link>

// 命名路由
<router-link :to="{name: 'home'}">Home</router-link>
/*
 如果是命名路由的話,需要在router.js 加上name字段,如下代碼:
 const routes = [
    {
      path: '/home',
      name: 'home',
      component: resolve => require(['./views/home'], resolve)  // 使用懶加載
    },
    {
      path: '/xxx',
      name: 'xxx',
      component: resolve => require(['./views/xxx'], resolve)  // 使用懶加載
    },
    {
      path: '*', // 其他沒有的頁面都重定向到 home頁面去
      redirect: '/home'
    }
  ]
*/

// 直接路由帶查詢參數query,地址欄就變成 /home?id=1
<router-link :to="{path: 'home', query: {id: 1 }}">Home</router-link>

// 如果是path的話,路由帶參數params的話,params是不會生效的。如下代碼 params的參數是不會在地址欄中顯示的。
<router-link :to="{path: 'home', params: {id: 1 }}">Home</router-link>

// 命名路由帶路由參數params,地址欄還是 /home, 但是可以通過 this.$route.params.id拿到值的
<router-link :to="{name: 'home', params: {id: 1 }}">Home</router-link>

4.2 router.push()方法

// 支持字符串
this.$router.push('xxx');

// 支持對象
this.$router.push({
  path: 'xxx'
});

// 支持命名路由方式
this.$router.push({
  name: 'xxx'
});

// 直接路由帶查詢參數 query, 地址欄變成 /xxx?id=1
this.$router.push({
  path: 'xxx',
  query: { id: 1 }
});

// 命名路由帶查詢參數query,地址欄變成 /xxx?id=1
this.$router.push({
  name: 'xxx',
  query: { id: 1 }
});

// 如果提供了path,直接路由帶參數params, params不會生效,在xxx頁面通過 
// console.log(this.$route.params.id) 是獲取不到值的。
this.$router.push({
  path: 'xxx',
  params: { id: 1 }
});

// 命名路由帶路由參數params,地址欄是/xxx。在xxx頁面通過 console.log(this.$route.params.id) 是可以獲取到值的。
this.$router.push({
  name: 'xxx',
  params: { id: 1 }
});

總結:1. 無論是直接路由path還是命名路由name,帶查詢參數query,地址欄會變成 /url?查詢參數名=查詢參數值

         2. 直接路由path帶路由參數params,params不生效。在xxx頁面通過 console.log(this.$route.params.id) 是獲取不到值的。

         3. 命名路由name帶路由參數params地址欄保持是 /url/路由參數值。在xxx頁面通過 console.log(this.$route.params.id) 是可以獲取到值的。

五:理解動態路由和命名視圖

5.1 動態路由
動態路由也可以叫做路由傳參。
在前面我們介紹了路由導航功能是不能傳遞參數的,我們可以叫他們靜態路由,而能傳遞參數的路由模式,並且對應的路由數量是不確定的,因此我們可以叫他動態路由。

應用場景:當我們的多個頁面或組件都要被多次重復利用的時候,我們的路由都指向同一個組件,這時候從不同的地方進入相同的組件的時候,我們根據傳遞的參數不同,來渲染不同的數據。

比如如下代碼:

const routes = [
  {
    path: '/xxx/:id',  // 動態路由信息
    name: 'xxx',
    component: resolve => require(['./views/xxx'], resolve)  // 使用懶加載
  }
];

如上代碼 path: /xxx/:id, 這種形式,這樣定義之后,vue-router將會匹配所有的 /xxx/1, /xxx/2,.....

比如我現在有多個頁面需要進入xxx組件內,這時候我們可以在配置路由路徑后加個:id。index.vue 改成如下代碼:

<template>
  <div id="app">
    <header>
      <router-link to="home">Home</router-link>
      <router-link to='xxx/on'>XXX</router-link>
    </header>
    <!-- 對應組件的內容渲染到router-view中 -->
    <router-view></router-view>
  </div>
</template>

當我點擊 XXX 時候,會進入xxx組件內,那么在xxx.vue組件內,可以通過 this.$route.params.id 獲取傳的參數值了。

如下圖所示:

5.2 理解命名視圖

有時候我們想同時顯示多個視圖,比如一個單頁應用頁面,有側導航(sidebar) 和 頭部(header) 和 main(主內容)等三個視圖,這個時候命名視圖就派上用場了。
我們可以在頁面中擁有多個單獨命名的視圖,而不是和我們上面一樣只有一個單獨的視圖。如果 router-view 沒有設置名字,那么默認為default。

現在我們可以在 app/index/components 新建文件夾為sidebar, header, main, 及在各對應的目錄下新建index.vue.

現在我們的項目的目錄結構變成如下了:

### 目錄結構如下:
demo1                                       # 工程名
|   |--- dist                               # 打包后生成的目錄文件             
|   |--- node_modules                       # 所有的依賴包
|   |--- app
|   | |---index
|   | | |-- views                           # 存放所有vue頁面文件
|   | | | |-- home.vue
|   | | | |-- index.vue
|   | | | |-- xxx.vue
|   | | |-- components                      # 存放vue公用的組件
|   | | | |--- header
|   | | | | |-- index.vue                   # 公用的頭部組件
|   | | | |--- main
|   | | | | |-- index.vue                   # 主體組件
|   | | | |--- sidebar
|   | | | | |-- index.vue                   # 側導航公用的組件
|   | | | |
|   | | |-- app.js                          # vue入口配置文件
|   | | |-- router.js                       # 路由配置文件
|   |--- views
|   | |-- index.html                        # html文件
|   |--- webpack.config.js                  # webpack配置文件 
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json
|   |--- .babelrc                           # babel轉碼文件

app/index/components/header/index.vue 代碼如下:

<style lang="stylus">
  * {margin: 0; padding: 0}
  .header-container
    width 100%
    height 50px
    background #000
    div 
      font-size 16px
      color #fff
      text-align center
</style>

<template>
  <div class="header-container">
    <div>我的頭部導航</div>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {

      }
    }
  }
</script>

app/index/components/sidebar/index.vue 代碼如下:

<style lang="stylus">
  .sidebar-container
    width 200px
    height 100%
    min-height 500px
    background red
    float left
    div 
      font-size 12px
      color #fff
      text-align center
</style>

<template>
  <div class="sidebar-container">
    <div>我的左側導航</div>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {

      }
    }
  }
</script>

app/index/components/main/index.vue 代碼如下:

<style lang="stylus">
  .main-container
    margin-left 200px
    height 100%
    min-height 500px
    background green
    div 
      font-size 12px
      color #fff
      text-align center
</style>

<template>
  <div class="main-container">
    <div>我的主體部分</div>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {

      }
    }
  }
</script>

框架搭建完成后,我們需要在index.vue 代碼變成如下:

<style lang="stylus">
  
</style>

<template>
  <div id="app">
    <!--
    <header>
      <router-link to="/home">Home</router-link>
      <router-link to='/xxx/on'>XXX</router-link>
    </header>
  -->
    <router-view name="Header"></router-view>
    <router-view name="Sidebar"></router-view>
    <router-view name="Main"></router-view>
    <!-- 對應組件的內容渲染到router-view中 -->
    <router-view></router-view>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        
      }
    }
  }
</script>

接着我們需要在router.js下配置代碼如下:

import Vue from 'vue';
import VueRouter from 'vue-router';

// 引入組件 
import home from './views/home';

import xxx from './views/xxx';

// 告訴 vue 使用 vueRouter
Vue.use(VueRouter);

const routes = [
  {
    path: '/home',
    name: 'home',
    components: {
      default: resolve => require(['./views/home'], resolve),  // 使用懶加載
      Header: resolve => require(['./components/header/index'], resolve),  // 使用懶加載
      Sidebar: resolve => require(['./components/sidebar/index'], resolve),  // 使用懶加載
      Main: resolve => require(['./components/main/index'], resolve)  // 使用懶加載
    }
  },
  {
    path: '/xxx/:id',
    name: 'xxx',
    components: {
      default: resolve => require(['./views/xxx'], resolve),  // 使用懶加載
      Header: resolve => require(['./components/header/index'], resolve),  // 使用懶加載
      Sidebar: resolve => require(['./components/sidebar/index'], resolve),  // 使用懶加載
      Main: resolve => require(['./components/main/index'], resolve)  // 使用懶加載
    } 
  },
  {
    path: '*', // 其他沒有的頁面都重定向到 home頁面去
    redirect: '/home'
  }
]

var router = new VueRouter({
  base: '/app/index', // 配置單頁應用的基路徑
  routes: routes
});

export default router;

然后打包運行下,在瀏覽器上可以看到如下圖所示的結構。

如上可以看到,我們可以使用 命名視圖 來搭建我們網站的框架,這可以理解命名視圖的作用了。

六:理解嵌套路由

什么是嵌套路由?比如我們進入到我們home頁面的時候,它下面還有分類,比如叫 java書籍,node書籍等,那么當我點擊各個類目的時候,就對應到相應的組件。比如首先我進入的是 /home 這個路由,然后當我進入 /home下面的分類的 java書籍的話,那么我們的路由就是 /home/java, 如果進入分類的是 node書籍的話,那么路由就是 /home/node 了。因此 vue提供了 childrens這個屬性來做這件事,它也是一組路由。

首先我們在 app/index/views 下 新建 java.vue 和 node.vue.

java.vue代碼如下:

<style lang='stylus'>
  .java-container
    width 100%
</style>

<template>
  <div class="java-container">
    <h1>歡迎來到java類書籍</h1>
    <p>{{msg}}</p>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        msg: '我是java組件'
      }
    },
    methods: {
      
    }
  }
</script>

node.vue 代碼如下:

<style lang='stylus'>
  .node-container
    width 100%
</style>

<template>
  <div class="node-container">
    <h1>歡迎來到node類書籍</h1>
    <p>{{msg}}</p>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        msg: '我是node組件'
      }
    },
    methods: {
      
    }
  }
</script>

app/index/router.js 代碼就改成如下:

import Vue from 'vue';
import VueRouter from 'vue-router';

// 引入組件 
import home from './views/home';

import xxx from './views/xxx';

// 告訴 vue 使用 vueRouter
Vue.use(VueRouter);

const routes = [
  {
    path: '/home',
    name: 'home',
    component: resolve => require(['./views/home'], resolve),
    // 子路由
    children: [
      {
        path: 'java',
        component: resolve => require(['./views/java'], resolve)
      },
      {
        path: 'node',
        component: resolve => require(['./views/node'], resolve)
      }
    ]
  },
  {
    path: '/xxx/:id',
    name: 'xxx',
    component: resolve => require(['./views/xxx'], resolve)
  },
  {
    path: '*', // 其他沒有的頁面都重定向到 home頁面去
    redirect: '/home'
  }
]

var router = new VueRouter({
  base: '/app/index', // 配置單頁應用的基路徑
  routes: routes
});

export default router;

app/index/views/index.vue 代碼如下:

<style lang="stylus">
  
</style>

<template>
  <div id="app">
    <header>
      <router-link to="/home" tag='li'>Home</router-link>
      <router-link to="/home/java">java</router-link>
      <router-link to="/home/node">node</router-link>
      <router-link to='/xxx/on'>XXX</router-link>
    </header>
  <!--
    <router-view name="Header"></router-view>
    <router-view name="Sidebar"></router-view>
    <router-view name="Main"></router-view>
  -->
    <!-- 對應組件的內容渲染到router-view中 -->
    <router-view></router-view>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        
      }
    }
  }
</script>

如上代碼增加完成后,進行打包,編譯都正常,但是點擊java或node的時候,子路由一直沒有渲染到對應的組件,網上很多資料說,子路由多寫了 /, 但是我router.js里面也沒有這些東西的,但是經過搜索,發現問題並不是在這里,而是說在嵌套的子模塊里面引用子路由的時候,也需要加上 <router-view></router-view> 這句代碼,如下是home.vue 代碼:

<template>
  <div class="home-container">
    <h1>歡迎來到Home</h1>
    <p>{{msg}}</p>
    <p @click="func">進入index頁面</p>
    <router-view></router-view>
  </div>
</template>

注意:路由嵌套的真正的含義是:點擊子路由的時候,父級組件還是會顯示的,子組件也會顯示的,這才是使用子路由嵌套的用途。
如果僅僅想顯示子組件的話,不想顯示父組件的話,那就不要使用路由嵌套,直接使用兄弟路由,寫死路徑即可。

比如

const router = [
  {
    path: '/home',
    name: 'home',
    component: resolve => require(['./views/home'], resolve),
  },
  // 兄弟路由
  {
    path: '/home/java',
    name: 'home',
    component: resolve => require(['./views/java'], resolve),
  }
]

 github源碼 webpack+vue+router


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM