vuex与vue-router

vuexvue-routervue生态系统中的很重要的两个工具。vuexvue用来管理数据状态的一种机制,将Vue中所有的状态(数据)抽离出来进行统一管理。Vue RouterVue.js 官方的路由管理器。它和Vue.js的核心深度集成,让构建单页面应用变得易如反掌,是SPA(单页应用)的路径管理器。

Vuex

Vuex是一个专门为vue.js应用程序开发的状态(数据)管理模式.它采用集中式存储应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.

插值语法
Vuex使用场景:多个视图依赖于同一状态或相同数据, 来自不同视图的行为需要

使用方式
  • npm install vuex -S 安装vuex模块
  • import Vuex from 'vuex' 引入vuex模块
  • Vue.use(Vuex) 作为插件使用
  • new Vuex.Store() 定义store容器
  • new Vue({store}) 注入到根实例中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // src/store/index.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex) // 将vuex作为插件使用
    let store = new Vuex.store({}) // 定义store容器
    export default store // 将store导出
    // main.js
    import store from './store'
    new Vue({
    el: "#app",
    store, // 将store注入到根实例中
    })
Vuex核心概念
  • Store 类似容器 包含应用的大部分状态 不建议直接修改state中的状态 使用提交mutations显式修改
  • State 包含所有应用级别状态的对象

    1
    2
    3
    4
    5
    6
    7
    // 定义store容器
    let store = new Vuex.Store({
    state: { conut: 10 }
    })
    // 组件内取出状态(数据) hello.vue
    this.$store.state.count
    //
  • Getters 对state数据进行处理 在组件内部获取store中的状态函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    let store = new Vuex.Store({
    state: {
    shopList: [{
    id: 123, count: 20
    },{
    id: 124, count: 30
    }]
    },
    getters: { // gettres可以定义为一个函数
    total(state){
    return state.shopList.reduce((prev, item) => prev + item.count, 0)
    }
    }
    })
    //xx.vue 中使用
    this.$store.getters.total
  • Mutation vuex的state状态更新的唯一方式提交mutations
    Mutation主要包含两部分 type 字符串的事件类型 handle该回调函数的第一个参数就是state
    mutation必须是同步更新状态,不要在mutation中进行异步的操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    mutations: {
    // 第一个默认参数是state 第二个参数 payload是commit传递过来的值 可以是对象
    updateCount(state, payload){
    state.count += payload.add
    }
    }
    methods: {
    // 修改store中state的数据 提交一个mutations
    // 如果不包含异步操作 可以直接在组建中 使用commit提交mutations
    changeState(){
    this.$store.commit('updateCount', {add: 5})
    }
    }
  • Action 包含异步操作都放在action里面 提交mutations改变状态
    context是和store对象具有相同方法和属性的对象,在action中可以通过context进行commit一个mutations操作,也可以获取context.state等。在vue组件中调用 this.$store.dispatch(‘action’)去触发action。可以将异步操作放在一个Promise中,并且成功或者失败后,调用对应的resolve或reject

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // action 中返回一个promise
    actions: {
    increment(context) {
    return new Promise((resolve) => {
    setTimeout(() => { // 异步操作
    context.commit('incrementMutation')
    resolve()
    }, 2000)
    })
    }
    }
    methods: {
    incrementHandle(){
    // 派发action操作
    this.$store.dispatch('increment').then(res => {
    console.log('完成更新操作')
    })
    }
    }
  • Module 模块
    Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutation、action、getters等
    module下的局部状态通过state获取, 根节点(全局)的状态则通过rootState获取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 获取全局的state
    const moduleA = {
    action: {
    test({state, commit, rootState}){}
    },
    getters: {
    test(state, getters, rootState){}
    }
    }
  • 使用vuex-persistedstate实现vuex持久化
    vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态, 使用此插件可以将state存储到localstorage或sessionstorage中

    1
    2
    3
    4
    5
    6
    import createPersistedState from "vuex-persistedstate"
    const store = new Vuex.Store({
      plugins: [createPersistedState({
        storage: window.sessionStorage
      })]
    })
Vuex的工具函数/语法糖/钩子函数
  • ...mapState(['count']) 取state中的数据
  • ...mapGetters(['total']) 对state数据的处理
  • ...mapMutations(['updateCount']) 调用mutations执行同步操作
  • ...mapActions(['updateCountSync']) 调用action提交mutations执行异步操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // index.vue
    import { mapState,mapGetters,mapMutations,mapActions } from 'vuex'
    new Vue({
    computed(){ // mapState 和 mapGetters 需要放在computed中
    ...mapState(['count']), // 后面还有内容时 需要加上 逗号,state数据放在字符串数组中
    ...mapState('goods', ['count']), // 有modules
    ...mapGetters(['total']),
    ...mapGetters('goods', ['total']), // 有modules
    },
    methods: { // mapMutations 和 mapActions 需要放在 methods中
    ...mapMutations(['updateCount']), // 后面还有内容时 需要加上 逗号,方法放在字符串数组中
    ...mapMutations('goods', ['updateCount']), // 有modules
    ...mapActions(['updateCountSync']),
    ...mapActions('goods', ['updateCountSync']), // 如果是某个modules下action需要加namespaced命名空间
    }
    })

