文章目录

https://vuex.vuejs.org/zh/
https://router.vuejs.org/zh/

npm install vuex npm install -g vue-router

when to use vuex?
multiple components depend on or affect one state

vuex flow

xx

demos

import {createStore, useStore, mapState, mapGetters, mapActions, mapMutations} from 'vuex'
// store/index.js
import user from './modules/user'

export const store = createStore<RootState>({
  modules: {
    user,
  }
})

// store/modules/user.js
export default { // put related actions,mutations,states,getters to one group
  strict: process.env.NODE_ENV !== 'production',
  namespaced: true,  // mapState('enable_this_param', [...])
  state: () => {
    return {
      state1: 0  // {{$store.state.state1}}
    }
  },
  actions: { // 业务逻辑的前置处理,通常伴随着api调用
    // warning: do not manipulate context.state.state1 directly!
    action1(context, value){
      context.commit('group1/ACTION1', value)  // pass value to mutations directly
    }
    context.dispatch('group1/action1', value)  // current logic is too complex, pass to other actions for further processing
  },
  mutations: { // this is the cheff; debugger is monitering these actions
    setAction1(state, value){
      state.state1 += value
    }
  },
  getters: {
    output1(state) { // {{$store.getters.output1}}; warning: this.$store.getters['group1/output1']
      return state.state1 * 10
    }
  }
}

// views/user.js
setup(){
  const store = useStore()
  // map states/getters to computed methods(debugger name: vuex bindings)
  computed:{
    ...mapState('group1',{state1: 'state1'})  // avoid writing '$store.state.group1.xxx' --> 'xxx'
    ...mapGetters(['group1','output1'])  // if computed name is the same as getter's name, u can use array! '$store.getters.group1.output1' --> 'output1'
  }
  // map actions/mutations to methods; params are passed by template
  methods:{
    ...mapActions('group1', ['action1'])  // {{action1(n)}} == this.$store.dispatch('action1', n); warning: when lose the first param: '{{group1.action1(n)}}'
    ...mapMutations('group1',{myACTIONS1: 'ACTION1'})  // {{myACTION1(n)}} == this.$store.commit('ACTION1', n)
  }
}

route

  • 路由切换时,路由组件将被频繁的挂载+卸载!
  • 除1级路由外无需加prefix:’/'!
  • ‘#’: url中井号后面的都是vue路由,刷新页面切换等不会被发送到服务器!
<!-- 路由跳转(非<a>的页面跳转,不会刷新页面); 实际上最终还是转换到了<a>标签 -->
<router-link :to="{
    name: 'routename',  <!-- 与path二选一即可,不常用 -->
    path: '/relative/path',
    query: {  <!-- {{$route.query.uid}} -->
      uid: 1
    },
    params: { <!-- warning: 必须配合name,不能使用path! -->
      id: 2  <!-- routes里面定义的path中的占位变量 -->
    }
  }"
  tag='li'
  event='mouseover'>
</router-link>
<!-- 路由变化的时候展示对应的组件; 保存状态(一般仅缓存有input交互的page,不指定会全缓存,多个可写成数组:include="['','']")! -->
<keep-alive include="component1"><router-view></router-view></<keep-alive>
import VueRouter from 'vue-router'
const routes = [
  { path: '/', redirect: '/home', name: 'routename' },
  { path: '/home', component: Home, props: true },  // 子组件通过prop访问动态路由的路径参数,也可以如下:
  { path: '/movie/:id',  // 访问动态路由的路径参数(冒号只能出现在路由规则中): this.$route.params.id
    component: Movie,
    props: true,  // component中props里也对应声明下'id',则template中{{id}}可以直接取到实参,省的写:'{{$route.params.id}}'!
    props($route) { // 比上面更高端的写法. 路由跳转时回调该函数. 计算后会将uid暴露给component. component在props声明后,在template中用'{{uid}}'拿到这里的计算值!
      return {uid: $route.query.uid}
    },
    meta:{
      isAuth: false,  // 路由守卫中使用
      title: ''
    },
    beforeEnter: (to, from, next) => { // 局部路由守卫(无后置)
    }
  },
  { path: '/about', component: About,
    redirect: '/about/tab1',  // 默认跳转. 实际上把tab1的path置空,就变成了默认子路由(router-link也要为空).
    children: [
      { path: 'tab1', component: Tab1 }  // /about/tab1
    ]
  },
]
const router = VueRoute.createRouter({
  history: VueRouter.createWebHashHistory(),  // url中没有'#'了(无#兼容性略差)
  routes: routes,
})

// 每个组件不同
this.$route.fullPath;  // /movie/2?name=zs&age=20
this.$route.path;  // /movie/2
this.$route.params.query;  // {name:'zs', age:'20'}

// 每个组件都是同一个router
this.$router.push('/home')  // 会增加历史记录
this.$router.replace('/home')  // 替换当前历史记录
this.$router.go(2)  // 前进或后退几步(可以是负数,超过上线无任何效果)
this.$router.forward/back()  // go(1) == forward(); go(-1) == back()

// 第一次'/'通常很多余
router.beforeEach((to, from, next) { // 前置路由守卫
  if (to.path === '/main') { // to: 将要访问的路由信息对象; from: 将要离开的路由信息对象
    const token = localStorage.getItem('token')  // debug-Application标签有Storage
    if (token) {
      next()  // 调用表示放行,允许此次路由导航
    } else {
      next('/login')  // 强制跳转到指定路由; next(false):停留在from,哪都不许去
    }
  }
  next()
})
router.afterEach((to, from){ // 后置路由守卫
  document.title = to.meta.title || 'default title'
})
app.use(router)