Before
在 Vue 中做数据的集中式状态管理首先想到的是 Vuex, 这是官方指定的状态管理模式。但是如果不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。(该描述出自 Vuex 官网描述) Vuex 官网文档
状态管理的本质是数据共享,也可以用这种思路实现组件通信,这里简单的探索一下如何实现一简单数据共享的 Store。
基本思路:定义一个全局可访问的 store 对象,该对象中维护了一可供访问的属性state, 并且提供了一些可以操作state的方法,简单来说这就实现了一个超级简单的数据共享。
定义 Store.js
import Vue from "vue";
class Store {
constructor(){
this.state ={
arr:[],
count:0
}
}
// 添加
addItem = (item) => {
this.state.arr.unshift(item)
}
// 删除
delItem = () => {
this.state.arr.pop()
}
// count++
increate = () => {
this.state.count++
}
}
const store = new Store()
store.install = () => {
Vue.prototype.$verySimpleStore = store
}
export default store
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 将 store 挂到 Vue原型
import store from "./assets/js/store";
Vue.use(store)
Vue.use(ElementUI);
new Vue({
render: h => h(App),
}).$mount('#app')
这里通过类实例化一个 store 对象,在其实例上声明state(注意是对象), addItem、delItem为对象上用来操作 arr数组 的增加与删除方法,increate 为给 count 递增的函数。通过Vue.use()将 store 对象挂在Vue的原型上,这样就可以实现该对象的全局访问(组件vc通过原型链获取 $verySimpleStore 属性)。如果对此不熟悉,传送门
案例 -- 使用Store
在根组件中引入vcA、vcB两个组件(简称A、B组件),在 A 组件中希望输入一段字符,将其存到数组 arr 中。在 B 组件中有一 删除按钮和一个点击递增的按钮,点击删除数组 arr 中最后一个元素,或者增加count值。
APP组件
<template>
<div id="app">
<div class="box">
<button @click="test">click me to show the state of Store</button>
<vcA></vcA>
<vcB></vcB>
<ul>
<!-- state.arr 或者 $verySimpleStore.state.arr 都会被响应式监听 -->
<li v-for="(item,index) in state.arr" :key="index"> {{item}} </li>
</ul>
count: {{ state.count }}
</div>
</div>
</template>
<script>
import vcA from "./components/vcA.vue";
import vcB from "./components/vcB.vue";
export default {
name: 'App',
components:{
vcA,
vcB
},
mounted(){
},
data() {
return {
state:this.$verySimpleStore.state
}
},
methods: {
test(){
console.log('Store',this.$verySimpleStore.state);
}
},
}
</script>
A组件
<template>
<div>
<input type="input" @keyup.enter="enterValue">
</div>
</template>
<script>
export default {
methods:{
enterValue(e){
let per = {name:e.target.value,age:18}
this.$verySimpleStore.addItem(per)
e.target.value = ''
}
},
}
B组件
<template>
<div ref="vcbBox">
<button @click="delBtn">click me to del</button>
<button @click=" $verySimpleStore.increate() ">click me to count++</button>
</div>
</template>
<script>
export default {
methods:{
delBtn(){
this.$verySimpleStore.delItem()
},
},
}
</script>
这样一来,一个超级简单的状态管理 Store 基本就实现了。
数据响应式变化?
在 App 中将其赋值给 data 中的 state 属性,这样做目的是为了给 Store 中 state 增加响应式。如果没有这一步,页面不会监听到数据变化,也不会发生修改。
如果不做这一步,我们怎么来实现Store的响应式状态?
在Vue 中,数据经过了深层次侦听,故当其变化时可以动态更新页面(数组的变化通过改写七种方式来实现,注意,这只是针对Vue2)。于是我们可以这样改造一下代码。
改造Store.js
// state 不再指向一对象,而是指向了一个 vm 实例,data 中 state 里面的属性会代理到this.state(即vm)身上,从而实现数据侦听
constructor(options){
this.state = new Vue({
data:options.state
})
}
// 实例化一个 store 将 options 传入
const store = new Store({
state: {
arr:[],
count:0
}
})
在组件中,无需通过data来引用,直接调用Store中state状态。
改写App组件
<template>
<div id="app">
<div class="box">
<button @click="test">click me to show the state of Store</button>
<vcA></vcA>
<vcB></vcB>
<ul>
<li v-for="(item,index) in $verySimpleStore.state.arr" :key="index"> {{item}</li>
</ul>
count: {{ $verySimpleStore.state.count }}
</div>
</div>
</template>
<script>
import vcA from "./components/vcA.vue";
import vcB from "./components/vcB.vue";
export default {
name: 'App',
components:{
vcA,
vcB
},
mounted(){
},
data() {
return {
// state:this.$verySimpleStore.state
}
},
methods: {
test(){
console.log('Store',this.$verySimpleStore.state);
}
},
}
</script>
以上便是一个简单的状态管理Store的简单实现。
参考来源: