Vue生态及实践 - vuex

news2025/1/12 12:19:55

目录

Vuex

vuex基础

阶段小结(vuex的四种对象)

vuex响应式原理

vuex.js的实现

index.js

Module

局部状态

命名空间

跨模块访问数据

小结


目标

  1. Vuex相关基本概念和使用方式
  2. vuex响应式原理过程
  3. vuex中如何进行模块式开发

Vue2.0是单向数据流的

Vuex

vuex基础

Vuex使用单一的Store状态树,包含全部层级状态

const store = new Vuex.Store({
    state: { // 状态值
        count: 0    
    },
    getter: {// 衍生值, 计算属性,变化后会再计算
      doubleCount: state => state.count * 2      
    },
    mutations: { // 变更state属性,同步函数
        addCount (state,payload=1) {
            state.count += payload    
        }    
    },
    actions: {// 异步操作
        asyncAddCount({commit}) { // 结构获得commit方法
            setTimeout(() => {
            	commit('addCount');// 通知状态变更 count++
            },1500);        
        }    
    }
})

cmd指令 :vue add vuex

快速在vue工程中添加vuex依赖

// 父组件简写
{{count}}{{count2}}{{doubleCount}}
import {mapState, mapGetters, mapMutations, mapActions} from "vuex"
// map方法,快速开发
export default {
    name: "App",
    components: {
        HelloWord    
    },
    computed: {
        // ...mapState(["count"])// 映射下面
        // count() {
        //   return this.$store.state.count;        
        // },
        // ---------------------------------
        ...mapGetters(["doubleCount"])// 映射下面
        // doubleCount() { // 派生属性
        //  return this.$store.getters.doubleCount;            
        // },
        // ---------------------------------
        ...mapState({ // 映射
            count: state => state.count, // 自定义变量
            count2: state => state.count // 别名修改   
        }),
    },
    methods: {
        ...mapMutations(["addCount"]),// 对象扩展语法 @click="addcount(2)"
        //addCount() {
        //    this.$store.commit("addCount", 3) // mutations  
        //},
        // ---------------------------------
        ...mapActions(["asyncAddCount"]),// 对象扩展语法
        //asyncAddCount() {
        //    // dispatch("【actions里面约定的函数】asyncAddCount") 异步
        //   this.$store.dispatch("asyncAddCount"); // actions   1.5s后操作变更
        }
    }
}

 vuex大大简化了组件通信的过程

// 子组件简写
<button @click="addCount(2)">addCount</button>
<button @click="asyncAddCount">asyncAddCount</button>
import {mapMutations, mapActions} from "vuex"
methods: {
        ...mapMutations(["addCount"]),// 对象扩展语法 @click="addcount(2)"
        //addCount() {
        //    this.$store.commit("addCount", 3) // mutations  
        //},
        // ---------------------------------
        ...mapActions(["asyncAddCount"]),// 对象扩展语法
        //asyncAddCount() {
        //    // dispatch("【actions里面约定的函数】asyncAddCount") 异步
        //   this.$store.dispatch("asyncAddCount"); // actions   1.5s后操作变更
        }
    }

阶段小结(vuex的四种对象)

  • State【单一Store、响应式】
  • Getter【由state派生出的状态、store的计算属性】
  • Mutations【由commit触发、必须是同步函数】
  • Actions【由dispatch触发、可以包含任意异步操作、不直接变更状态,可以通过mutation变化】

Mutations—修改—>State—响应式—>Vue Component【响应式变化,进行渲染】

vuex响应式和vue响应式串联,使vuex里的变化同步到vue组件中

vuex响应式原理

vuex.js的实现

import { reactive } from "./index.js"; // 响应式劫持
export class Store { // export 暴露Store
    // 构造器
    constructor(options = {}) {
        let { state, mutations, plugins, actions, getter } = options;// plugins插件
        this._vm = reactive(state);// reactive响应式劫持
        this._mutations = mutations;
        
        this._subscribe = [] // 订阅函数队列
        // 初始化之后
        // 遍历,
        plugins.forEach(plugin => {
            plugin(this) // 每个插件执行一下,把Store传进去     
        })
    }
    get state() {
        return this._vm;    
    }
    
    commit(type, payload) {// 执行_muations的type函数
        const entry = this._muations[type];// 通过枚举取出  this._muations[type]  
        if(!entry) {
        	return;          
        }
        entry(this.state, payload);
        
        this._subscribe.forEach(sub => sub({type, payload}, this.state));
    }
    
    subscribe(fn) {
        if(!this._subscribe.includes(fn)) {// 判断fn不在订阅队列里
           this._subscribe.push(fn);          
        }           
    }
}

