Vuex的基本框架
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})
========================================
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App)
}).$mount('#app')
由基本使用可知
- Vuex.Store是个类,参数传递给构造函数
- 并且Vuex暴露的对象中有install方法交给Vue.use调用,则暴露的对象格式为Vuex={Store,install}
- 通过Vue.use(Vuex) 使得每个组件都可以拥有store实例
从而得出:
class Store{
}
let install = function(){
}
let Vuex = {
Store,
install
}
export default Vuex
实现Vue.use
- 如果插件第一个参数是一个对象,必须提供install方法。如果插件是一个函数,它会被作为install方法。调用install方法时,会将Vue作为参数传入。install方法被同一个插件多次调用时,插件也只会被安装一次。
Vue.use = function(plugin){
//已注册的的插件列表
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
//通过indexOf判断传入的插件是否已经注册,已经注册直接返回终止
if(installedPlugins.indexOf(plugin)>-1){
return this;
}
//进行参数的处理
//toArray使用的是类数组的转换,和获取传给use第一个参数以外的参数(第一个参数为对象或函数)
const args = toArray(arguments,1);
//将Vue实例放在参数首部
args.unshift(this);
//调用方式
if(typeof plugin.install === 'function'){
plugin.install.apply(plugin,args);
}else if(typeof plugin === 'function'){
plugin.apply(null,args);
}
//放进插件列表
installedPlugins.push(plugin);
return this;
}
通过Vue.use(Vuex) 使得每个组件都可以拥有store实例,即完善Vuex暴露的install方法
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false;
//目前只有根组件有这个store值,而其他组件是还没有的,所以我们需要让其他组件也拥有这个store
new Vue({
store,
render: h => h(App)
}).$mount('#app');
- 通过Vue.mixin全局混入实现所有实例在beforeCreate前获取到store
- 如果判断当前组件是子组件的话,就将我们根组件的store也复制给子组件。注意是引用的复制,因此每个组件都拥有了同一个$store挂载在它身上。
let install = function(Vue){
Vue.mixin({
beforeCreate(){ //beforeCreate负责初始化$parent, $children等,create负责初始化props、data、methods、watch等
//this.$options获取Vue的配置选项
if (this.$options && this.$options.store){ // 如果是根组件
this._root = this; //把当前实例挂载到_root上
this.$store = this.$options.store //将$store放在Vue实例上
}else { //如果是子组件
this._root= this.$parent && this.$parent._root
this.$store = this.$parent && this.$parent.$store //获取上级的$store
}
}
})
}
为什么判断当前组件是子组件,就可以直接从父组件拿到$store
父组件和子组件的执行顺序:父beforeCreate-> 父created -> 父beforeMounte -> 子beforeCreate ->子create ->子beforeMount ->子 mounted -> 父mounted
在执行子组件的beforeCreate的时候,父组件已经执行完beforeCreate了,那就能获取到父组件上的$store了
实现Vuex的state
<p>{{this.$store.state.num}}</p>
- 由调用形式可知,state是作为实例的属性
class Store{
constructor(options){
this.state = options.state || {}
}
}
state里的值变成响应式
- 直接通过new Vue传入data,通过Vue实现响应式
class Store{
constructor(options) {
this.vm = new Vue({
data:{
state:options.state
}
})
}
//通过getter,实现格式调用一致
get state(){
return this.vm.state
}
}
实现getter
- 调用方式和计算属性类似,第一个参数为state中的内容
class Store{
constructor(options) {
this.vm = new Vue({
data:{
state:options.state
}
})
// 新增代码
let getters = options.getter || {}
this.getters = {}
//通过Object.defineProperty进行劫持,通过get进行方法的调用,并传入state
Object.keys(getters).forEach(getterName=>{
Object.defineProperty(this.getters,getterName,{
get:()=>{
return getters[getterName](this.state)
}
})
})
}
get state(){
return this.vm.state
}
}
实现mutation
- 通过this.$store.commit(‘方法名’,参数)进行调用
- 每个mutation接受state和传入的参数
class Store{
constructor(options) {
this.vm = new Vue({
data:{
state:options.state
}
})
let getters = options.getter || {}
this.getters = {}
Object.keys(getters).forEach(getterName=>{
Object.defineProperty(this.getters,getterName,{
get:()=>{
return getters[getterName](this.state)
}
})
})
//新增代码
let mutations = options.mutations || {}
this.mutations = {}
//进行mutations的存储
Object.keys(mutations).forEach(mutationName=>{
this.mutations[mutationName] = (arg)=> {
mutations[mutationName](this.state,arg)
}
})
}
get state(){
return this.vm.state
}
//新增代码
commit(method,arg){
this.mutations[method](arg)
}
}
实现actions
- 通过this.$store.dispatch(‘函数名’,参数)
- 每个action第一个参数是context上下文对象,即store实例,第二个参数是传入的参数
class Store{
constructor(options) {
this.vm = new Vue({
data:{
state:options.state
}
})
let getters = options.getter || {}
this.getters = {}
Object.keys(getters).forEach(getterName=>{
Object.defineProperty(this.getters,getterName,{
get:()=>{
return getters[getterName](this.state)
}
})
})
let mutations = options.mutations || {}
this.mutations = {}
Object.keys(mutations).forEach(mutationName=>{
this.mutations[mutationName] = (arg)=> {
mutations[mutationName](this.state,arg)
}
})
//新增代码
let actions = options.actions
this.actions = {}
Object.keys(actions).forEach(actionName=>{
this.actions[actionName] = (arg)=>{
//将store实例传入action
actions[actionName](this,arg)
}
})
}
// 新增代码
dispatch(method,arg){
this.actions[method](arg)
}
//需要修改的地方,因为在actions中的方法传入了当前store实例,当通过解构{commit}获得commit调用时,commit中的this会因为丢失调用上下文,绑定在window上
commit(method,arg){
this.mutations[method](arg)
}
//commit修改为箭头函数,this作用域为箭头函数所在的作用域的this,即当前class(class是es5中funtion的语法糖),进行this绑定
commit=(method,arg)=>{
this.mutations[method](arg)
}
get state(){
return this.vm.state
}
}
解构调用的方式
actions: {
asyncIncre({commit},arg){
setTimeout(()=>{
commit('incre',arg)
},1000)
}
},
state是响应式的,为何需要commit来修改state
vuex能够记录每一次state的变化记录,保存状态快照,实现时间漫游/回滚之类的操作。