手写Vuex核心原理,再也不怕面试官问我Vuex原理

本文详细介绍了如何在Vue.js中使用use方法注册插件,以及如何通过mixin在组件间共享store实例。重点讲解了install方法的完善,Vuex的state、getter、mutations和actions的实现,以及它们之间的交互。同时提到了面试中可能被问及的问题,如插件复用和组件生命周期的理解。
摘要由CSDN通过智能技术生成

}

installedPlugins.push(plugin);

return this;

}

1、在Vue.js上新增了use方法,并接收一个参数plugin。

2、首先判断插件是不是已经别注册过,如果被注册过,则直接终止方法执行,此时只需要使用indexOf方法即可。

3、toArray方法我们在就是将类数组转成真正的数组。使用toArray方法得到arguments。除了第一个参数之外,剩余的所有参数将得到的列表赋值给args,然后将Vue添加到args列表的最前面。这样做的目的是保证install方法被执行时第一个参数是Vue,其余参数是注册插件时传入的参数。

4、由于plugin参数支持对象和函数类型,所以通过判断plugin.install和plugin哪个是函数,即可知用户使用哪种方式祖册的插件,然后执行用户编写的插件并将args作为参数传入。

5、最后,将插件添加到installedPlugins中,保证相同的插件不会反复被注册。(~~让我想起了曾经面试官问我为什么插件不会被重新加载!!!哭唧唧,现在总算明白了)

五、完善install方法


我们前面提到 通过Vue.use(Vuex) 使得每个组件都可以拥有store实例

这是什么意思呢???

来看mian.js

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’);

我们可以发现这里只是将store ,也就是store/index.js导出的store实例,作为Vue 参数的一部分。

但是这里就是有一个问题咯,这里的Vue 是根组件啊。也就是说目前只有根组件有这个store值,而其他组件是还没有的,所以我们需要让其他组件也拥有这个store。

因此,install方法我们可以这样完善

let install = function(Vue){

Vue.mixin({

beforeCreate(){

if (this.KaTeX parse error: Expected 'EOF', got '&' at position 9: options &̲& this.options.store){ // 如果是根组件

this. s t o r e = t h i s . store = this. store=this.options.store

}else { //如果是子组件

this. s t o r e = t h i s . store = this. store=this.parent && this. p a r e n t . parent. parent.store

}

}

})

}

解释下代码:

  1. 参数Vue,我们在第四小节分析Vue.use的时候,再执行install的时候,将Vue作为参数传进去。

  2. mixin的作用是将mixin的内容混合到Vue的初始参数options中。相信使用vue的同学应该使用过mixin了。

  3. 为什么是beforeCreate而不是created呢?因为如果是在created操作的话,$options已经初始化好了。

  4. 如果判断当前组件是根组件的话,就将我们传入的store挂在到根组件实例上,属性名为$store

  5. 如果判断当前组件是子组件的话,就将我们根组件的$store也复制给子组件。注意是引用的复制,因此每个组件都拥有了同一个$store挂载在它身上。

这里有个问题,为什么判断当前组件是子组件,就可以直接从父组件拿到$store呢?这让我想起了曾经一个面试官问我的问题:父组件和子组件的执行顺序

A:父beforeCreate-> 父created -> 父beforeMounte -> 子beforeCreate ->子create ->子beforeMount ->子 mounted -> 父mounted

可以得到,在执行子组件的beforeCreate的时候,父组件已经执行完beforeCreate了,那理所当然父组件已经有$store了。

六、实现Vuex的state


{{this.$store.state.num}}

我们都知道,可以通过这个 语句获得 state的值

但是我们在Store类里还没实现,显然,现在就这样取得话肯定报错。

前面讲过,我们是这样使用Store的

export default new Vuex.Store({

state: {

num:0

},

mutations: {

},

actions: {

},

modules: {

}

})

也就是说,我们把这个对象

{

state: {

num:0

},

mutations: {

},

actions: {

},

modules: {

}

}

当作参数了。

那我们可以直接在Class Store里,获取这个对象

class Store{

constructor(options){

this.state = options.state || {}

}

}

那这样是不是可以直接使用了呢?

试一下呗!

//App.vue

123

{{this.$store.state.num}}

运行结果:

太赞了吧,怎么会这么简单。。。不敢相信。

哦不,当然没有这么简单,我们忽略了一点,state里的值也是响应式的哦,我们这样可没有实现响应式。

曾经面试官问我Vuex和全局变量比有什么区别。这一点就是注意区别吧

那要怎么实现响应式呢? 我们知道,我们new Vue()的时候,传入的data是响应式的,那我们是不是可以new 一个Vue,然后把state当作data传入呢? 没有错,就是这样。

class Store{

constructor(options) {

this.vm = new Vue({

data:{

state:options.state

}

})

}

}

现在是实现响应式了,但是我们怎么获得state呢?好像只能通过this.$store.vm.state了?但是跟我们平时用的时候不一样,所以,是需要转化下的。

我们可以给Store类添加一个state属性。这个属性自动触发get接口。

class Store{

constructor(options) {

this.vm = new Vue({

data:{

state:options.state

}

})

}

//新增代码

get state(){

return this.vm.state

}

}

这是ES6,的语法,有点类似于Object.defineProperty的get接口

成功实现。

七、实现getter


//myVuex.js

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 gettersgetterName

}

})

})

}

get state(){

return this.vm.state

}

}

我们把用户传进来的getter保存到getters数组里。

