大白話理解和初步使用vue-router

TL;DR

  • router其實就是讓路徑和組件一一對應
  • 即使不使用vue-router也能夠實現跳轉,但老是手動處理歷史記錄
  • vue-router的使用好處:歷史記錄、參數處理、路由鉤子、權限控制、記錄滾動條位置
  • vue-router指南vue-router的API
  • 坑:歷史模式懶加載的時候注意名字、子路由通常不用/

router究竟是什麼

router,路由,我理解的其實就是,讓不一樣的路徑對應的不一樣的視圖。在vue裏,其實視圖就至關於組件。也就是讓不一樣的路徑對應不一樣的組件。換言之,router就是規定了路徑和組件一一對應。這句多是理解vue-touter的核心。javascript

先說說render,爲下個標題鋪墊

vue官網對render解釋的很清楚,這邊我簡單的說下。 通常咱們寫vue組件的時候,會寫template,但任何template均可以用render函數代替,事實上,vue就是把template轉換爲render函數的,之因此咱們用template,是由於其易讀易寫。html

怎麼用render函數替換template呢?
簡單的舉個例子,你的template是<h1 :title="blogTitle">{{ blogTitle }}</h1>,對應的render函數以下vue

render: function (createElement) {
  // `createElement`的參數通常有三個,第一個必填就是標籤名或者組件名,第二個是這個標籤的屬性,第三個就是子節點。
  return createElement('h1', {attrs:{title:this.blogTitle}}, [this.blogTitle])
}
複製代碼

createElement其實更準確的名字是createNodeDescription,由於它所包含的信息會告訴 Vue 頁面上須要渲染什麼樣的節點,包括及其子節點的描述信息。也就是「虛擬節點 (virtual node)」,簡寫爲「VNode」。「虛擬 DOM」是對由 Vue 組件樹創建起來的整個 VNode 樹的稱呼。java

即使不用vue-router,也能夠實現單頁面跳轉的

開始總覺得,要想不一樣路徑就必需要vue-router,其實沒必要。vue官方文檔有個很好的demo,這邊挪用下邏輯。若是你只是須要簡單的路由,也是能夠直接這麼用的。node

// <div id="app"><div>
const Home = { template: "<p>home page</p>" };
const About = { template: "<p>about page</p>" };
const NotFound = { template: "<p>Page not found</p>" };
// 路由 路徑和組件一一對應
const routes = {'/':Home,'about':About}
var app = new Vue({
el: "#app",
data: {
    // 拿到地址欄路徑
    currentRoute: window.location.pathname
},
computed: {
    // 由routes獲得路徑對應的組件
    ViewComponent() {
      return routes[this.currentRoute] || NotFound;
    }
},
// 這邊用render了
render(h) {
    return h(this.ViewComponent);
}
});
// 歷史記錄的處理
window.addEventListener('popstate', () => {
  app.currentRoute = window.location.pathname
})
複製代碼

用vue-router有什麼好處

  • 本身寫上面一套繁瑣哇
  • 不一樣路徑很輕易對應不一樣組件,並且人家處理好了歷史記錄
  • 輕易解決傳參數問題
  • 有路由鉤子,控制什麼狀況去什麼路徑,好比不登陸的話就去登陸頁面
  • 甚至能夠記住滾動條的位置

官網就是很好的學習路徑

vue-router官網,如下我簡單的總結下。代碼示例統一在末尾。webpack

