Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

思考问题

  1. Vuex 只在更实例引入了,那么它是如何在每个子组件中都能使用的?
  2. Vuex 是一个插件,为什么更改了Vuex中的state,会触发视图的跟新?

vuex原理

vuex 是vue的状态管理工具,目的是为了更方便实现多个组件之间的状态共享

vuex的工作原理, Vuex 官方文档 

vuex实现

  1. 新建一个vuex文件,导出一个store对象

    let Vue;
    class Store { }
    const install = (_Vue) => {
    Vue = _Vue;
    } export default {
    // 这方法在use的时候默认会被调用
    install,
    Store
    }
  2. 使用混合,在创建组件之前,将vuex实例挂载到vue的实例上

    Vue.mixin({
    beforeCreate() {
    // 需要拿到store,给每个组件都增加 $store 属性
    // 为什么不直接给Vue.prototype 上增加?是因为可能会new 好多个Vue的实例,在别的实例上不需要store
    if (this.$options && this.$options.store) {
    this.$store = this.$options.store;
    } else {
    // 这里判断一下,如果单独创建了一个实例没有parent
    this.$store = this.$parent && this.$parent.$store;
    }
    }
    });
  3. 获取new Store传入的对象

    class Store {
    constructor(options = {}) {
    // 将用户的状态放入到 store 中
    this.state = options.state; // 获取计算属性
    let getters = options.getters;
    this.getters = {};
    Object.keys(getters).forEach(getterName => {
    Object.defineProperty(this.getters, getterName, {
    get: () => {
    return getters[getterName](this.state);
    }
    });
    });
    }
    }
  4. state的数据变成响应式的数据

    class Store {
    constructor(options = {}) {
    // Vuex 的核心,定义了响应式变化,数据更新之后更新视图
    this._vm = new Vue({
    data() {
    return {
    state: options.state
    };
    }
    });
    }
    // 类的属性访问器
    get state() {
    return this._vm.state;
    }
    }
  5. 通过触发 mutations 更改状态

    // 通过 this.commit() 触发更改
    mutations: {
    syncAdd(state, payload) {
    state.age += payload;
    }
    } // 通过发布订阅的模式
    class Store {
    constructor(options = {}) {
    let mutations = options.mutations;
    this.mutations = {};
    Object.keys(mutations).forEach(mutationName => {
    // 订阅所有的mutations
    this.mutations[mutationName] = (payload) => {
    // 内部的第一个参数是状态
    mutations[mutationName](this.state, payload);
    }
    });
    }
    // 提交更改,会在当前的 store 上找到对应的函数执行
    // 发布
    commit = (mutationName, payload) => { // 保证this
    this.mutations[mutationName](payload);
    }
    }
  6. 内部封装的 forEach, 减少重复代码

    const forEachValue = (obj, fn) => {
    Object.keys(obj).forEach(key => {
    fn(key, obj[key]);
    });
    }; // 对上面的 getters 改造下
    forEachValue(getters, (gettersName, fn) => {
    Object.defineProperty(this.getters, getterName, {
    get: () => {
    return fn(this.state);
    }
    });
    }); // 对上面的mutations 改造下
    forEachValue(mutations, (mutationName, fn) => {
    this.mutations[mutationName] = (payload) => {
    fn(this.state, payload);
    }
    })
  7. 通过触发 action 异步跟新转态

    action 异步提交更改,异步操作完之后提交到mutation中
    例:
    actions: {
    asyncMinus({ commit }, payload) {
    setTimeout(() => {
    commit('syncMinus', payload);
    }, 1000);
    }
    }
    mutations: {
    syncMinus(state, payload) {
    state.age -= payload;
    }
    } // 也是一个发布订阅模式
    class Store {
    constructor(options ={}) {
    let actions = options.actions;
    this.actions = {};
    forEachValue(actions, (actionName, fn) => {
    this.actions[actionName] = (payload) => {
    fn(this, payload);
    }
    });
    }
    dispatch = (actionName, payload) => {
    // 源码里有一个变量,来控制是否是通过mutation 来更新的转态,不是会抛个警告
    this.actions[actionName](payload);
    }

    vuex简单实现

    let Vue;
    const forEachValue = (obj = {}, fn) => {
    return Object.keys(obj || {}).forEach(key => {
    fn(key, obj[key]);
    })
    }
    class Store {
    constructor(options = {}) {
    this._vm = new Vue({
    data() {
    return {
    state: options.state
    }
    }
    }); let getters = options.getters;
    this.getters = {};
    forEachValue(getters, (getterName, fn) => {
    Object.defineProperty(this.getters, getterName, {
    get: () => {
    return fn(this.state);
    }
    });
    }); // mutations
    let mutations = options.mutations;
    this._mutations = {};
    // 订阅
    forEachValue(mutations, (mutationName, fn) => {
    this._mutations[mutationName] = (paylod) => {
    fn(this.state, paylod);
    }
    }); // actions
    let actions = options.actions;
    this._actions = {};
    forEachValue(actions, (actionName, fn) => {
    this._actions[actionName] = (paylod) => {
    fn(this, paylod);
    }
    });
    }
    // 发布
    commit = (mutationName, paylod) => {
    this._mutations[mutationName](paylod);
    }
    dispatch = (actionName, paylod) => {
    this._actions[actionName](paylod);
    }
    get state() {
    return this._vm.state;
    }
    }
    const install = (_Vue) => {
    Vue = _Vue;
    Vue.mixin({
    beforeCreate() {
    if (this.$options && this.$options.store) {
    this.$store = this.$options.store;
    } else {
    this.$store = this.$parent && this.$parent.$store;
    }
    }
    });
    } export default {
    install,
    Store
    };
  8. modules的实现

    主要是将mosules里面的数据格式化成我们想要的格式 js { _modules: { root: state: {__ob__: Observer} _children: {} _rawModule: {modules: {…}, state: {…}, getters: {…}, mutations: {…}, actions: {…} } }

    • 数据格式化
    // 在 Store 中定义modules
    this._modules = new ModuleCollection(options); // 把数据格式化成我们想要的结构 class ModuleCollection {
    constructor(options) { // 模块依赖的收集
    this.register([], options); // 注册模块,将模块注册成树结构
    }
    register(path, rootModule) {
    let module = { // 将模块格式化
    _rawModule: rootModule,
    _children: {},
    state: rootModule.state
    }
    if (path.length === 0) { // 如何是根模块 将这个模块挂载到根实例上
    this.root = module;
    } else {
    // 递归都用reduce方法, 通过_children 属性进行查找
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root._children[current];
    }, this.root);
    parent._children[path[path.length - 1]] = module;
    }
    // 看当前模块是否有modules
    if (rootModule.modules) { // 如果有modules 开始重新注册
    forEachValue(rootModule.modules, (moduleName, module) => {
    this.register(path.concat(moduleName), module);
    })
    }
    }
    }
    • 安装模块
    installModule(this, this.state, [], this._modules.root);
    
      // 安装模块
    const installModule = (store, rootState, path, rootModule) => {
    // 将state 挂载到根上
    if (path.length > 0) {
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root[current];
    }, rootState);
    // vue 不能再对象上增加不存在的属性,否则视图不会更新
    // parent.path[path.length - 1] = rootModule.state;
    Vue.set(parent, path[path.length - 1], rootModule.state);
    }
    // getters
    let getters = rootModule._rawModule.getters;
    if (getters) {
    forEachValue(getters, (getterName, fn) => {
    Object.defineProperty(store.getters, getterName, {
    get() {
    // 让getters 执行,将自己的状态传入
    return fn(rootModule.state); // 将对应的函数执行
    }
    });
    });
    }
    // mutations
    let mutations = rootModule._rawModule.mutations; // 拿到每个模块里的mutations
    if (mutations) {
    forEachValue(mutations, (mutationName, fn) => {
    let mutations = store._mutations[mutationName] || [];
    mutations.push((paylod) => {
    fn.call(store, rootModule.state, paylod);
    // 发布,让所有的订阅依次执行
    store._subscribes.forEach(fn => fn({ type: mutationName, paylod }, rootState))
    });
    store._mutations[mutationName] = mutations;
    });
    }
    // actions
    let actions = rootModule._rawModule.actions; // 拿到每个模块里的mutations
    if (actions) {
    forEachValue(actions, (actionName, fn) => {
    let actions = store._actions[actionName] || [];
    actions.push((paylod) => {
    fn.call(store, store, paylod);
    });
    store._actions[actionName] = actions;
    });
    }
    // 循环挂载儿子
    forEachValue(rootModule._children, (moduleName, module) => {
    installModule(store, rootState, path.concat(moduleName), module);
    });
    }

    vuex完整实现

    let Vue;
    const forEachValue = (obj = {}, fn) => {
    return Object.keys(obj || {}).forEach(key => {
    fn(key, obj[key]);
    });
    }
    class ModuleCollection {
    constructor(options) { // 模块依赖的收集
    this.register([], options); // 注册模块,将模块注册成树结构
    }
    register(path, rootModule) {
    let module = { // 将模块格式化
    _rawModule: rootModule,
    _children: {},
    state: rootModule.state
    }
    if (path.length === 0) { // 如何是根模块 将这个模块挂载到根实例上
    this.root = module;
    } else {
    // 递归都用reduce方法, 通过_children 属性进行查找
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root._children[current];
    }, this.root);
    parent._children[path[path.length - 1]] = module;
    }
    // 看当前模块是否有modules
    if (rootModule.modules) { // 如果有modules 开始重新注册
    forEachValue(rootModule.modules, (moduleName, module) => {
    this.register(path.concat(moduleName), module);
    })
    }
    }
    }
    // 安装模块
    const installModule = (store, rootState, path, rootModule) => {
    // 将state 挂载到根上
    if (path.length > 0) {
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root[current];
    }, rootState);
    // vue 不能再对象上增加不存在的属性,否则视图不会更新
    // parent.path[path.length - 1] = rootModule.state;
    Vue.set(parent, path[path.length - 1], rootModule.state);
    }
    // getters
    let getters = rootModule._rawModule.getters;
    if (getters) {
    forEachValue(getters, (getterName, fn) => {
    Object.defineProperty(store.getters, getterName, {
    get() {
    // 让getters 执行,将自己的状态传入
    return fn(rootModule.state); // 将对应的函数执行
    }
    });
    });
    }
    // mutations
    let mutations = rootModule._rawModule.mutations; // 拿到每个模块里的mutations
    if (mutations) {
    forEachValue(mutations, (mutationName, fn) => {
    let mutations = store._mutations[mutationName] || [];
    mutations.push((paylod) => {
    fn.call(store, rootModule.state, paylod);
    // 发布,让所有的订阅依次执行
    store._subscribes.forEach(fn => fn({ type: mutationName, paylod }, rootState))
    });
    store._mutations[mutationName] = mutations;
    });
    }
    // actions
    let actions = rootModule._rawModule.actions; // 拿到每个模块里的mutations
    if (actions) {
    forEachValue(actions, (actionName, fn) => {
    let actions = store._actions[actionName] || [];
    actions.push((paylod) => {
    fn.call(store, store, paylod);
    });
    store._actions[actionName] = actions;
    });
    }
    // 循环挂载儿子
    forEachValue(rootModule._children, (moduleName, module) => {
    installModule(store, rootState, path.concat(moduleName), module);
    });
    }
    class Store {
    constructor(options = {}) {
    this._vm = new Vue({
    data() {
    return {
    state: options.state
    }
    }
    });
    this.getters = {};
    this._mutations = {};
    this._actions = {};
    this._subscribes = [];
    this._modules = new ModuleCollection(options); // 把数据格式化成一个想要的数据结构 // 递归将结果分类
    // this 整个store
    // this.state 当前的根状态,把模块中的状态放在根上
    // [] 是为了递归的初始值
    // this._modules.root 是为了从跟模块开始安装
    installModule(this, this.state, [], this._modules.root); // plugins
    if (!Array.isArray(options.plugins)) {
    throw new TypeError('plugins is not Array');
    }
    options.plugins.forEach(fn => fn(this));
    }
    // 发布
    commit = (mutationName, paylod) => {
    this._mutations[mutationName].forEach(fn => fn(paylod));
    }
    dispatch = (actionName, paylod) => {
    this._actions[actionName].forEach(fn => fn(paylod));
    }
    // 订阅所有的plugins
    subscribe = (fn) => {
    this._subscribes.push(fn);
    }
    get state() {
    return this._vm.state;
    }
    }
    const install = (_Vue) => {
    Vue = _Vue;
    Vue.mixin({
    beforeCreate() {
    if (this.$options && this.$options.store) {
    this.$store = this.$options.store;
    } else {
    this.$store = this.$parent && this.$parent.$store;
    }
    }
    });
    } export default {
    install,
    Store
    };