最有意思的是经常会有面试题问:为什么用getter的时候不用写括号。要不是我学到这个手写Vuex,也不会想不明白,原来这个问题就像问我们平时写个变量,为什么不用括号一样。(如{{num}},而不是{{num()}}

原来就是利用了Object.defineProperty的get接口。

ok,现在来试一下,getter可不可以使用。

//store/index.js

import Vue from ‘vue’

import Vuex from ‘./myVuex’

Vue.use(Vuex)

export default new Vuex.Store({

state: {

num:0

},

// 新增测试代码

getter:{

getNum:(state)=>{

return state.num

}

},

mutations: {

},

actions: {

},

})

123

state:{{this.$store.state.num}}

getter:{{this.$store.getters.getNum}}

完美。毫无事故。

八、实现mutation


//myVuex.js

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 gettersgetterName

}

})

})

//新增代码

let mutations = options.mutations || {}

this.mutations = {}

Object.keys(mutations).forEach(mutationName=>{

this.mutations[mutationName] = (arg)=> {

mutationsmutationName

}

})

}

get state(){

return this.vm.state

}

}

mutations跟getter一样,还是用mutations对象将用户传入的mutations存储起来。

但是怎么触发呢?回忆一下,我们是怎么触发mutations的。

this.$store.commit(‘incre’,1)

对,是这种形式的。可以看出store对象有commit这个方法。而commit方法触发了mutations对象中的某个对应的方法,因此我们可以给Store类添加commit方法

//myVuex.js

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 gettersgetterName

}

})

})

let mutations = options.mutations || {}

this.mutations = {}

Object.keys(mutations).forEach(mutationName=>{

this.mutations[mutationName] = (arg)=> {

mutationsmutationName

}

})

}

//新增代码

commit(method,arg){

this.mutationsmethod

}

get state(){

return this.vm.state

}

}

好了,现在来测试一下。

123

state:{{this.$store.state.num}}

getter:{{this.$store.getters.getNum}}

<button @click=“add”>+1

//新增测试代码

store/index.js

//store/index.js

import Vue from ‘vue’

import Vuex from ‘./myVuex’

Vue.use(Vuex)

export default new Vuex.Store({

state: {

num:0

},

getter:{

getNum:(state)=>{

return state.num

}

},

// 新增测试代码

mutations: {

incre(state,arg){

state.num += arg

}

},

actions: {

},

})

运行成功。

九、实现actions


当会实现mutations后,那actions的实现也很简单,很类似,不信看代码:

//myVuex.js

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 gettersgetterName

}

})

})

let mutations = options.mutations || {}

this.mutations = {}

Object.keys(mutations).forEach(mutationName=>{

this.mutations[mutationName] = (arg)=> {

mutationsmutationName

}

})

//新增代码

let actions = options.actions

this.actions = {}

Object.keys(actions).forEach(actionName=>{

this.actions[actionName] = (arg)=>{

actionsactionName

}

})

}

// 新增代码

dispatch(method,arg){

this.actionsmethod

}

commit(method,arg){

console.log(this);

this.mutationsmethod

}

get state(){

return this.vm.state

}

}

一毛一样,不过有一点需要解释下,就是这里为什么是传this进去。这个this代表的就是store实例本身

这是因为我们使用actions是这样使用的:

actions: {

asyncIncre({commit},arg){

setTimeout(()=>{

commit(‘incre’,arg)

},1000)

}

},

其实{commit} 就是对this,即store实例的解构

那我们来测试一下。

123

state:{{this.$store.state.num}}

getter:{{this.$store.getters.getNum}}

<button @click=“add”>+1

<button @click=“asyncAdd”>异步+2

store/index.js

//store/index.js

import Vue from ‘vue’

import Vuex from ‘./myVuex’

Vue.use(Vuex)

export default new Vuex.Store({

state: {

num:0

},

getter:{

getNum:(state)=>{

return state.num

}

},

mutations: {

incre(state,arg){

state.num += arg

}

},

//新增测试代码

actions: {

asyncIncre({commit},arg){

setTimeout(()=>{

commit(‘incre’,arg)

},1000)

}

},

})

oh my god,居然出错了,它这里错误 说的是执行到这里发现这里的this为undefined。

不过,不对啊,我们在实现mutation的时候也执行到这里了啊,而且执行成功了啊。

来分析一下:

this.$store.commit(‘incre’,1)

执行这段代码的时候,执行commit的时候,this是谁调用就指向谁,所以this指向$store

this.$store.dispatch(‘asyncIncre’,2)

执行这段代码,就会执行

asyncIncre({commit},arg){

setTimeout(()=>{

commit(‘incre’,arg)

},1000)

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可免费获取,包括答案解析。

Wdrci5jbi1iai51ZmlsZW9zLmNvbS85OWQ4YmI1YS01NGExLTQyY2YtOWI4OC1hMWZlOWM5YTY1NTYucG5n?x-oss-process=image/format,png)

不过,不对啊,我们在实现mutation的时候也执行到这里了啊,而且执行成功了啊。

来分析一下:

this.$store.commit(‘incre’,1)

执行这段代码的时候,执行commit的时候,this是谁调用就指向谁,所以this指向$store

this.$store.dispatch(‘asyncIncre’,2)

执行这段代码,就会执行

asyncIncre({commit},arg){

setTimeout(()=>{

commit(‘incre’,arg)

},1000)

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-0ZyDbrlG-1713837373733)]

[外链图片转存中…(img-RCejQ1O7-1713837373734)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-jilRW6e5-1713837373734)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-LYW9cFbK-1713837373734)]

最后

文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可免费获取,包括答案解析。

[外链图片转存中…(img-vPs6Wagp-1713837373734)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值