【小慕读书】—— 后台管理系统学习:Vuex 和 Vue-router 进阶
前言:最近在学习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>
注:项目来自慕课网