Vuex原理实现的更多相关文章

  1. 举个例子去理解vuex(状态管理),通俗理解vuex原理,通过vue例子类比

    通俗理解vuex原理---通过vue例子类比   本文主要通过简单的理解来解释下vuex的基本流程,而这也是vuex难点之一. 首先我们先了解下vuex的作用vuex其实是集中的数据管理仓库,相当于数 ...

  2. vuex原理

    Vuex 框架原理与源码分析 vuex状态管理到底是怎样一个原理? 状态管理 Vuex框架原理与源码分析 Vuex实现原理解析 Vue刚出不久,Vuex 就出来了,想请教下Vuex做了什么事情? 个人 ...

  3. 通俗理解vuex原理---通过vue例子类比

    本文主要通过简单的理解来解释下vuex的基本流程,而这也是vuex难点之一. 首先我们先了解下vuex的作用 vuex其实是集中的数据管理仓库,相当于数据库mongoDB,MySQL等,任何组件都可以 ...

  4. 快速理解 VUEX 原理

    1. vuex 的作用: vuex其实是集中的数据管理仓库,相当于数据库mongoDB,MySQL等,任何组件都可以存取仓库中的数据. 2. vuex 流程和 vue 类比: 我们看一下一个简单的vu ...

  5. Vuex 原理

    1.Vuex是什么? 学院派:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式:集中存储和管理应用的所有组件状态. 理解:以上这4个词是我们理解的关键.状态:什么是状态,我们可以通俗的理 ...

  6. vuex原理笔记

    本文总结自: https://tech.meituan.com/vuex-code-analysis.html, 将要点提炼为笔记,以便不时之需,安不忘危. 核心可分为两部分: 1.vue.use(V ...

  7. Vuex原理详解

    一.Vuex是什么 Vuex是专门为Vuejs应用程序设计的状态管理工具.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生改变.它集中于MVC模式中的Model层 ...

  8. 初识vuex

    1.简介 vuex是 vue官方推荐的一个状态管理器.当我们遇到很多状态改变时,组件之间的通信就会变得复杂,这时候vuex的强大就展现出来. 我们从vuex的原理以及vuex的api两个部分介绍vue ...

  9. vuex的学习和理解

    初识Vuex: vuex是 vue官方推荐的一个状态管理器,也是vue专用的一个插件.当我们遇到很多状态改变时,组件之间的通信就会变得复杂,这时候vuex的强大就体现出来了. Vuex 应用的核心就是 ...

