详解几十行代码实现一个vue的状态管理


Posted in Javascript onJanuary 28, 2019

介绍

采用集中式存储管理应用的所有组件的状态, 就能实现组件间数据共享

实现

逻辑图

详解几十行代码实现一个vue的状态管理

从图上有两条线: Vue.use(vuec), 与 new Vuec.center(options)

第一条线Vue.use(vuec)安装插件

使用Vue.use(vuec)时, 会执行vuec的install方法,会注入参数Vue 所以vuec是这样的,

// index.js
import {Center, install} from './center'
export default {Center, install}

Center对象将实例化成center(下面再说),我们先看看install方法

// center.js
let Vue // 全局变量, 保存install里的Vue
export function install (_Vue) {
 if (!Vue) {
  _Vue.mixin({
   beforeCreate: applyMixin // 在beforeCreate钩子上混入applyMixin函数
  })
 }
 Vue = _Vue
}

install在Vue原型的beforeCreate混入applyMixin函数, 也就是说在生成每个Vue组件时,在它的生命周期beforeCreate钩子上就会执行applyMixin方法

第二条线 new Vuec.center(options)实例化Center对象

先看看用户传入的options, 下面例子

export default new Vuec.Center({
 state: {
  name: 'liuyang'
 },
 mutations: {
  changeName (state) {
   state.name = 'jike'
  }
 }
})

上面代码会生成center实例, 该实例上应该包括:state状态,commit方法提交变更等

// center.js
export class Center {
 constructor (options= {}) {
  let center = this
  this.mutations = options.mutations
  observeState(center, options.state)
 }
 get state () { // 代理了this.$center.state的最终访问值
  return this._vm.$data.$$state
 }
 commit (_type, _payload) {
  this.mutations[_type](this.state, _payload)
 }
}
function observeState(center, state) { // 响应式state
 center._vm = new Vue({
  data: {
   $$state: state
  }
 })
}

在执行new Vuec.Center({..})时,就是执行Center的构造函数

首先执行let center = this, 定义center保存当前实例

接着执行this.mutations = options.mutations, 在实例center上添加mutations属性, 值就是用户输入mutations,

按上面例子, this.mutations长成这样

this.mutations = {
  changeName (state) {
   state.name = 'jike'
  }
}

最后执行observeState(center, options.state), 作用:让center实例的state属性指向options.state并且是响应式的

function observeState(center, state) { // 响应式state
   center._vm = new Vue({ // 利用Vue的响应系统,将state转化成响应式
    data: {
     $$state: state
    }
   })
  }

在center实例上添加_vm属性, 值是一个Vue实例, 在该Vue实例的data下定义了$$state, 它的值是options.state用户输入的state; 结合上面的这段代码

// center.js
export class Center {
 ...省略
 get state () { // 代理了this.$center.state的最终访问值
  return this._vm.$data.$$state
 }
 ...省略
}

所以我们在组件中访问center.state其实就是访问center._vm.$data.$$state

OK, center就构建好了

创建Vue组件

用户输入

import Vue from 'vue'
import App from './App'
import router from './router'
import center from './center'

new Vue({
 el: '#app',
 router,
 center, // 构建好的center实例
 template: '<App/>',
 components: {App}
})

在beforeCreate生命周期时会触发上面混入的applyMixin函数

// mixins.js
export default function applyMixin() {
 vuecInit.call(this) // 
}

function vuecInit () {
 const options = this.$options
 // vue的实例化是从外往内, 所以父组件的$center一定是options的center
 this.$center = options.parent?options.parent.$center: options.center
}

applyMixin里会执行vuecInit.call(this), 这里的this指向当前组件的实例,

接着看vuecInit, 定义了options等于用户输入选项,因为先创建根组件, 所以根组件this.$center的值的引用就是我们在new Vue({..center})时传入的center实例, 下面所有组件都指向它

OK, 你就可以在组件里使用this.$center访问了

commit变更

// center.js
export class Center {
 ... 省略
 commit (_type, _payload) {
  this.mutations[_type](this.state, _payload)
 }
}

通常我们变更时: this.$center.commit('changeName', 'jike'), 这样的话, this.mutations[_type]就是对应方法函数, 往该函数里传入state以及payload,

举上面的例子

// this.mutations[_type] , _type = 'changeName', payload= 'jike'
this.mutations = {
  changeName (state, payload) {
   state.name = payload
  }
}

说明

上面只是一个简单的状态管理, 还有很多地方没有实现: actions异步变更,getters函数,modules模块分割, 辅助函数mapState..等

