1. 程式人生 > 其它 >【小慕讀書】—— 後臺管理系統學習:Vuex 和 Vue-router 進階

【小慕讀書】—— 後臺管理系統學習:Vuex 和 Vue-router 進階

技術標籤:vuereactjavajavascriptjs

前言:最近在學習Vue+Element UI+Node.js小慕讀書中後臺管理系統開發課程,這裡對學習過程作個筆記,方便自己和大家翻閱。


一、Vuex 原理解析

Vuex 的原理關鍵:使用 Vue 例項管理狀態

<body>
    <div id="root">{{data}}</div>
    <div id="root2">{{data2}}</div>
    <div id="root3">
      <button @click="change">change</button>
    </div>
    <script>
      function registerPlugin(Vue) {
        const vuex = {}
        vuex._vm = new Vue({     //_vm是Vue例項
          data: {
            message: 'hello vue.js'
          }
        })
        vuex.state = vuex._vm
        vuex.mutations = {
          setMessage(value) {
            vuex.state.message = value
          }
        }
        function init() {
          this.$store = vuex
        }
        Vue.mixin({             //全域性的mixin
          beforeCreate: init    
        })
      }
      Vue.use(registerPlugin)
      new Vue({
        el: '#root',
        computed: {
          data() {
            return this.$store.state.message
          }
        }
      })
      new Vue({
        el: '#root2',
        computed: {
          data2() {
            return this.$store.state.message
          }
        }
      })
      new Vue({
        el: '#root3',
        methods: {
          change() {
            const newValue = this.$store.state.message + '.'
            this.$store.mutations.setMessage(newValue)     //Vue響應式 前兩個Vue例項的message都會變化
          }
        }
      })
    </script>
  </body>

二、vue-router 實現原理

vue-router 例項化時會初始化 this.history,不同 mode 對應不同的 history

constructor (options: RouterOptions = {}) {
    this.mode = mode
    
    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
}

這裡以 HashHistory 為例,vue-router 的 push 方法實現如下: (實際呼叫的是Hashhistory的push方法) 

push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    // $flow-disable-line
    if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        this.history.push(location, resolve, reject)
      })
    } else {
      this.history.push(location, onComplete, onAbort)
    }
}

HashHistory 具體實現了 push 方法:  

function pushHash (path) {
  if (supportsPushState) {
    pushState(getUrl(path))
  } else {
    window.location.hash = path
  }
}

對路由的監聽通過 hash 相應的事件監聽實現:  

window.addEventListener(
  supportsPushState ? 'popstate' : 'hashchange',
  () => {
    const current = this.current
    if (!ensureSlash()) {
      return
    }
    this.transitionTo(getHash(), route => {
      if (supportsScroll) {
        handleScroll(this.router, route, current, true)
      }
      if (!supportsPushState) {
        replaceHash(route.fullPath)
      }
    })
  }
)

除此之外,vue-router 還提供了兩個元件:  

Vue.component('RouterView', View)
Vue.component('RouterLink', Link)

三、vue-router 路由守衛

建立 router.js:  

import Vue from 'vue'
import Route from 'vue-router'
import HelloWorld from './components/HelloWorld'

Vue.use(Route)
const routes = [
  { path: '/hello-world', component: HelloWorld }
]
const router = new Route({
  routes
})

export default router

在 main.js 中引用 router,並加入 vue 例項:  

import router from './router'

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

四、全域性守衛

進入每一個路由都會觸發的鉤子函式

注意:因為寫在vue-router中,所以全域性守衛都是沒有辦法獲得Vue例項的。

//router.js
router.beforeEach((to, from, next) => { //當路由進入之前可以觸發的操作 console.log('beforeEach', to, from) next() }) router.beforeResolve((to, from, next) => { //當路由被解析之前被呼叫 Vue2.5新特性 console.log('beforeResolve', to, from) next() }) router.afterEach((to, from) => { //當路由進入之後可以觸發的事件 console.log('afterEach', to, from) })

整個路由守衛執行是在元件生命週期函式之前的:

beforeEach(全域性守衛)、befforeResolve(全域性守衛)、afterEach(全域性守衛)

beforeCreate、created、beforeMount、mouted

五、區域性守衛

只適合在當前路由使用,是區域性的路由守衛。(寫在和元件的生命週期函式同一級)

1.to:表示當前路由,即要進入的路由

2.from:表示從哪來的,

3.next() 表示下一步要幹啥,next('/addGoods')就表示下一步,調到路由/addGoods

//A.vue
beforeRouteEnter (to, from, next) { //在需要進入的元件中呼叫 // 不能獲取元件例項 `this`, 因為在元件生命週期beforeCreate之前 console.log('beforeRouteEnter', to, from) next() }, beforeRouteUpdate (to, from, next) { //在複用(更新)的元件中呼叫 console.log('beforeRouteUpdate', to, from) next() }, beforeRouteLeave (to, from, next) { //在離開的元件中呼叫 console.log('beforeRouteLeave', to, from) next() }
  • 進入元件:beforeEach(全域性守衛)、beforeRouteEnter(區域性守衛)、beforeResolve(全域性守衛)、afterEach(全域性守衛)、beforeCreate、created、beforeMount、mounted
  • 更新元件:beforeEach(全域性守衛)、beforeRouteUpdate(區域性守衛)、beforeResolve(全域性守衛)、afterEach(全域性守衛)
  • 離開元件:beforeRouteLeave(區域性守衛)、beforeEach(全域性守衛)、beforeResolve(全域性守衛)、afterEach(全域性守衛)、beforeDestory

六、路由元資訊

通過 meta 定義路由元資訊  

const routes = [
  { path: '/a', component: A, meta: { title: 'Custom Title A' } }
]

使用 meta 資訊動態修改標題

(和路由相關的功能,推薦放在路由的全域性守衛中實現)  

router.beforeEach((to, from, next) => {
  console.log('beforeEach', to, from)
  if (to.meta && to.meta.title) {
    document.title = to.meta.title
  } else {
    document.title = 'default title'
  }
  next()
})

七、路由 API

使用 router.addRoutes 動態新增路由

使用場景:選單路由的許可權賦值 

addRoute() {
    this.$router.addRoutes([{
      path: '/b', component: B, meta: { title: 'Custom Title B' },
    }])
}

此時可以訪問到 B 元件  

<router-link to='/b'>to B</router-link>


注:專案來自慕課網