vuex实现原理

使用 Vue 实例管理状态,只能在vue中使用

Vue Router

vue-router实现原理
  • SPA(single page application) 单页面应用程序 页面url改变, 但页面不进行整体的刷新
  • Hash模式

    1
    2
    // vue-router 默认 hash 模式 hash模式会在url中自带#
    // Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。hash 模式的原理是 onhashchange 事件(监测hash值变化)
  • History模式

    1
    2
    3
    4
    5
    6
    7
    // 用路由的 history 模式,只需要在配置路由规则时,加入"mode: 'history'",这种模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。
    // 对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求
    // 需要后台配置支持 后台没有正确的配置 就会返回 404 如nginx配置
    /* location / {
    try_files $uri $uri/ /index.html;
    } */
    // {path: "*", redirect: "/"}] 前端路由设置 如果URL输入错误 路由不存在 自动跳到到首页 或设置404页面
基础配置
  • 安装 npm install vue-router --save
  • 配置路由文件 router/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // router/index.js
    // 引入vue-router插件
    import Router from 'vue-router'
    // 安装路由插件router
    Vue.use(Router)
    // 生成路由实例
    // Router 常用参数
    // routes: [] 路由信息 mode: 'hash' 路由模式 linkActiveClass: 'highlight' 路由高亮
    // scrollBehavior(to,from,savedPosition) 滚动行为 fallback:true 对于不支持history路由时, 自动跳转到hash模式下
    let router = new Router({ // 配置组件和路由的映射关系
    // 定义路由模式 默认为 hash
    mode: 'history', // 需要后端配合
    // 设置当前路由高亮样式
    linkActiveClass: 'is-active',
    // 配置组件和路径的映射关系 routes 常见参数
    // path: '/home' redirect: '/app' 重定向 name: 路由名称 component: 'todo' 路由组件
    // meta: 元信息 设置meta信息 children: [{}] 子路由 嵌套路由
    routes: [{
    path: '/',
    redirect: '/index' // 将根路径重定向到首页
    }, {
    path: '/index',
    name: Home,
    component: Home
    }, {
    path: '/Management',
    name: 'Management',
    component: Layout,
    children:[{
    path: '/project',
    name: 'Project',
    component: Project,
    meta: {
    login: true,
    title: '标题',
    auth: true
    }
    }]
    }]
    })
    new Vue({
    el: '#app',
    router, // 将路由挂载到Vue实例中
    })
    // app.vue 中
    <router-view>路由对应组件显示的位置</router-view>
  • 通过<router-link><router-view>使用声明式路由
    <router-view /> 组件显示的位置
    <router-link to="/index" tag="li">主页</router-link> 跳转链接
    tag可以指定<router-link>之后渲染成什么标签,默认是a

编程式导航跳转

this.$router.push() 会向history栈添加一个新的记录并导航到改URL下

  • this.$router.push('index') 字符串
  • this.$router.push({ name: 'user', params: { userId: '123' }}) 命名的路由 使用的是name和params 如果使用path则params无效
  • this.$router.push({ path: 'register', query: { plan: 'private' }}) 带查询参数 使用的是path和query
    this.$router.replace() 它不会向 history 添加新记录, 替换掉当前的 history 记录。
    this.$router.go(n) 在 history 记录中向前或者后退多少步
  • this.$router.go(1) 在 history 记录中前进一步,等同于 history.forward()
  • this.$router.go(-1) 在 history 后退一步记录,等同于 history.back()
$router与$route
  • $router 路由实例对象 使用new VueRouter创建的实例,包括了路由的跳转方法,钩子函数等。常用于编程式导航
  • $route 当前路由信息对象 在组件内部可以通过 this.$route 的方式进行调用。

    1
    2
    3
    4
    5
    6
    // 1. $route.fullPath 为当前路由的全路径
    // 2. $route.path 为当前路由的绝对路径
    // 3. $route.params 包含路由中动态片段和全匹配片段的键值对
    // 4. $route.query 包含路由中查询参数的键值对
    // 5. $route.name 当前路由设置的name属性。
    // 6. $route.meta 当前路由元信息
  • $route与$router的区别
    $router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
    $route为当前路由对象,里面可以获取name、path、query、params等

滚动行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//点击浏览器的前进后退或切换导航时触发
scrollBehavior(to,from,savePosition){
console.log(to) //要进入的目标路由对象 要到哪里去
console.log(from) //离开的路由对象 从哪里离开
console.log(savaPosition) //记录滚动条的坐标 点击前进后退的时候记录值
if(savePosition){
return savePosition // 滚动到记录的位置
}else{
return {x:0, y:0}
}
if(to.hash){ // 滚动到某锚点
return {
selector:to.hash
}
}
}
路由传参

