文章目录
- 主要内容
- React的传值方式
- Vue3的传值方式
- Vue2的传值方式
主要内容
本文总结了React、Vue3、Vue2组件之间传值的各种方式
React的传值方式
react的传值方式可以有六种:
- props:
父传子:父组件以props的方式传参
子传父:父组件以props的方式传递方法,让子组件调用- context:上下文通信类似于vue的project/inject
- ref:通信获取组件实例,想要获取函数式组件的ref实例,需要使用forwardRef、或者forwardRef配合useImperativeHandle才能获取
子组件只使用forwardRef:那么父组件可以直接获取子组件ref绑定的dom元素
子组件forwardRef配合useImperativeHandle使用:那么父组件只能获取useImperativeHandle返回的自定义句柄,无法访问ref绑定的dom元素- chilren:类似于vue的插槽,当props和children同时存在的时候,children插槽为准
- eventEmitter:借助外部插件,通过一个空的实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信
- redux(或者mobx等):状态管理通信
应用场景:
- 父子组件通信:
- props
- context
- ref
- children
- 兄弟组件通信:
- EventEmitter
- Redux
- 跨层级组件通信:
- context
- EventEmitter
- Redux (或者mobx等)
1、props 通信
(1)父传子
【父组件】传入collapsed属性
【子组件】接收collapsed属性
(2)子传父
【父组件】传入一个函数给子组件,等待子组件触发此函数就执行
【子组件】接收父组件传入的函数,点击了按钮时触发父组件传入的函数
在react中子组件向父组件传递消息是通过回调函数的方式,也就是父组件把方法以props的形式传递给子组件来调用。
这一块和vue还是有很大差别的
2、context 上下文通信
类似于 vue 的 provide/inject
(1)子孙组件通信
【Home.tsx】提供 上下文
【Son.tsx】子组件可以接收也可以不接收,这里是啥也不做
【GrandSon.tsx】孙组件接收
接收方式一:使用 Consumer 接收爷爷的值
接收方式二:使用 useContext 接收爷爷的值
3、ref 通信
通过ref可以获取到子组件实例,然后使用组件上的属性或方法,这个和vue是类似的
注意:如果想要获取函数式组件的ref,需要使用forwardRef、或者forwardRef配合useImperativeHandle才能使用
子组件只使用forwardRef:那么父组件可以直接获取子组件ref绑定的dom元素
子组件forwardRef配合useImperativeHandle使用:那么父组件可以获取useImperativeHandle返回的自定义句柄
(1) 父组件通过useRef() 获取子组件的ref实例,子组件通过forwardRef()暴露实例
子组件
父组件
结果
(2) 父组件通过useRef() 获取子组件的ref实例,子组件通过forwardRef()结合useImperativeHandle()向父组件公开自定义引用句柄
useImperativeHandle:向父组件公开自定义引用句柄
子组件
父组件 (父组件通过ref只能访问到useImperativeHandle公开的自定义引用句柄)
结果
4、children 通信
在react中,写在组件中的内容都是children,类似vue里面的插槽slot。
子组件通过this.props.children获取插槽内容
注意:当props和插槽同时存在的时候,插槽为准。
插槽的使用
【home.tsx】父组件,给子组件传递插槽
【Son.tsx】子组件,接收插槽内容,通过props.children获取父组件传递过来的插槽内容
当props和插槽同时存在的时候,插槽为准。
父组件,传入插槽内容
子组件,接收插槽内容
结果
5、EventEmitter
react并不支持自定义事件,所以没有vue的$on $off $emit $once等方法。所以需要借助外部插件。
EventEmitter 就是一种,类似mitt。
这种方法通过一个空的实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。
安装
npm install events
使用
注意:on一定要先于emit之前,要不然浏览器的事件注册队列没有相应的触发器。当你emit抛出方法时,页面没有该方法的事件监听,所以导致你无法获取。
(1)先创建工具函数:eventEmiiter.ts 【src/utils/eventEmiiter.ts】
(2)注册事件
(3)发布事件
6、redux 状态管理通信
redux是一个 js 的状态管理库,用于管理应用的全局状态。
可以帮助开发者 管理和同步 应用中的数据状态,以实现组件间的数据共享和通信。
redux 遵循了一种单向数据流的架构模式,将整个应用的状态数据存储在一个全局的状态树store中,通过action和reducer来管理状态的变化。
redux遵循的三个原则:单一数据来源、状态只读、使用和纯函数进行修改。
redux的核心组成有
(1)store:状态容器,包含了应用的所有状态,并提供了一些方法来访问和修改状态,通过一个单一的store,管理整个应用的状态。
(2)action:是一个普通的 js 对象,每个action都有一个type字段,表示将要执行的动作,并可以携带一些额外的数据。
(3)reducer:状态变化的处理者,是一个纯函数,它接收当前的 state 和 action,返回一个新的state。
redux本身是同步的,可以使用中间件处理异步操作
redux-thunk可以让action变成函数形式存在,在函数中可以做一个写异步操作,然后等接口拿到数据以后再发生dispatch去更新最新的数据。
中间件:扩展redux的功能。
中间件是一个函数,可以捕获、拦截、处理Action,也可以在处理完Action后派发新的Action。
常见的中间件包括redux-thunk(处理异步Action)和 redux-logger(记录状态变化)
安装
npm i redux -D
封装store
【src/store/redux/reducers/settingReducer.ts】
【src/store/redux/reducers/testReducer.ts】
【src/store/redux/reducers/index.ts】合并reducer
【src/store/redux/index.ts】创建store
使用store
有三种方式,下面进行一一举例
使用store的第一种方式
页面一,使用并改变count的值、改变contentColor的值
页面二,只使用count的值和contentColor的值
使用store的第二种方式 使用Provider实现redux和react项目的结合
安装
npm i react-redux @types/react-redux -D
全局提供store:【src/main.tsx】通过Provider进行redux和react的项目关联
在页面一里使用connect(高阶组件)把redux数据映射到组件上,在组件中可以通过dispatch派发来改变redux数据
页面二,展示
使用store的第三种方式 使用useSelector, useDispatch (推荐使用!!)
安装
npm i react-redux @types/react-redux -D
全局提供store:【src/main.tsx】通过Provider进行redux和react的项目关联
在页面一里使用:使用useSelector获取某个store的所有state,使用 useDispatch 派发来改变redux数据
页面二,展示
效果展示:
上面例子中的三种使用store的方式达到的效果是一致的
页面一:初始化效果图
页面一,改变count的值为4,改变contentColor的值为 yellow ,跳转到页面2,显示count、contentColor最新值
页面二,显示count、contentColor最新值
action处理异步
action必须是一个简单的对象,不能是异步的。如果需要处理异步action,我们需要借助redux中间件来处理
处理异步action的中间件有:
1、redux-thunk 支持dispatch(函数)
2、redux-promise 支持dispatch(Promise)、dispatch({type:“”,payload:Promise})
3、redux-saga 支持异步和同步(推荐使用)
(1)redux-thunk 支持dispatch(函数)
【1】安装中间件
npm i redux-thunk -D
【2】修改【src/store/redux/index.ts】文件,创建store时添加中间件
【3】使用:添加中间件后,dispatch支持函数
【4】如果dispatch是一个函数,为了能够重复使用,可以封装一个公共函数
使用
(2)redux-promise,支持dispatch(Promise)、dispatch({type:“”,payload:Promise})
【1】安装中间件
npm i redux-promise -D
【2】修改【src/store/redux/index.ts】文件,创建store时添加中间件
【3】使用:添加中间件后,dispatch可以接收Promise对象作为参数。
thunk-promise的dispatch两种使用方法:
1、是直接传入Promise对象。
2、是将Promise对象放入Action的payload字段中。
1、dispatch函数,直接传入promise对象
封装一个Promise对象
使用
2、将Promise对象放入Action的payload字段中
封装一个Promise对象
使用
(3)redux-saga 支持异步和同步(推荐使用)
【1】安装中间件
npm i redux-saga -D
【2】创建【src/store/redux/reducers/asyncAction.ts】文件,专门写异步操作的方法
【3】创建【src/store/redux/reducers/sagas.ts】文件,集中写saga
【4】修改【src/store/redux/index.ts】文件,创建store时添加中间件
【5】使用 saga执行reducer ,可以选择调用异步,也可以选择调用同步
持久化store
安装
npm i redux-persist --D
设置持久化
(1)修改【src/store/index.ts】文件,改为持久化,持久化至sessionStorage
紫色框原来创建store的方式,没有持久化的方式。红色框是新增的代码
(2)修改【src/main.tsx】文件,添加持久化
注释掉的那行是修改前的代码,红色框是新增的代码
(3)浏览器持久化效果
Vue3的传值方式
1、props/emits 通信
(1)父传子
【parent.vue】
【son.vue】
(2)子传父
【son.vue】
【parent.vue】
(3)v-model双向绑定更新
父组件传入的v-model:show 和 子组件emits(“update:show”, newShow.value) 配合使用,使得子组件改变值时,父组件能及时更新
子组件的props.show可以在初始化时收到父组件传过来的值,但父组件改变值时,子组件无法知道,所以子组件需要设置一个watch监听,可以让父组件改变时,子组件能及时更新。
【parent.vue】
【son.vue】
效果:点击父组件的按钮或子组件的按钮都能同步更新值
2、$attrs / $listeners 通信
(1)$attrs 父传下层
$attrs里面包含着上层组件传递的所有数据(除style和class)
当组件(子组件)声明了prop时候,attrs里面包含上层组件传递的所有数据除去prop里面的数据剩下的数据
【parent.vue】
【son.vue】
效果
(2)v-bind=“$attrs” 和 inheriAttrs:false的使用
默认inheriAttrs:true自动继承$attrs剩下的属性到当前组件(子组件)根节点
例如上面的案例,剩下age属性,就默认继承显示在子组件的根节点上
【son.vue】设置v-bind=“$attrs” 和 inheriAttrs:false
效果
(3)$listeners 子传父,父调用子组件的事件
【son.vue】
【parent.vue】
效果
3、provide/inject 通信:父传下层
【parent.vue】
【son.vue】
效果
点击父组件的按钮更改值,子组件也能及时更新
4、ref/defineExpose 子传父,父调用子组件的暴露的事件、属性
defineExpose是vue3新增的一个api
放在 < scipt setup >下使用的,目的是把属性和方法暴露出去
可以用于父子组件通信,子组件把属性暴露出去, 父组件用ref获取子组件DOM,子组件暴露的方法或属性可以用dom获取。
【son.vue】
【parent.vue】
效果
5、插槽
(1)默认插槽
无默认内容
【son.vue】
【parent.vue】
【parent.vue】还可以这样写
//写法1
<Son> 666 </Son>
//写法2
<Son>
<template v-slot:default> 666 </template>
</Son>
//写法3
<Son>
<template #default> 666 </template>
</Son>
效果
有默认内容
【son.vue】
【parent.vue】父组件使用子组件什么也不传
效果
【parent.vue】父组件传入了默认插槽的内容,就会替换子组件内部的默认内容
效果
(2)具名插槽
具名插槽-不传值
【son.vue】设置了一个名为content的插槽
【parent.vue】
效果
具名插槽-传值
【son.vue】设置了一个名为content的插槽,并传了一个值msg
【parent.vue】
【parent.vue】还可以这样写
//解构写法1:
<Son>
<template #content="{ msg }">
接收值:{{ msg }}
</template>
</Son>
//解构写法2:
<Son>
<template v-slot:content="{ msg }">
接收值:{{ msg }}
</template>
</Son>
//不解构写法1:
<Son>
<template #content="contentProps">
接收值:{{ contentProps.msg }}
</template>
</Son>
//不解构写法2:
<Son>
<template v-slot:content="contentProps">
接收值:{{ contentProps.msg }}
</template>
</Son>
效果
(3)动态插槽
【son.vue】
【parent.vue】
效果
6、EventEmitter
安装 mitt
npm install mitt --save
Mitt是一个微型的 EventEmitter 库,在Vue3中,官方推荐使用它替代已经移除的EventBus,所以在Vue3使用前我们需要先安装Mitt依赖
1.emit(name,data) : 触发事件,两个参数:name:触发的方法名,data:需要传递的参数
2.on(name,callback) : 绑定事件,两个参数:name:绑定的方法名,callback:触发后执行的回调函数
3.off(name) : 解绑事件,一个参数:name:需要解绑的方法名
注:以上的name应该保持一致(同一个)
注:on一定要先于emit之前,要不然浏览器的事件注册队列没有相应的触发器。当你emit抛出方法时,页面没有该方法的事件监听,所以导致你无法获取。
使用 mitt
方式一:挂载全局
【main.ts】挂载在全局
【son.vue】注册事件,并接收值
注意:on一定要先于emit之前,要不然浏览器的事件注册队列没有相应的触发器。当你emit抛出方法时,页面没有该方法的事件监听,所以导致你无法获取。
【parent.vue】发布事件、卸载事件
效果
方式二:提供统一的方法
新建文件【src/utils/eventBus.ts】
import mitt from "mitt";
const eventBus = mitt();
export default eventBus;
【son.vue】注册事件,并接收值
注意:on一定要先于emit之前,要不然浏览器的事件注册队列没有相应的触发器。当你emit抛出方法时,页面没有该方法的事件监听,所以导致你无法获取。
import eventBus from "@/utils/eventBus";
eventBus.on("message", (value: string) => {
console.log("接收值:", value);
});
【parent.vue】发布事件、卸载事件
import eventBus from "@/utils/eventBus";
onMounted(() => {
eventBus.emit("message", "我是根组件"); //发布事件
});
onUnmounted(() => {
eventBus.off("message"); //卸载事件
});
效果
7、pinia
(1)安装
npm install pinia -S
持久化插件:
npm install pinia-plugin-persistedstate --save
(2)定义Store
新建目录
【src/store/globalStore.ts】-----------Pinia的Option Store方式定义 Store
import { useWindowSize } from "@vueuse/core";
import { defineStore } from "pinia";
export const useGlobalStore = defineStore("globalStore", {
state: () => {
return {
windowWidth: useWindowSize().width, // 窗口实时宽度
windowHeight: useWindowSize().height, // 窗口实时高度
projectName: "fuyu",
};
},
getters: {
getWindowWidth: (state) => {
return state.windowWidth;
},
getWindowHeight: (state) => {
return state.windowHeight;
},
getProjectName: (state) => {
return state.projectName;
},
},
actions: {
setProjectName(projectName: string) {
this.projectName = projectName;
},
},
//如果安装了npm install pinia-plugin-persistedstate --save持久化插件就可根据需要配置
// persist:true,//全部持久化
persist: {
//部分持久化
key: "globalStore", //缓存key
storage: window.sessionStorage, //缓存方式
// 部分持久化状态的点符号路径数组,默认持久化所有数据
paths: ["projectName"], //持久化字段
},
});
【src/store/index.ts】
import { createPinia } from "pinia";
import { App } from "vue";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; //持久化插件
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate); //使用插件持久化存储状态
export function setupStore(app: App<Element>) {
app.use(pinia);
}
export default pinia;
(3)注册store
(4)使用store
(5)store持久化(使用插件)
安装持久化插件
npm install pinia-plugin-persistedstate --save
【src/store/index.ts】
【src/store/globalStore.ts】
(6)store持久化(不使用插件,手动设置持久化)
示例一
示例二
(7)pinia 知识点
修改state的值的方式有
import { useGlobalStore } from "@/store/globalStore";
const globalStore = useGlobalStore();
//方法一,使用$patch代理
globalStore.$patch((state) => {
state.projectName = "测试";
});
//方法二
globalStore.projectName = "测试";
//方法三
globalStore.$state.projectName = "测试";
//方法四
globalStore.$state = {
projectName: "测试",
};
//方法五:在actions内定义一个修改state中属性的方法,然后于组件内部调用该方法即可
globalStore.setProjectName("测试");
store解构
保留响应式解构 使用 storeToRefs
失去响应式的解构
$reset()
重置state内所有属性(即恢复到store中设置的最初值)
import { useGlobalStore } from "@/store/globalStore";
const globalStore = useGlobalStore();
globalStore.$reset();
$subscribe()
返回一个函数,函数默认有两个形参;
检测store对象中任何一个属性的变动,变动转化成对象存储在形参args里,state顾名思义就是存储变化后的state
import { useGlobalStore } from "@/store/globalStore";
const globalStore = useGlobalStore();
globalStore.setProjectName("付裕");
globalStore.$subscribe((args, state) => {
console.log(args);
console.log(state);
});
Vue2的传值方式
1、props/emits 通信
(1)父传子
【parent.vue】
【son.vue】
(2)子传父
【son.vue】
【parent.vue】
(3)父子组件双向绑定更新
方式一:v-mode + value + input 的方式
父组件传入的v-model 和 子组件this.$emit(“input”, this.show);配合使用,使得子组件改变值时,父组件能及时更新,props的value,专指父组件v-model传入进来的值
子组件的props中的value可以在初始化时收到父组件传过来的值,但父组件改变值时,子组件无法知道,所以子组件需要设置一个watch监听,可以让父组件改变时,子组件能及时更新。
【parent.vue】
【son.vue】
效果:点击父组件的按钮或子组件的按钮都能同步更新值
方式二:变量.sync + update: 变量 的方式
父组件传入的.sync 和 子组件this.$emit(“update:show”, this.newShow);;配合使用,使得子组件改变值时,父组件能及时更新
子组件的props中的show可以在初始化时收到父组件传过来的值,但父组件改变值时,子组件无法知道,所以子组件需要设置一个watch监听,可以让父组件改变时,子组件能及时更新。
【parent.vue】
【son.vue】
效果:点击父组件的按钮或子组件的按钮都能同步更新值
2、$attrs / $listeners 通信
(1)$attrs 父传下层
$attrs里面包含着上层组件传递的所有数据(除style和class)
当组件(子组件)声明了prop时候,attrs里面包含上层组件传递的所有数据除去prop里面的数据剩下的数据
【parent.vue】
【son.vue】
效果
(2)v-bind=“$attrs” 和 inheriAttrs:false的使用
默认inheriAttrs:true,自动继承$attrs剩下的属性到当前组件(子组件)根节点
例如上面的案例,剩下age属性,就默认继承显示在子组件的根节点上
【son.vue】
效果
(3)$listeners 子传父,父调用子组件的事件
【son.vue】
【parent.vue】
效果
3、provide/inject 通信:父传下层
provide:Object | () => Object
inject:Array | { [key: string]: string | Symbol | Object }
provide:提供依赖``是一个对象,或者是一个返回对象的函数。里面包含要给子孙后代的属性和属性值。
inject: 注入依赖一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。
- 祖先组件不需要知道哪些后代组件需要使用它提供的属性
- 后代组件不需要知道被注入的属性来自哪里
注意:provide 是一个对象时,是非响应式,provide是返回对象的函数就是可响应式的。
非响应式 示例
【parent.vue】
【son.vue】
效果
当父组件修改this.msg的值,子组件接收的注入值并不会被改变,仍是原来的值,这里就不贴代码了。
响应式 示例
(1)provide传递的参数用一个方法返回
个人理解:函数的引用地址相同,所以是可响应的
【parent.vue】
【son.vue】
效果
点击父组件的按钮改变值,子组件接收的注入值也会被改变
(2)provide传递的参数定义成一个对象
个人理解:原理是data()返回obj的引用地址,provide注入又提供了这个obj的引用地址,所以子组件注入时是可响应的,引用地址相同。
【parent.vue】
【son.vue】
效果
点击父组件的按钮改变值,子组件接收的注入值也会被改变
4、ref
ref:如果在普通的 dom 元素上使用,引用指向的就是 dom实例;如果用在子组件上,引用就指向组件实例
注意:ref要等组件挂载之后才能使用
挂载顺序:父beforeCreate > 父created > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > 父mounted
【parent.vue】
效果
5、$parent 和 $children
$parent 示例
【parent.vue】
打印结果
$children 示例
【son.vue】
打印结果
6、插槽
(1)默认插槽
无默认内容
【son.vue】
【parent.vue】
【parent.vue】还可以这样写
//写法1
<Son> 666 </Son>
//写法2
<Son>
<template v-slot:default> 666 </template>
</Son>
//写法3
<Son>
<template #default> 666 </template>
</Son>
效果
有默认内容
【son.vue】
【parent.vue】父组件使用子组件什么也不传
效果
【parent.vue】父组件传入了默认插槽的内容,就会替换子组件内部的默认内容
效果
(2)具名插槽
具名插槽-不传值
【son.vue】设置了一个名为content的插槽
【parent.vue】
效果
具名插槽-传值
【son.vue】设置了一个名为content的插槽,并传了一个值msg
【parent.vue】
【parent.vue】还可以这样写
//解构写法1:
<Son>
<template #content="{ msg }">
接收值:{{ msg }}
</template>
</Son>
//解构写法2:
<Son>
<template v-slot:content="{ msg }">
接收值:{{ msg }}
</template>
</Son>
//不解构写法1:
<Son>
<template #content="contentProps">
接收值:{{ contentProps.msg }}
</template>
</Son>
//不解构写法2:
<Son>
<template v-slot:content="contentProps">
接收值:{{ contentProps.msg }}
</template>
</Son>
效果
(3)动态插槽名
【son.vue】
【parent.vue】
效果
7、(bus):$emit / $on
注意:
on一定要先于emit之前,要不然浏览器的事件注册队列没有相应的触发器。当你emit抛出方法时,页面没有该方法的事件监听,所以导致你无法获取。
复习一下组件挂载顺序:父beforeCreate > 父created > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > 父mounted
使用方式有两种。
方式一:挂载全局
【main.ts】挂载在全局
【son.vue】注册事件,并接收值
【parent.vue】发布事件、注销事件
效果
方式二:提供统一的方法
新建文件【src/utils/bus.js】
【son.vue】注册事件,并接收值
【parent.vue】发布事件、卸载事件
效果
8、Vuex
vuex就是专门为vue.js应用程序开发的状态管理模式,可以帮助我们管理共享状态,也就是管理全局变量。
Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。
vuex使用一个store对象管理应用状态,一个store包括:
- state:状态管理的数据源
- getter:将state过滤后输出
- mutations:是vuex中改变state的唯一途径,并且只能同步操作
- actions:一些对于state的异步操作可以放在action中,并通过在action提交mutations来变更状态。
【使用Action,为了方便devtools打个快照存下来,方便管理维护。所以说这个只是规范,而不是逻辑的不允许,只是为了让这个工具能够追踪数据变化而已】- module:当store对象过于庞大时,可以根据具体的业务需求分为多个module,模块化vuex,可以让每一个模块拥有自己的state、mutations、actions、getters,使得结构非常清晰,方便管理。
(1)安装
安装vuex指定版本:npm install vuex@3 -S
(2)使用Vuex
【1】创建一个store【src/store/globalStore.js】
【2】全局注册store
【src/main.js】
【3】使用store
1、调用state状态
(1)在标签中调用 state
<h1>{{ $store.state.token }}</h1>
(2)在js中调用 state
console.log(this.$store.state.token);
(3)使用辅助函数mapState + computed属性
2、使用getter获取状态
(1)在标签中调用 getters
<h1>{{ $store.getters.getToken }}</h1>
(2)在js中调用 getters
console.log(this.$store.getters.getToken);
(3)使用辅助函数mapGetters + computed属性
3、调用Mutations 变更状态
(1)在标签中调用 mutations
<Button @click="$store.commit('setToken', '222')">点击我,改变Token</Button>
(2)在js中调用 mutations
this.$store.commit("setToken", "222");
(3)使用辅助函数mapMutations属性
4、调用Actions
(1)在标签中调用 actions
<Button @click="$store.dispatch('asyncSetToken', '222')">点击我,改变Token</Button>
(2)在js中调用 actions
this.$store.dispatch("asyncSetToken", "222");
(3)使用辅助函数mapActions属性
5、Modules 模块化vuex
当遇见大型项目时,数据量大,store就会显得很臃肿
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
(1)新建目录
【src/store/globalStore.js】注意是导出一个对象,不是new Vuex.Store({})
【src/store/routerStore.js】注意是导出一个对象,不是new Vuex.Store({})
【src/store/index.js】创建 new Vuex.Store({})
(2)注册store
【src/main.js】
(3)使用store
没有命名空间时:
state是局部的,调用时需要加上模块名称
getters、mutations、actions是全局的,不需要加上模块名称
####### (4)设置命名空间
当设置为命名空间时,使用都需要加上模块名称,如下图所示
(5)模块动态注册
可以使用 store.unregisterModule(moduleName) 来动态卸载模块。注意,你不能使用此方法卸载静态模块(即创建 store 时声明的模块)。
可以通过 store.hasModule(moduleName) 方法检查该模块是否已经被注册到 store。
这里就不举例啦
(3)持久化Vuex-persistedstate
- vuex优势: 相比sessionStorage,存储数据更安全,sessionStorage可以在控制台被看到。
- vuex劣势: 在F5刷新页面后,vuex会重新更新state,所以,存储的数据会丢失。
vuex可以进行全局的状态管理,但刷新后刷新后数据会消失,这是我们不愿意看到的。怎么解决呢?
我们可以结合本地存储做到数据持久化,也可以通过插件vuex-persistedstate,插件的原理其实也是结合了存储方式,只是统一的配置就不需要手动每次都写存储方法
安装
npm i vuex-persistedstate -S