一、Vuex 是什么
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
对于组件间的通信方式而言,vuex也是一个可以进行任意组件通信的方法。
二、什么是“状态管理模式”
1.案例引入
让我们从一个简单的 Vue 计数应用开始:
const Counter = {
// 状态
data () {
return {
count: 0
}
},
// 视图
template: `
<div>{{ count }}</div>
`,
// 操作
methods: {
increment () {
this.count++
}
}
}
createApp(Counter).mount('#app')
这个状态自管理应用包含以下几个部分:
-
状态,驱动应用的数据源;
-
视图,以声明方式将状态映射到视图;
-
操作,响应在视图上的用户输入导致的状态变化。
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
多个视图依赖于同一状态。
来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
三、什么情况下我应该使用 Vuex
-
vue2是通过
new Vue()
,构造函数的方式来创建实例Vue2的组件系统设计中,所有Vue实例是共享一个Vue构造函数对象的,包括全局指令/全局组件,无法做到相互隔离。
也就是说我们整个项目中,只有一个根Vue实例,其他的单文件组件创建的 Vue实例都会成为它的子实例。
import Vue from 'Vue' import APP from './APP.vue' const vm = new Vue({ render:h => h(APP) }) vm.$mount('#app')
-
vue3通过
createApp
工厂函数来创建实例而Vue3通过createApp方法可以返回一个提供应用上下文的应用实例。不同实例注册的组件无法在不同的实例下使用。Vue3更适合在大型的开发环境中使用,不同开发人员可以完全独立地使用不同的实例。
import {createApp} from 'vue' import APP from './APP.vue' const app = createApp(APP) app.mount('#app')
四、具体使用方法
-
在vue2项目中,在入口文件main.js中通过Vue.prototype挂载全局方法对象。
import Vue from 'vue' import Axios from 'axios import utils from '@/utils' import qs from 'qs' ... //挂载全局对象 Vue.prototype.$http = Axios; Vue.prototype.$qs = qs ....
-
vue3中取消了Vue.prototype,需要使用官方提供的globalPropertiesAPI在全局挂载方法和属性。
import { createApp } from 'vue' import App from './App.vue' import router from './router/index' import axios from './utils/axios' import qs from 'qs' const app = createApp(App) //全局挂载 app.config.globalProperties.$axios = axios; app.config.globalProperties.$qs = qs; ...
-
使用全局
-
vue2中使用
this.$http , this.$qs <script> export default { data() { return { dataList: [] } }, mounted() { this.getList() }, methods: { getList() { this.$http({ url: 'url地址' }).then(res=>{ let { data } = res.data this.dataList = data }) }, }, } </script>
-
vue3中使用, 在vue3的setup中是使用getCurrentInstanceAPI获取全局对象
... setup(){ const currentInstance = getCurrentInstance() const {$axios,$route} = currentInstance.appContext.config.globalProperties function submit(){ $axios.post('/adminUser/login',{ userName:state.ruleForm.username || '', passwordMd5:md5(state.ruleForm.password) }).then(res=>{ console.log(res) }) } }
五、各个状态的核心概念
1.state
2.mutations
3.actions
4.getters
-
把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新。在单向绑定的基础上,用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定
-
vue2.x采用的是object.defineProperty
-
1、vue2.x的双向数据绑定是通过数据劫持结合发布者订阅者模式的方式来实现的,通过object.defineProperty来劫持各个属性的setter,getter,在数据变化时发布消息给订阅者,触发相应的监听回调来渲染视图。也就是说数据和视图同步,数据发生变化,视图变化,视图变化,数据也随之发生变化
-
2、Obejct.defineproperty(obj,prop,descriptor)方法,接收三个参数,分别是obj(定义其上属性的对象),prop(定义或者修改的属性),description(具体的改动方法),就是用这个方法来定义一个值,当使用时我们调用他里面的get方法,当我们给这个属性赋值时,用到他的set方法。
-
具体步骤
第一步: 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给对象的某个值赋值,就会触发setter,那么就可以监听到数据的变化。 第二步: complie解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令的界定绑定到更新函数,添加监听数据的订阅者,一旦数据发生变动,收到通知,更新视图。 第三步: Watcher订阅者是Obejct和compile之间的通信桥梁,主要做的事情就是: 1、在自身实例化的往属性订阅器dep里面添加自己。 2、自身必须有个update方法。 3、待属性变动dep.notice通知时,能调用自身的update方法,并触发compile中绑定的回调。 第四步: MVVM作为数据绑定的入口,整合Observe、Compile和Watcher三者,通过Observe来监听自己的model数据变化,通过compile来解析编译模板指令,最终利用watcher搭起observe和Compile之间的通信桥梁,到达数据变化视图更新,视图变化数据更新的效果。
-
vue3 中使用了 es6 的 ProxyAPI 对数据代理。
-
proxy:对象用户定义基本操作的自定义行为(比如:属性的查找,复制,函数调用等等)。proxy就像一个拦截器一样,他可以在读取对象的属性,修改对象的属性,获取对象属性列表。他可以阻止对象上的默认行为,然后我们自己去定义这些行为。比如我们通过拦截默认的set方法,在自己定一个set方法添加回调函数。
-
proxy的语法
-
target: 要兼容的对象,可以是一个对象,数组,函数等等 handler: 是一个对象,里面包含了可以监听这个对象的行为函数,比如里面的
get
与set
const proxy = new Proxy(target,handler) -
1、handler.get
当通过proxy去读取对象里面的属性的时候,会进入到get钩子函数里面
2、handler.set
当通过proxy去为对象设置修改属性的时候,会进入到set钩子函数里面
3、handler.deleteProperty当使用delete去删除对象里面的属性的时候,会进入deleteProperty`钩子函数
4、handler.apply
当proxy监听的是一个函数的时候,当调用这个函数时,会进入apply钩子函数
5、handle.ownKeys
当通过Object.getOwnPropertyNames, Object.getownPropertySymbols, Object.keys, Reflect.ownKeys去获取对象的信息的时候,就会进入ownKeys这个钩子函数
6、handler.construct
当使用new操作符的时候,会进入construct这个钩子函数
详细介绍:
get 当通过 proxy 去读取对象里面的属性的时候,会进入到get钩子函数里面 target: 目标对象,即通过proxy代理的对象 key: 要访问的属性名称 receiver: receiver相当于是我们要读取的属性的this,一般情况下他就是proxy对象本身,关于receiver的作用,后文将具体讲解 handle.get(target,key, receiver) set 当为对象里面的属性赋值的时候,会触发set target: 目标对象,即通过proxy代理的对象 key: 要赋值的属性名称 value: 目标属性要赋的新值 receiver: 与 get的receiver 基本一致 handle.set(target,key,value, receiver)
六、模块化编码
-
Vue2使用选项类型API(Options API)对比Vue3合成型API(Composition API)
-
响应式处理
Vue2采用的是Object.defineProperty进行响应式处理,通过劫持数据的getter和setter来监听数据变化。Vue2的响应式处理有很多局限性,例如添加和删除属性,数组下标变更等都无法有效监听。Vue3采用的是ES6的Proxy来处理响应式,不仅支持上述的所有情况,而且有更好的性能表现。在Vue3中,每个数据都使用Proxy进行了代理拦截,这使得Vue3的响应式处理更加方便、灵活和高效。
-
组件生命周期
Vue2的生命周期主要分为以下几个阶段: beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed。Vue3的生命周期相比较Vue2而言,更加易于使用,分为三个阶段:setup, created, mounted。 其中setup阶段被引入作为原来Vue2中的beforeCreate、created阶段的替代品,它负责组件的初始化和响应式数据的创建。
vue2 --------------------- vue3
beforeCreate -> setup() created -> setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted activated -> onActivated deactivated -> onDeactivated
-
组件通讯
Vue2中组件通讯主要通过 e m i t 和 emit和 emit和on进行实现。 e m i t 用于子组件向父组件传递数据,而 emit用于子组件向父组件传递数据,而emit用于子组件向父组件传递数据,而on则用于父组件接收子组件传递过来的数据。Vue3中通过provide和inject来实现组件通讯,但是与Vue2不同的是,它是基于新的响应式API来实现的,它们之间可以多层嵌套传递数据,且避免了命名冲突。
-
其他差异
编译器的变化
Vue2中的编译器是一个单独的项目,它将template转化成render函数进行渲染。而在Vue3中,编译器被合并到了核心代码库中,便于维护。TypeScript
Vue3完全支持TypeScript,这意味着开发者可以更加轻松、安全地编写Vue的组件。静态类型提升
Vue3支持静态类型,将在编译期间进行类型检查,从而提高代码的健壮性和效率。
总结
-
Vue2 - 这里把数据放入data属性中
export default { data() { return { //声明数据变量 } }, methods: { } };
-
Vue3 setup()方法里。
(1)使用reactive 代替Object,Array,Map,Set
(2)使用ref 代替String,Number,Boolean
import {reactive,ref} from ‘vue’; export default { setup(){ const state = reactive({count:0}); const num = ref(0); } }
methods: {
}
}; -
Vue3 setup()方法里。
(1)使用reactive 代替Object,Array,Map,Set
(2)使用ref 代替String,Number,Boolean
import {reactive,ref} from ‘vue’; export default { setup(){ const state = reactive({count:0}); const num = ref(0); } }