index.js

let active;
let watch = function(cb) {
  active = cb;
  active();
  active = null;
};
let queue = [];
let nextTick = cb => Promise.resolve().then(cb);
let queueJob = job => {
  if (!queue.includes(job)) {
    queue.push(job);
    nextTick(flushJobs);
  }
};
let flushJobs = () => {
  let job;
  while ((job = queue.shift()) !== undefined) {
    job();
  }
};
class Dep {
  constructor() {
    this.deps = new Set();
  }
  depend() {
    if (active) {
      this.deps.add(active);
    }
  }
  notify() {
    this.deps.forEach(dep => queueJob(dep));
  }
}
let ref = initValue => {
  let value = initValue;
  let dep = new Dep();
  return Object.defineProperty({}, "value", {
    get() {
      dep.depend();
      return value;
    },
    set(newValue) {
      value = newValue;
      dep.notify();
    }
  });
};
let createReactive = (target, prop, value) => {
  let dep = new Dep();
  // return new Proxy(target, {
  //   get(target, prop) {
  //     dep.depend();
  //     return Reflect.get(target, prop);
  //   },
  //   set(target, prop, value) {
  //     Reflect.set(target, prop, value);
  //     dep.notify();
  //   },
  // });
  return Object.defineProperty(target, prop, {
    get() {
      dep.depend();
      return value;
    },
    set(newValue) {
      value = newValue;
      dep.notify();
    }
  });
};
export let reacitve = obj => {
  let dep = new Dep();
  Object.keys(obj).forEach(key => {
    let value = obj[key];
    createReactive(obj, key, value);
  });
  return obj;
};
// let data = reacitve({
//   count: 0
// });
import { Store } from "./vuex";
let store = new Store({
  state: {
    count: 0
  },
  mutations: {
    addCount(state, payload = 1) {
      state.count += payload;
    }
  },
  plugins: [// 定义一个插件
  	function(store) {
       // 订阅mutation, state
      store.subscribe((mutation, state) => {
        console.log(mutation);// 监听mutation变化,如日志上报
      })    
   },
    store =>
      store.subscribe((mutation, state) => {
        console.log(mutation);
      })
  ]
});
document.getElementById("add").addEventListener("click", function() {
  // data.count++;
  store.commit("addCount", 1);
});
let str;
watch(() => {
  str = `hello ${store.state.count}`;
  document.getElementById("app").innerText = str;
});

Module

因为使用了单一对象树,应用的所有状态都会集中在一个比较大的对象当中,应用变的复杂时,Store对象就会变的非常臃肿

为了解决上面的问题,Vuex允许我们将Store分割成模块