随机推荐

  1. SQL SERVER 函数举例

    需求说明 将字符串按照指定的分隔符进行分割,并将结果按照从后往前的顺序倒序排列,拼接后的结果用‘/’符连接.(也可以按照指定符号分割为多个列,修改最后一部分即可) 创建测试表及数据 /* 创建一张测试 ...

  2. 【T-SQL】基础——表别名

    Som有时候我们需要为表设置别名,这样就可以方便的修改表. 如果在SSMS中,可以点击 Query-> SQL CMD mode --Set Alisa for the table:setvar ...

  3. 多线程高并发编程(8) -- Fork/Join源码分析

    一.概念 Fork/Join就是将一个大任务分解(fork)成许多个独立的小任务,然后多线程并行去处理这些小任务,每个小任务处理完得到结果再进行合并(join)得到最终的结果. 流程:任务继承Recu ...

  4. Awareness Kit让你的音乐APP脱颖而出,更懂用户,也更动人心

    让你的音乐APP脱颖而出,更懂用户,也更动人心. 场景 情景感知服务能带来什么?   作为音乐发烧友,闲下来的时候总想打开App,享受沉浸在音乐中的放松.然而,App推荐的歌单经常没法满足我的需要,如 ...

  5. [csu1508 地图的四着色]二分图染色

    抽象后的题意:给一个不超过30个点的图,A从中选不超过5个点涂红绿两种颜色,B用黑白两种颜色把剩下的涂完,任意一条边两端的颜色不同,求每种颜色至少用涂一次的方案数 思路:枚举A涂的点的集合,将原图分成 ...

  6. 帝国cms 批量删除包含关键字的 内容

    删除包含关键字的 内容delete from www_kaifatu_com_ecms_news where playurl like '%关键字%'

  7. day_02~day_09的Python语法基础

    Python基础学习 day_02 使用方法修改字符串的大小写 将字符串首字母变成大写 >>> name = "ada lovelace" >>> ...

  8. 题解 洛谷P1562 【还是N皇后】

    原题:洛谷P1562 这个题的原理和8皇后的原理是一模一样的,就是必须要用n个皇后把每一个行填满,同时满足每一列,每一行,每一条对角线只有一个棋子.但如果按照原来的方法暴打的话只有60分(优化亲测无效 ...

  9. 输入一个整数n,输出契波那契数列的第n项

    package bianchengti; /* * 输入一个整数n,输出契波那契数列的第n项 * 斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89... */ p ...

  10. python操作excel----openpyxl模块

    openpyxl模块支持.xls和.xlsx格式的excel创建,但是只支持.xlsx格式的读取操作,不支持.xls的读取(可以使用xlrd模块来读取,写入操作也可使用xlwt模块),也可使用pand ...