怎麼根據路徑顯示不一樣組件的的

  • vue拿到 地址欄的路徑(如/user/user-list
  • => 找routes 那邊的配置,從上到下找 (如path:'/user'),拿到components,(如找到{default:Foo,a:Bar}
  • => 由於/user就是第一級路徑,因此直接將app.vue裏面的<router-view/>替換成Foo組件<router-view name="a"/>替換成Bar組件
  • => 再繼續,找path:'/user'下面的children,發現目標path:'user-list',拿到components,找到{default:Second,a:Zoo}
  • => 由於user-list是user的children,因此將user.vue裏面的<router-view/>替換成Second組件<router-view name="a"/>替換成Zoo組件
  • 找不到的話會報錯,固然通常都會配置404

示例代碼

<!-- 示例1 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 組件來導航. -->
    <!-- 經過傳入 `to` 屬性指定連接. -->
    <!-- <router-link> 默認會被渲染成一個 `<a>` 標籤 -->
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的組件將渲染在這裏 -->
  <router-view></router-view>
</div>
<script> // 0. 若是使用模塊化機制編程,導入Vue和VueRouter,要調用 Vue.use(VueRouter) // 1. 定義 (路由) 組件。也就是視圖!!!! // 能夠從其餘文件 import 進來 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } // 2. 定義路由。也就是什麼路徑顯示什麼視圖(組件)!!! // 每一個路由應該映射一個組件。 其中"component" 能夠是 // 經過 Vue.extend() 建立的組件構造器, // 或者,只是一個組件配置對象。 // 咱們晚點再討論嵌套路由。 const routes = [ { path: '/foo', component: Foo }, // 懶加載模式,會在合適的時機加載 bar.js,webpackChunkName就是將此組件的代碼命名爲bar.js 看控制檯的network { path: '/bar', component: () => import(/* webpackChunkName: "bar" */ './views/Bar.vue'), }, // 注意 404 { path: '*', component: () => import(/* webpackChunkName: "404" */ './views/404.vue'), }, ] ] // 3. 建立 router 實例,而後傳 `routes` 配置。 // 你還能夠傳別的配置參數, 不過先這麼簡單着吧。 const router = new VueRouter({ routes // (縮寫) 至關於 routes: routes }) // 4. 建立和掛載根實例。!!!掛載在根實例下,也就是任何組件內均可以經過this.$router知道內容 // 記得要經過 router 配置參數注入路由, // 從而讓整個應用都有路由功能 const app = new Vue({ router }).$mount('#app') // 如今,應用已經啓動了! </script>
複製代碼
// 示例1 js
// Home.vue
export default {
  computed: {
    username () {
      // 咱們很快就會看到 `params` 是什麼
      return this.$route.params.username
    }
  },
  methods: {
    goBack () {
      window.history.length > 1
        ? this.$router.go(-1)
        : this.$router.push('/')
    }
  }
}
複製代碼
/* 示例2:有嵌套路由的話 */

  routes: [
    { path: '/user/:id', component: User,
      children: [
        {
          // 當 /user/:id/profile 匹配成功,
          // UserProfile 會被渲染在 User 的 <router-view> 中
          path: 'profile',
          component: UserProfile
        },
        {
          // 當 /user/:id/posts 匹配成功
          // UserPosts 會被渲染在 User 的 <router-view> 中
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]

/* 示例3:router.push使用 */

// push的兩種參數狀況,儘可能用路徑的形式,用name的話有時候子路由可能不太方便
// -> /user/123
router.push({ path: `/user/${userId}` })
// 帶查詢參數,變成 /register?plan=private
router.push({ path: 'register', query: { userId }})
// replace
router.replace({ path: 'register', query: { userId }})
router.go(1)
router.go(-1)


/* 示例4:router-view多個的狀況 */
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

{
  path: '/',
  components: {
    default: Foo,
    a: Bar,
    b: Baz
  }
}

/* 示例5:重定向和別名 */

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
    // { path: '/b', component: B, alias: '/a' }
  ]
})


/* 示例6:導航守衛 */

const router = new VueRouter({ ... })
// router上面的鉤子
router.beforeEach((to, from, next) => {
  next()
//   next(false)
//   next('/')
})

router.afterEach((to, from) => {
  // ...
})

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      // route上的鉤子
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
// 組件內的鉤子
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染該組件的對應路由被 confirm 前調用
    // 不!能!獲取組件實例 `this`
    // 由於當守衛執行前,組件實例還沒被建立
  },
  beforeRouteUpdate (to, from, next) {
    // 在當前路由改變,可是該組件被複用時調用
    // 舉例來講,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
    // 因爲會渲染一樣的 Foo 組件,所以組件實例會被複用。而這個鉤子就會在這個狀況下被調用。
    // 能夠訪問組件實例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 導航離開該組件的對應路由時調用
    // 能夠訪問組件實例 `this`
  }
}

/* 示例7:meta的使用 */

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!auth.loggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 確保必定要調用 next()
  }
})

/* 示例8:跳轉路徑的特效 */

<transition>
  <router-view></router-view>
</transition>

const Foo = {
  template: `
    <transition name="slide">
      <div class="foo">...</div>
    </transition>
  `
}

/* 示例9:根據路徑的參數去請求數據 這邊顯示跳轉以後請求數據 */
export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // 組件建立完後獲取數據,
    // 此時 data 已經被 observed 了
    this.fetchData()
  },
  watch: {
    // 若是路由有變化,會再次執行該方法
    '$route': 'fetchData'
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}

/* 示例10:控制滾動條的位置 */
scrollBehavior (to, from, savedPosition) {
  if (savedPosition) {
    return savedPosition
  } else {
    return { x: 0, y: 0 }
  }
}
複製代碼

寫例子來實踐

這裏直接查看
github地址git

需求:github

  • 如今總共有四個頁面 home profile user login,profile是我的中心的意思
  • 而後 user下面有 create-user user-list
  • 而後設置 只有登陸以後 才能去 profile user 不然就跳轉到 login
  • 每一個頁面都有導航條

1.加四個視圖和對應的路由

  • vue create router-apply,先選擇history模式好了
  • views增長 每一個頁面的視圖
  • router那邊配置路徑
  • app那邊就能夠寫導航條了 router-link router-view
  • 坑:發現懶加載的狀況下,路徑不能寫成變量,可是能夠別名

2.user那邊增長子頁面

  • views增長 create-user user-list
  • router那邊配置路徑
  • user.vue那邊增長router-view

3.user子頁面能相互跳轉,增長的用戶跳轉的時候傳遞到user-list

  • create-user 有input框和跳轉按鈕,跳轉的時候,帶着值,用query的方式
  • 順便還能夠設置/user路徑的時候跳轉到/user/create-user

4.設置只有登陸才能去profile和user

  • profile和user路徑設置meta
  • 在router那邊增長beforeEach鉤子

核心代碼展現

// main.js
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.needLogin)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!isLogin) {
      next({
        path: '/login',
        // 方便登陸以後返回來
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 確保必定要調用 next()
  }
})

// router.js
 {
  path: '/user',
  name: 'user',
  component: () => import(/* webpackChunkName: "user" */ './views/User.vue'),
  children: [
    {
      path: 'create-user',
      alias: '',
      component: () => import(/* webpackChunkName: "createUser" */ './views/CreateUser.vue')
    },
    {
      path: 'user-list',
      component: () => import(/* webpackChunkName: "userList" */ './views/UserList.vue')
    }
  ],
  meta: {
    needLogin: true
  }
}
複製代碼
相關文章
相關標籤/搜索