源码地址: github

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery select(列表)的操作(取值/赋值)
Aug 06 Javascript
利用webqq协议使用python登录qq发消息源码参考
Apr 08 Javascript
js获取location.href的参数实例代码
Aug 02 Javascript
IE中图片的onload事件无效问题和解决方法
Jun 06 Javascript
javascript实现确定和取消提示框效果
Jul 10 Javascript
JS中BOM相关知识点总结(必看篇)
Nov 22 Javascript
火狐和ie下获取javascript 获取event的方法(推荐)
Nov 26 Javascript
微信小程序--onShareAppMessage分享参数用处(页面分享)
Apr 18 Javascript
详解JavaScript的变量
Apr 04 Javascript
vue实现路由懒加载及组件懒加载的方式
Jun 11 Javascript
vue elementUI使用tabs与导航栏联动
Jun 21 Javascript
JS操作字符串转数字的常见方法示例
Oct 29 Javascript
vue.js仿hover效果的实现方法示例
Jan 28 #Javascript
vue-for循环嵌套操作示例
Jan 28 #Javascript
使用pm2自动化部署node项目的方法步骤
Jan 28 #Javascript
jQuery访问json文件中数据的方法示例
Jan 28 #jQuery
JS实现的点击按钮图片上下滚动效果示例
Jan 28 #Javascript
vue-cli3 项目从搭建优化到docker部署的方法
Jan 28 #Javascript
JavaScript 判断iPhone X Series机型的方法
Jan 28 #Javascript
You might like
解决MySQL中文输出变成问号的问题
2008/06/05 PHP
php split汉字
2009/06/05 PHP
有关phpmailer的详细介绍及使用方法
2013/01/28 PHP
php 模拟POST提交的2种方法详解
2013/06/17 PHP
深入Apache与Nginx的优缺点比较详解
2013/06/17 PHP
ThinkPHP中Widget扩展的两种写法及调用方法详解
2017/05/04 PHP
Laravel5.7 数据库操作迁移的实现方法
2019/04/12 PHP
Prototype使用指南之string.js
2007/01/10 Javascript
用JS提交参数创建form表单在FireFox中遇到的问题
2013/01/16 Javascript
解决jquery submit()提交表单提示:f[s] is not a function
2013/01/23 Javascript
HTML长文本截取含有HTML代码同样适用的两种方法
2013/07/31 Javascript
js获取事件源及触发该事件的对象
2013/10/24 Javascript
javascript 闭包详解
2015/02/15 Javascript
jQuery实现简单二级下拉菜单
2015/04/12 Javascript
AngularJS 最常用的功能汇总
2016/02/17 Javascript
解决node.js安装包失败的几种方法
2016/09/02 Javascript
Vue按需加载的具体实现
2017/12/02 Javascript
原生js实现省市区三级联动代码分享
2018/02/12 Javascript
js实现鼠标单击Tab表单切换效果
2018/05/16 Javascript
使用Nuxt.js改造已有项目的方法
2018/08/07 Javascript
微信小程序mpvue点击按钮获取button值的方法
2019/05/29 Javascript
Element InputNumber 计数器的实现示例
2020/08/03 Javascript
[00:34]TI7不朽珍藏III——地穴编织者不朽展示
2017/07/15 DOTA
[00:48]食人魔魔法师至宝“金鹏之幸”全新模型和自定义特效展示
2019/12/19 DOTA
[43:58]DOTA2-DPC中国联赛定级赛 LBZS vs SAG BO3第一场 1月8日
2021/03/11 DOTA
[01:07:15]DOTA2-DPC中国联赛 正赛 DLG vs XG BO3 第二场 1月25日
2021/03/11 DOTA
python模拟新浪微博登陆功能(新浪微博爬虫)
2013/12/24 Python
Python3 利用requests 库进行post携带账号密码请求数据的方法
2018/10/26 Python
python3 实现函数写文件路径的正确方法
2019/11/27 Python
django框架单表操作之增删改实例分析
2019/12/16 Python
Python爬虫破解登陆哔哩哔哩的方法
2020/11/17 Python
英国领先的野生鸟类食品供应商:GardenBird
2018/08/09 全球购物
俄罗斯电子产品在线商店:UltraTrade
2020/01/30 全球购物
导游词开场白
2015/01/31 职场文书
2016幼儿园中班开学寄语
2015/12/03 职场文书
试了下Golang实现try catch的方法
2021/07/01 Golang