const moduleA = {
    state: {},
    mutations: {},
    actions: {},
    getters: {}
}
const moduleB = {
    state: {},
    mutations: {},
    actions: {}
}
const store = new Vuex.Store({
    modules: {
        a: moduleA,
        b: moduleB    
    }
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

局部状态

const moduleA = {
    state: { count: 0 },
    mutations: {
        increment (state) {
            //这里的`state`对象时模块的局部状态
            state.count++        
        }    
    },
    actions: {
        doubleCount (state, getters) {
            return state.count * 2        
        }    
    },
    getters: {
        incrementIfOddOnRootSum ({state, commit}) {
            if(state.count%2 === 1) {
                commit('increment')            
            }        
        }    
    }
}

命名空间

默认情况下,模块内部action、mutation和getter是注册在全局命名空间的,这就导致不同的module中间出现命名冲突问题const

const store = new Vuex.Store({
    modules: {
    	moduleA: {
         	 namespaced: true, // 命名空间开启
            state: { count: 0 },
            mutations: {
                increment (state) {// 使用时->mutations['moduleA/increment']
                    //这里的`state`对象时模块的局部状态
                    state.count++        
                }    
            },
            actions: {
                doubleCount (state, getters) {// actions['moduleA/doubleCount']
                    return state.count * 2        
                }    
            },
            getters: {
                incrementWhenOdd ({state, commit}) {// getters['moduleA/incrementWhenOdd']
                    if(state.count%2 === 1) {
                        commit('increment')            
                    }        
                }    
            }
        }    
    }
})

跨模块访问数据

modules: {
    foo: {
        namespaced: true,
        getters: {
            // 在中国模块的getter中,`getters`被局部化了
            // 你可以使用getter的第4个参数来调用`rootGetters`
            someGetter(state, getters, rootState, rootGetters) {
                getters.someOtherGetter// -> 'foo/someOtherGetter' foo命名空间下的方法
                rootGetters.someOtherGetter// -> 'someOtherGetter' 根模块的方法           
            },
            someOtherGetter: state => {...}        
        },
        actions: {
            // 在这个模块中,dispatch和commit也被局部化了
            // 它们可以接受`root`属性以访问根dispatch或commit
            someAction({dispatch,commit,getters,rootGetters}) {
                getters.someGetter // -> 'foo/someGetter' foo命名空间下的方法
                rooGetters.someGetter// -> 'someGetter'  根模块的方法    
                
                dispatch('someOtherAction') // -> 'foo/someOtherAction'
                dispatch('someOtherAction', null, { root: true}) //到根模块事件 -> 'someOtherAction'                                                                                                               
            		
                commit('someMutation') // -> 'foo/someMutation'
            	 commit('someMutation', null, { root: true}) // ->到根模块事件 'someMutation'
            },
            someOtherAction(ctx, payload) {...}       
        }
        // Mutations 在设计时候,不会暴露rootState,rootGetters,Mutation都是易于测试的同步纯函数,
        // 所以获取rootState这种具有副作用的参数,统一需要大家放在action里面操作
        // 在action里去拼接rootState和rootGetters和当前的state
        // 在通过commit的纯函数去修改我们的state
    }
}

小结

  1. vuex响应式源码分析
  2. 如何去通过vuex的插件机制去订阅mutation的改变
  3. 在vuex当中模块化设计module

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/703885.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

H5学习期间 问题文档(更新中)

目录 一、html与css中让标签 二、css鼠标滑入显示div 三、git报错解决方案 四、ul>li中给h1标签设置行高 一、html与css中让标签<li>在同一行显示 li标签一行显示 效果展示&#xff1a; 代码块&#xff1a; <!DOCTYPE html> <html lang"en"…

国内做MES系统的企业哪家好?

什么是MES&#xff1f;国内做MES系统的企业哪家好&#xff1f;下面分为两块跟大家详细讲解。 一、什么是MES&#xff1f; 1、概念&#xff1a; MES&#xff08;英文全称&#xff1a;Manufacturing Execution System&#xff09;制造执行系统&#xff0c;是面向车间生产的管理…

常用调用链等监控对比

1 了解监控 什么是监控&#xff1f; 指对计算机系统、网络、应用程序等进行实时、持续的管理和控制&#xff0c;以确保系统的性能、稳定性和安全性。 具体来说&#xff0c;监控可以通过收集和分析各种指标数据来实现&#xff0c;例如CPU、内存、网络、磁盘等硬件指标&#xf…

NamedPipeClientStream连接远程主机异常的处理

NamedPipeClientStream连接远程主机异常的处理 命名管道通讯测试异常解决方法&#xff1a; 命名管道通讯测试异常 在两个PC主机上测试命名管道通讯&#xff0c;设置服务器IP&#xff0c;初始化客户端时&#xff0c;会报无法访问路径异常。 解决方法&#xff1a; 在Windows功能…

软件测试项目案例去哪找【银行/金融/电商/外卖】

项目经验&#xff08;案例一&#xff09; 项目时间&#xff1a;2016-08 - 2017-07 项目名称&#xff1a;小花钱包&#xff08;Web&#xff09; 项目描述&#xff1a; 项目介绍 这个产品产是互联网金融理财服务平台&#xff0c;既可以发起投标&#xff0c;也可以借款&#xf…

使用Python实现.mat文件转换、读取

目录 一、前言 二、将excel文件转换为mat文件 三、读取mat文件并进行可视化 四、将mat文件转换为excel文件 一、前言 有时候我们拿到一个数据文件不是excel或者csv的怎么办呢&#xff1f;比如&#xff1a;你可恶的合作伙伴测实验数据时&#xff0c;用MATLAB丢给你一个mat…

【mac切换go不同版本】

1.需求是&#xff1a;mac本机想要同时拥有几个版本的go&#xff0c;并可以方便切换 第一种brew link切换失败&#xff0c;手动切换成功 第二种gvm方法失败 第三种docker成功 &#xff08;别看前面啰里八嗦&#xff0c;我只是记录一下&#xff0c;请直接跳到第三个&#xff09; …

【IMX6ULL驱动开发学习】14.Linux驱动开发 - GPIO中断(设备树 + GPIO子系统)

代码自取【14.key_tree_pinctrl_gpios_interrupt】&#xff1a; https://gitee.com/chenshao777/imx6-ull_-drivers 主要接口函数&#xff1a; 1. of_gpio_count&#xff08;获得GPIO的数量&#xff09; static inline int of_gpio_count(struct device_node *np)2. kzalloc…

uniapp项目或者vue项目 封装弹框组件

baseDialog组件代码: <template><view class"base-dialog" v-if"show"><view class"mask"></view><view class"Popmenu" :style"{ width }"><view class"header">{{ title …

怎么做好技术团队规划

一、做规划包括哪些东西 业务结果&#xff1a; 直白说就是业务层面的战绩&#xff0c;你团队打造了一个公司 GMV 占比超过 50%的商城&#xff0c;或者支撑了某个快速发展业务&#xff0c;这些都是业务结果&#xff0c;用业务数字来说话。 技术创新&#xff1a; 由技术人员发起…

【DBA专属】mysql-------->>>MMM高可用集群架构

Mysql---MMM高可用集群架构 目录 MMM安装部署 环境配置&#xff1a;&#xff08;所有主机配置&#xff09; 1、主机信息 2、关闭防火墙 3、同步时区 4、配置主机解析文件 5、配置ssh免密登录 6、所有机器安装epel源 数据库配置&#xff1a; 【所有数据库均做的配置】…

蜣螂优化算法(DBO)优化VMD参数,最小包络熵、样本熵、信息熵、排列熵(适应度函数可自行选择,一键修改)包含MATLAB源代码

蜣螂优化算法是华大学沈波教授团队&#xff0c;继麻雀搜索算法(Sparrow Search Algorithm&#xff0c;SSA&#xff09;之后&#xff0c;于2022年11月27日又提出的一种全新的群体智能优化算法。已有很多学者将算法用于实际工程问题中&#xff0c;今天咱们用蜣螂优化算法优化一下…

技术干货——Selenium Python使用技巧(二)

目录 进行自动跨浏览器测试 使用CSS定位器 WebElement的HTML源代码 鼠标悬停 关闭标签而不是浏览器 处理下拉菜单 复选框处理 通过CSS选择器选择元素 总结&#xff1a; 进行自动跨浏览器测试 您可能需要在多种情况下针对不同的浏览器&#xff08;例如Firefox&#xff…

软件测试工程师的工作内容?告诉你们什么是真正的测试工程师

目录 前言 1.何为软件测试工程师&#xff1f; 2.软件测试工程师的职责&#xff1f; 3.为什么要做软件测试&#xff1f; 4.软件测试的前途如何&#xff1f; 5.工具和思维谁更重要&#xff1f; 6.测试和开发相差大吗&#xff1f; 7.成为测试工程师的必备条件 8.测试的分…

shiro和redis一起使用

Shiro 缓存配置 当我们进行授权操作时,每次都会从数据库查询用户权限信息,为了提高授权性能,可以将用户权限信息查询出来以后进行缓存,下次授权时从缓存取数据即可。 Shiro 中内置缓存应用实现,其步骤如下: 第一步:在 SpringShiroConfig 中配置缓存 Bean 对象(Shiro 框架提供)…

Talk | 阿姆斯特丹大学博士生胡涛:计算机视觉中的标签效率学习

本期为TechBeat人工智能社区第509期线上Talk&#xff01; 北京时间6月29日(周四)20:00&#xff0c;阿姆斯特丹大学博士生—胡涛的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “计算机视觉中的标签效率学习”&#xff0c;届时将从生成式学习、数…

Drools用户手册翻译——第二章 入门(上)

因为篇幅原因&#xff0c;所以分为上下两个部分&#xff0c;主要就是通过一个交通违章项目的例子&#xff0c;带你先粗略感受一下决策模型的使用流程&#xff0c;总体来说有详细&#xff0c;也有没说清的地方&#xff0c;如果想要了解一下决策模型&#xff0c;可以进来了解一下…

试用有奖:在线试用stable diffusion 模型生成优质人物好图

一、活动介绍 InsCode是一个集成了在线IDE、在线AI编程、在线算力租赁、在线项目部署以及在线SD 模型使用的综合代码开发平台。不论你是初级软件工程师&#xff0c;还是AI大模型爱好者&#xff0c;InsCode都能帮助你快速编写代码&#xff0c;运行项目。甚至在这里你还可以一键…

某平台登录之电话号码加密

抓包 POST /******/common/****** HTTP/1.1 Host: ****** Content-Type: application/json; charsetUTF-8 Host: ****** User-Agent: okhttp/3.14.9{"type":"login","apor":"******************"}加密 由于信息原因就不泄露了 此处只…

Linux系统编程:进程的创建、终止和替换

目录 一. 进程创建 1.1 fork函数的使用 1.2 fork函数的底层实现 1.3 子进程创建的写时拷贝问题 二. 进程的退出 2.1 进程退出的场景和方法 2.2 exit和_exit函数 三. 进程的等待 3.1 为什么要有进程等待 3.2 进程等待的方法 3.2.1 进程等待的相关函数 3.2.2 进程的阻…