Vuex
-
本质上使用Vuex的原因其实是组件之间的数据交互过于复杂,重点在于当组件过多时,数据的传递就会不可控,所以引入 Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式。它采用集中式管理应用的所有组件的状态,并以相应 的规则保证状态以一种可预测的方式发生变化。
-
回顾数据传递方式:props , 自定义事件$emit(.sync) , EventBus , $parent , $root,vuex 。
组件较少的情况下传递数据可以使用EventBus ; 组件特别多,而且对数据要求可预测改变就应该使用Vuex。开发大型单页面应用时用Vuex,简单的SPA不要使用。 -
代码执行流程:
- 创建store
- 创建state
- 创建mutations
- 创建actions
- 创建modules
-
操作store
- 读取state
快捷方案:computed --> state getter - 设置state(修改)
快捷方案:methods -->mutations actions
- 读取state
-
表单相关
表单操作:
1. 使用 v-model 解决方案:get 和 set
2. 不使用 v-model 解决方案:通过事件 -
边缘化操作
- 项目结构
- 插件
- 严格模式 =》安全的操作方式
- 热重载:方便开发人员调试
Vuex核心知识点
Store(仓库):一个项目只能有一个store仓库
State(状态):数据
Mutation(计算方式):只能通过此方法修改state状态
Action(动作):异步操作的核心
Getter(读取数据的方案):可选项,读取的容错方式
Module(模块):分解state,避免state过多引起臃肿
Vuex执行流程:VueComponent —dispatch—> Actions —commit—> Mutations —mutate—> state —render—> VueComponent
安装并引入
javascript">npm install --save es6-promise(Vuex依赖于es-promise)
npm install --save vuex
import 'es6-promise/auto'
import Vuex from 'vuex'
Vue.use(Vuex)
Vuex和全局对象的区别
全局对象:Vue.prototype.$api = api
; //此时给两个页面分别通过
this.$api
都使用一次,然后改变其中一个页面的$api
的值,另一个组件的值不会改变。
Vuex : this.$store.state.对象名.key 如果其中一个改变则另外一个响应的也改变。
创建仓库
javascript">创建一个store文件夹,创建index.js并配置
const store = new Vuex.Store({
state : {
obj : {
name : "apple",
pic : 0
}
}
})
然后挂载到 new Vue({ //主入口文件main.js
render : h => h(App),
store
}).$mount('#app')
显示 :产品:{{ this.$store.state.obj.name }}
价格:{{ this.$store.state.obj.pic }}
分离 store文件:src下创建一个 store,里边创建 index.js引入配置导出,main.js引入使用。
State快捷读取方法
mapState辅助函数:
javascript">import { mapState } from 'vuex'
写法1 :computed : mapState({
msg : state => state.obj
})
写法2:computed : {
...mapState(["obj","...",,,]) //必须是字符串
}
Mutation
更改 Vuex 中的 store 中的状态的唯一方法是提交 mutation。
javascript">在 index.js 中配置, 和 state 同级配置:
mutations:{
increment(state){ //参数state指的就是同级的state
state.obj.pic++
},
decrement(state){
state.obj.pic --
}
javascript">在组件中配置:
<button @click="addHandle">增加</button>
<button @click="minHandle">减少</button>
addHandle(){
this.$store.commit("increment")
},
minHandle(){
this.$store.commit("decrement")
}
提交载荷(Payload)
javascript">在index.js中配置, 和 state 同级配置:
mutations:{
// increment(state,n){ //写法1
// state.obj.pic+=n
// },
increment(state,payload){ //写法2
state.obj.pic += payload.num
},
decrement(state,{ num }){ //写法3:es6解构赋值
state.obj.pic -= num
}
javascript">在组件中配置:
<button @click="addHandle">增加</button>
<button @click="minHandle">减少</button>
<input placeholder="请输入" type="text" v-model="num">
methods:{
addHandle(){
// this.$store.commit("increment",parseInt(this.num)) //写法1
this.$store.commit("increment",{ //写法2
num : parseInt(this.num)
})
this.$store.commit({ //写法3
type:"increment",
num : parseInt(this.num)
})
},
minHandle(){
// this.$store.commit("decrement",parseInt(this.num))
this.$store.commit("decrement",{
num : parseInt(this.num)
})
}
}
Mutation遵循Vue的响应规则
就和数组,对象的更新检测一个意思
例如:需求要给state仓库添加一个值,同时页面也要显示
javascript">在vuex.js中配置, 和 state 同级配置:
mutatios : {
xiangying(state,payload){
// state.obj.aa = payload.msg;//仓库加进去了但是页面不显示,和数组对象更新检测同理
Vue.set(state.obj,"aa",payload.msg);//成功
}
}
javascript">在组件中配置:
aa:{{ this.$store.state.obj.aa }}
data(){ return{ msg : "我是新添加的产品" }}
methods:{
tianjia(){
this.$store.commit("xiangying",{
msg : this.msg
})
}
}
-
以新对象替换老对象
利用对象展开运算符我们可以这样写:
state.obj = { …state.obj, newProp: 123 } -
使用常量替代 Mutation 事件类型
在vuex文件夹下创建一个 mutation-type.js 文件,导出
export const INCREMENT = “INCREMENT”;
export const DECREMENT = “DECRMENT”;
随后在vue.js和组件使用vuex的地方,引入并把字符串“increment”"decrement"替换掉
import { INCREMENT,DECREMENT } from ‘./mutation-type’
Mutation必须是同步函数(重点)
待补充。。。
在组件中提交Mutation(快捷方案)
javascript">import { mapMutations } from 'vuex'
methods:{
...mapMutations([INCREMENT,DECREMENT,"xiangying"]),//...是es6高级语法,这里的这个包 core-js 经常丢包,重装一下就行。
addHandle(){
// this.$store.commit({ //写法3
// type:INCREMENT,
// num : parseInt(this.num)
// })
this[INCREMENT]({ num : parseInt(this.num) }) //写法4
},
minHandle(){
// this.$store.commit(DECREMENT,{
// num : parseInt(this.num)
// })
this[DECREMENT]({ num : parseInt(this.num) })
},
tianjia(){
// this.$store.commit("xiangying",{
// msg : this.msg
// })
this.xiangying({ msg:this.msg })
}
}
Action
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作,有异步操作时action才有意义。
javascript">index.js中配置
const store = new Vuex.Store({
state : {
banner : { } //定义一个存储仓库
},
mutations : {
setBanner(state,payload){
//异步请求数据
state.banner = payload.banner; //把参数的值赋值到仓库
}
},
actions : {
asyncBanner(context,url){ //定义一个异步请求方法,参数context固定,url地址
axios.get(url).then(res=>{
context.commit("setBanner",res.data);//通过context.commit提交一个mutation
})
}
}
})
javascript">组件中配置:
<button @click="asyncAction">action提交异步请求</button>
<ul>
<li v-for="(item,index) in banner" :key="index">{{item.title}}</li>
</ul>
import { mapState } from 'vuex'
computed:mapState({
banner : state => state.banner //将仓库中的banner存放到变量banner中
//...mapState([ "banner" ]) //写法2
}),
methods:{
asyncAction(){ //定义一个方法
this.$store.dispatch("asyncBanner","http://iwenwiki.com/api/blueberrypai/getIndexBanner.php"); //通过dispatch分发action
}
}
在组件中分发action
javascript"> 组件中配置:
import { mapState,mapActions } from 'vuex'
methods:{
...mapActions(["asyncBanner"]), //写法2
asyncAction(){
// this.$store.dispatch("asyncBanner","http://iwenwiki.com/api/blueberrypai/getIndexBanner.php");//写法1
this.asyncBanner("http://iwenwiki.com/api/blueberrypai/getIndexBanner.php"); //写法2
}
}
Getter
对store中的state的状态的验证修改
- 需求:假设产品价格最高不超过100,最低不低于1
javascript">index.js配置:
const store = new Vuex.Store({
getters:{
getPic(state){
if(state.obj.pic > 100 || state.obj.pic <= 0){
return "价格出错了"
}else{
return state.obj.pic;
}
}
}
})
在组件中读取 :{{ this.$store.getters.getPic }}
- 通过属性访问
javascript">index.js配置:
const store = new Vuex.Store({
getters:{
getPic(state,getter){
console.log(getter);
if(state.obj.pic > 100 || state.obj.pic <= 0){
// return "价格出错了"; //方法1
return getter.getHello; //方法2
}else{
return state.obj.pic;
}
},
getHello(){
return "价格出错了";
}
}
})
- 通过方法访问
- mapGetters 辅助函数
javascript">组件读取:
import { mapGetters } from 'vuex'
computed : mapState({
...mapGetters(["getPic"])
})
<div> 价格:{{ getPic }} </div>
项目结构
├── index.html
├── main.js
├── api
│ └── … # 抽取出API请求
├── components
│ ├── App.vue
│ └── …
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
Module
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
javascript">// 创建Store仓库
const store = new Vuex.Store({
state : defaultStore,
mutations : defaultMutations,
actions : defaultActions,
getters : defaultGetters,
modules:{
//拥有完整的vuex结构
moduleA : {
namespaced : true, //命名空间
state : {
msg : "我是moduleA的"
},
mutations : {
},
actions : {
},
getters : {
}
},
moduleB : {
namespaced:true,
state : {
msg : "我是moduleB的"
},
.....
},
// moduleC....
}
})
javascript"> 读取moduleA 和 moduleB 的值:
方式1直接读取 : {{ this.$store.state.moduleA.msg }}
读取方式2 : {{ msg }}--{{str}}
import { mapState } from 'vuex'
computed:{ //计算属性
...mapState("moduleA",{ //这种读取方式必需设置命名空间 namespaced:true 才能读到
msg : state => state.msg
}),
...mapState("moduleB",{
str : state => state.msg
})
}
Vuex动态模块
可以使用 store.registerModule 动态创建,使用 store.unregisterModule(模块名) 来动态卸载模块,使用 store.hasModule(moduleName) 方法检查该模块是否已经被注册到 store。
插件
Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数:
javascript">在index.js中配置:
const myPlugin = store =>{
//当store初始化后调用
store.subscribe((mutation,state)=>{
console.log( mutation , state);
})
}
// 在Store仓库引用
const store = new Vuex.Store({
plugins : [myPlugin , ......],
})
严格模式
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。然而在mounted生命周期函数中和标签中直接用 = 赋值,都可以改变它的值。为了解决这个问题,严格模式由此而生,仅需在创建 store 的时候传入 strict : true ,在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
javascript">const store = new Vuex.Store({
strict : true
// ...
})
【注意】:不要在发布环境下启用严格模式!请确保在发布环境下关闭严格模式,以避免性能损失。类似于插件,我们可以让构建工具来处理这种情况 strict : process.env.NODE_ENV !== 'production’
javascript">const store = new Vuex.Store({
// ...
//直接写这种就行,自动识别
strict : process.env.NODE_ENV !== 'production'
})
表单处理
建议使用v-model模式
Vuex热重载
方便开发阶段调试
-
热更新:浏览器自动刷新 webpack(我们在开发阶段在浏览器控制台添加调试的都会重置)
-
热重载:浏览器不刷新,但是数据要更新(不会重置)