传递参数方式一: <router-link>

1
2
3
4
5
6
<router-link
:to="{
path: '/detial/:id' // params形式
query: { name: 'tew', age: '28' } // query模式
}"
>简介</router-link>

传递参数方式二: JavaScript代码

1
2
3
4
this.$router.push({
path: '/detial/:id',
query: { name: 'tew', age: '28' }
})

传递参数主要有两种类型: params和query

1
2
3
4
5
6
7
8
9
10
11
12
/*
params的类型:
配置路由格式: /router/:id
传递的方式: 在path后面跟上对应的值
传递后形成的路径: /router/123, /router/abc
通过this.$route.params 获取params方式参数
query的类型:
配置路由格式: /router, 也就是普通配置
传递的方式: 对象中使用query的key作为传递方式
传递后形成的路径: /router?id=123, /router?id=abc
通过this.$route.query 获取query方式参数
*/

路由导航守卫

全局路由导航守卫

  • router.beforeEach(to, from, next) 全局前置守卫 常用beforeEach来完成页面标题的修改.
  • router.beforeResolve(to, from, next) 全局解析守卫
  • router.afterEach(to, from, next) 全局后置守卫
1
2
3
4
5
6
7
8
9
10
11
12
// 每个守卫方法接收三个参数
// to: 即将要进入的目标 路由对象
// from: 当前导航正要离开的路由
// next: 同意跳转 进行下一步执行 resolve
router.beforeEach((to, from, next) => {
// 路由跳转前的特定的业务逻辑 修改路由跳转、设置title等
NProgress.start() // start progress bar
document.title = to.meta.title // 设置页面标题
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})

路由独享的守卫
在路由配置上直接定义 beforeEnter

1
2
3
4
5
6
7
const router = new VueRouter({
routes: [{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {}
}]
})

组件内的守卫

  • beforeRouteEnter
  • beforeRouteUpdate 路由参数更新时触发 详情页面id变化 mounted里的函数只执行一次的问题 可以代替watch
  • beforeRouteLeave
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this` 因为当守卫执行前,组件实例还没被创建
// 但可以使用 通过 `vm` 访问组件实例 next(vm => {})
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
// 生命周期 beforeDestroy/destroyed 在beforeRouteLeave 之后
}
}
使用router.addRoutes动态添加路由
1
2
3
this.$router.addRoutes([{
path: '/test', component: Test, meta: { title: 'test'}
}])
路由守卫触发过程
  1. 导航被触发
  2. 调用全局的beforeEach前置守卫
  3. 在重用的组件里调用beforeRouterUpdate守卫
  4. 在路由配置里面调用路由独享守卫
  5. 调用被激活组件的beforeRouterEnter守卫
  6. 调用全局的beforeResolve守卫
  7. 调用全局的afterEach后置守卫
  8. 触发DOM更新
keep-alive与 vue-router
1
2
3
4
5
6
7
8
9
10
11
12
// keep-alive 是 Vue 内置的组件,可以使被包含的组件保留状态,或避免重新渲染。
// 它们有两个非常重要的属性:
// include - 字符串或正则表达,只有匹配的组件会被缓存
// exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
// router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
// 组件使用keep-alive专有 生命周期
// activated: function(){ console.log('activated') }
// deactivated: function(){ console.log('deactivated') }
// Detail,City 不被缓存组件name
<keep-alive exclude="Detail, City">
<router-view/>
</keep-alive>
vue项目实现按需加载或懒加载的3种方式
  • 路由懒加载的主要作用就是将每个路由对应的组件打包到单独的js中.只有在这个路由被访问到的时候, 才加载对应路由的js文件
  • vue异步组件 AMD

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .
    // 但是,这种情况下一个组件生成一个js文件
    /* vue异步组件技术 */
    {
    path: '/home',
    name: 'home',
    component: resolve => require(['@/components/home'],resolve)
    }, {
    path: '/index',
    name: 'Index',
    component: resolve => require(['@/components/index'],resolve)
    }
  • 路由懒加载(使用import)

    1
    2
    3
    4
    5
    6
    // 没有指定webpackChunkName,每个组件打包成一个js文件。
    /* const Home = () => import('@/components/home')
    const Index = () => import('@/components/index')
    // 指定了相同的webpackChunkName,会合并打包成一个js文件。 把组件按组分块*/
    const Home = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home')
    const Index = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/index')
  • webpack的require.ensure()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 使用webpack的require.ensure, 多个路由指定相同的chunkName,会合并打包成一个js文件。
    {
    path: '/home',
    name: 'home',
    component: r => require.ensure([], () => r(require('@/components/home')), 'demo')
    }, {
    path: '/index',
    name: 'Index',
    component: r => require.ensure([], () => r(require('@/components/index')), 'demo')
    }
-------------本文结束感谢您的阅读-------------
0%