为您推荐:
精华内容
最热下载
问答
  • 5星
    52.18MB huayula 2021-09-07 06:59:33
  • 5星
    91.31MB weixin_44510615 2021-07-09 17:18:52
  • 文章目录手写Vuex核心原理一、核心原理二、基本准备工作三、剖析Vuex本质四、分析Vue.use五、完善install方法六、实现Vuex的state七、实现getter八、实现mutation九、实现actions 一、核心原理 Vuex本质是一个对象 ...

    手写Vuex核心原理

    一、核心原理

    1. Vuex本质是一个对象
    2. Vuex对象有两个属性,一个是install方法,一个是Store这个类
    3. install方法的作用是将store这个实例挂载到所有的组件上,注意是同一个store实例。
    4. Store这个类拥有commit,dispatch这些方法,Store类里将用户传入的state包装成data,作为new Vue的参数,从而实现了state 值的响应式。

    二、基本准备工作

    我们先利用vue-cli建一个项目

    删除一些不必要的组建后项目目录暂时如下:

    已经把项目放到 github:https://github.com/Sunny-lucking/howToBuildMyVuex 可以卑微地要个star吗。有什么不理解的或者是建议欢迎评论提出

    我们主要看下App.vue,main.js,store/index.js

    代码如下:

    App.vue

    <template>
      <div id="app">
        123
      </div>
    </template>
    

    store/index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
      },
      mutations: {
      },
      actions: {
      },
      modules: {
      }
    })
    
    

    main.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')
    

    现在我们启动一下项目。看看项目初始化有没有成功。

    ok,没毛病,初始化成功。

    现在我们决定创建自己的Vuex,于是创建myVuex.js文件

    目前目录如下

    再将Vuex引入 改成我们的myVuex

    //store/index.js
    import Vue from 'vue'
    import Vuex from './myVuex' //修改代码
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
      },
      mutations: {
      },
      actions: {
      },
      modules: {
      }
    })
    

    三、剖析Vuex本质

    先抛出个问题,Vue项目中是怎么引入Vuex。

    1. 安装Vuex,再通过import Vuex from 'vuex'引入
    2. 先 var store = new Vuex.Store({…}),再把store作为参数的一个属性值,new Vue({store})
    3. 通过Vue.use(Vuex) 使得每个组件都可以拥有store实例

    从这个引入过程我们可以发现什么?

    1. 我们是通过new Vuex.store({})获得一个store实例,也就是说,我们引入的Vuex中有Store这个类作为Vuex对象的一个属性。因为通过import引入的,实质上就是一个导出一个对象的引用

    所以我们可以初步假设

    Class Store{
      
    }
    
    let Vuex = {
      Store
    }
    
    1. 我们还使用了Vue.use(),而Vue.use的一个原则就是执行对象的install这个方法

    所以,我们可以再一步 假设Vuex有有install这个方法。

    Class Store{
      
    }
    let install = function(){
      
    }
    
    let Vuex = {
      Store,
      install
    }
    

    到这里,你能大概地将Vuex写出来吗?

    很简单,就是将上面的Vuex对象导出,如下就是myVuex.js

    //myVuex.js
    class Store{
    
    }
    let install = function(){
    
    }
    
    let Vuex = {
        Store,
        install
    }
    
    export default Vuex
    

    我们执行下项目,如果没报错,说明我们的假设没毛病。

    天啊,没报错。没毛病!

    四、分析Vue.use

    Vue.use(plugin);

    (1)参数

    { Object | Function } plugin
    

    (2)用法

    安装Vue.js插件。如果插件是一个对象,必须提供install方法。如果插件是一个函数,它会被作为install方法。调用install方法时,会将Vue作为参数传入。install方法被同一个插件多次调用时,插件也只会被安装一次。

    关于如何上开发Vue插件,请看这篇文章,非常简单,不用两分钟就看完:如何开发 Vue 插件?

    (3)作用

    注册插件,此时只需要调用install方法并将Vue作为参数传入即可。但在细节上有两部分逻辑要处理:

    1、插件的类型,可以是install方法,也可以是一个包含install方法的对象。

    2、插件只能被安装一次,保证插件列表中不能有重复的插件。

    (4)实现

    Vue.use = function(plugin){
    	const installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
    	if(installedPlugins.indexOf(plugin)>-1){
    		return this;
    	}
    	<!-- 其他参数 -->
    	const args = toArray(arguments,1);
    	args.unshift(this);
    	if(typeof plugin.install === 'function'){
    		plugin.install.apply(plugin,args);
    	}else if(typeof plugin === 'function'){
    		plugin.apply(null,plugin,args);
    	}
    	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.$options && this.$options.store){ // 如果是根组件
                    this.$store = this.$options.store
                }else { //如果是子组件
                    this.$store = this.$parent && this.$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

        <p>{{this.$store.state.num}}</p>
    

    我们都知道,可以通过这个 语句获得 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
    <template>
      <div id="app">
        123
        <p>{{this.$store.state.num}}</p>
      </div>
    </template>
    

    运行结果:

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

    哦不,当然没有这么简单,我们忽略了一点,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 getters[getterName](this.state)
                    }
                })
            })
    
        }
        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: {
      },
    })
    
    <template>
      <div id="app">
        123
        <p>state:{{this.$store.state.num}}</p>
        <p>getter:{{this.$store.getters.getNum}}</p>
      </div>
    </template>
    

    完美。毫无事故。

    八、实现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 getters[getterName](this.state)
                    }
                })
            })
            //新增代码
            let mutations = options.mutations || {}
            this.mutations = {}
            Object.keys(mutations).forEach(mutationName=>{
                this.mutations[mutationName] = (arg)=> {
                    mutations[mutationName](this.state,arg)
                }
            })
    
        }
        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 getters[getterName](this.state)
                    }
                })
            })
            
            let mutations = options.mutations || {}
            this.mutations = {}
            Object.keys(mutations).forEach(mutationName=>{
                this.mutations[mutationName] =  (arg)=> {
                    mutations[mutationName](this.state,arg)
                }
            })
    
        }
        //新增代码
        commit(method,arg){
            this.mutations[method](arg)
        }
        get state(){
            return this.vm.state
        }
    }
    

    好了,现在来测试一下。

    <template>
      <div id="app">
        123
        <p>state:{{this.$store.state.num}}</p>
        <p>getter:{{this.$store.getters.getNum}}</p>
        <button @click="add">+1</button>
      </div>
    </template>
    //新增测试代码
    <script>
      export default {
          methods:{
              add(){
                  this.$store.commit('incre',1)
              }
          }
      }
    </script>
    

    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 getters[getterName](this.state)
                    }
                })
            })
    
            let mutations = options.mutations || {}
            this.mutations = {}
            Object.keys(mutations).forEach(mutationName=>{
                this.mutations[mutationName] =  (arg)=> {
                    mutations[mutationName](this.state,arg)
                }
            })
            //新增代码
            let actions = options.actions
            this.actions = {}
            Object.keys(actions).forEach(actionName=>{
                this.actions[actionName] = (arg)=>{
                    actions[actionName](this,arg)
                }
            })
    
        }
        // 新增代码
        dispatch(method,arg){
            this.actions[method](arg)
        }
        commit(method,arg){
            console.log(this);
            this.mutations[method](arg)
        }
        get state(){
            return this.vm.state
        }
    }
    

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

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

      actions: {
        asyncIncre({commit},arg){
            setTimeout(()=>{
              commit('incre',arg)
            },1000)
        }
      },
    

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

    那我们来测试一下。

    
    <template>
      <div id="app">
        123
        <p>state:{{this.$store.state.num}}</p>
        <p>getter:{{this.$store.getters.getNum}}</p>
        <button @click="add">+1</button>
        <button @click="asyncAdd">异步+2</button>
      </div>
    </template>
    
    <script>
      export default {
          methods:{
              add(){
                  this.$store.commit('incre',1)
              },
              asyncAdd(){
                  this.$store.dispatch('asyncIncre',2)
              }
          }
      }
    </script>
    

    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)
    }
    

    发现问题了吧?? 谁调用commit??是$store吗?并不是。所以要解决这个问题,我们必须换成箭头函数

    //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 getters[getterName](this.state)
                    }
                })
            })
    
            let mutations = options.mutations || {}
            this.mutations = {}
            Object.keys(mutations).forEach(mutationName=>{
                this.mutations[mutationName] =  (arg)=> {
                    mutations[mutationName](this.state,arg)
                }
            })
    
            let actions = options.actions
            this.actions = {}
            Object.keys(actions).forEach(actionName=>{
                this.actions[actionName] = (arg)=>{
                    actions[actionName](this,arg)
                }
            })
    
        }
        dispatch(method,arg){
            this.actions[method](arg)
        }
        // 修改代码
        commit=(method,arg)=>{
            console.log(method);
            console.log(this.mutations);
            this.mutations[method](arg)
        }
        get state(){
            return this.vm.state
        }
    }
    

    再来测试

    完美收官!!!!

    补充:有群友问到一个问题,我觉得很有意思,就是说直接通过$store.state.xx = ""。可以吗?其实这样赋值也不会有问题,而且state依旧是响应式的。那么为什么用commit来多此一举呢?

    vuex能够记录每一次state的变化记录,保存状态快照,实现时间漫游/回滚之类的操作

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RrkjCLrY-1596337606360)(https://imgkr2.cn-bj.ufileos.com/8617ccd9-c389-4512-bd9c-a55f8e972a68.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=yKwZcXm2glLeMFGVcaoF3dv6eXc%253D&Expires=1596423850)]

    我有想到一件有意思的事情,要是说我们要实现一个最简单的Vuex,其实只实现state不就好了,其他的getter啊,action,commit都不实现。有种轻装上阵的感觉。其实也能实现。

    而这样实现后发现其实跟全局变量差不多,只不过state是响应式的。

    有什么不理解的或者是建议欢迎评论提出

    感谢您也恭喜您看到这里,我可以卑微的求个star吗!!!

    github:https://github.com/Sunny-lucking/howToBuildMyWebpack

    展开全文
    weixin_43964148 2020-08-01 13:06:02
  • 遇见面试 Vuex原理解析 一、前言 自从学习了VUE框架,其中必不可少的会用到vuex这个核心插件,而且在做项目的时候,基本都会使用,可能你会使用vuex状态管理,但是对vuex原理存在着或多或少的的疑惑或不解,这篇文章...

    遇见面试 Vuex原理解析

    一、前言

    自从学习了VUE框架,其中必不可少的会用到vuex这个核心插件,而且在做项目的时候,基本都会使用,可能你会使用vuex状态管理,但是对vuex原理存在着或多或少的的疑惑或不解,这篇文章就针对vuex原理进行研究,希望能帮助到大家,如果有不准确的地方,大家多多指教。。。

    二、Vuex是什么?

    Vuex是专门为Vue服务,用于管理页面的数据状态、提供统一数据操作的生态系统,相当于数据库mongoDB,MySQL等,任何组件都可以存取仓库中的数据。其中vuex类似的 还是有Redux,Redux大多用于React,针对Redux后续在做补充,现在就让我们好好了解下Vuex到底是个啥东西?

    概念理解性(必读

    Vuex采用MVC模式中的Model层,规定所有的数据必须通过action—>mutaion—>state这个流程进行来改变状态的。再结合Vue的数据视图双向绑定实现页面的更新。统一页面状态管理,可以让复杂的组件交互变的简单清晰,同时在调试时也可以通过DEVtools去查看状态。

    在当前前端的spa模块化项目中不可避免的是某些变量需要在全局范围内引用,此时父子组件的传值,子父组件间的传值,兄弟组件间的传值成了我们需要解决的问题。虽然vue中提供了props(父传子)commit(子传父)兄弟间也可以用localstorage和sessionstorage。但是这种方式在项目开发中带来的问题比他解决的问题(难管理,难维护,代码复杂,安全性低)更多。vuex的诞生也是为了解决这些问题,从而大大提高我们vue项目的开发效率。

    抛出问题

    使用Vuex只需执行 Vue.use(Vuex),并在Vue的配置中传入一个store对象的示例,store是如何实现注入的?
    state内部是如何实现支持模块配置和模块嵌套的?
    在执行dispatch触发action(commit同理)的时候,只需传入(type, payload),action执行函数中第一个参数store从哪里获取的?
    如何区分state是外部直接修改,还是通过mutation方法修改的?
    

    三、vue和vuex关系

    看一下这个vue响应式的例子,vue中的data 、methods、computed,可以实现响应式。

    视图通过点击事件,触发methods中的increment方法,可以更改state中count的值,一旦count值发生变化,computed中的函数能够把getCount更新到视图。

    <div id="app">
            <button @click="increment"></button>
            {{getcount}}
        </app>
        new Vue({
            el: "#app",
            // state
            data () {
             return {
                count: 0
             }
            },
             // view
            computed: {
                getCount(){
                    return this.count
                }
            },
            // actions
            methods: {
             increment () {
                this.count++
             }
            },
        })
    

    那么vuex又和vue这个响应式的例子有什么关系呢?

    我们可以用vuex实现和vue同样的响应式功能。
    

    其实他们原理时一样的,vuex中也有四个属性值:state、getters、mutations、actions。。

    在没有actions的情况下:

    • 数据:state --> data
    • 获取数据:getters --> computed
    • 更改数据:mutations --> methods

    视图通过点击事件,触发mutations中方法,可以更改state中的数据,一旦state数据发生更改,getters把数据反映到视图。

    那么actions,可以理解处理异步,而单纯多加的一层。

    既然提到了mutions actions这时候 就不得不提commit,dispatch这两个有什么作用呢?

    在vue例子中,通过click事件,触发methods中的方法。当存在异步时,而在vuex中需要dispatch来触发actions中的方法,actions中的commit可以触发mutations中的方法。同步,则直接在组件中commit触发vuex中mutations中的方法。

    四、vuex实现

    下面我们看下vuex中能像vue中实现改变状态,更新视图的功能:

    Vuex.js

    const store =  new Vuex.Store({
        
        state: {
            count: 0
        },
        
        //state的值只能通过mutations来修改
        mutations: {
            increment(state) {
                state.count++
            }
        },
        
       //this.$store.commit("increment")触发mutations中函数"increment"
        actions: {
            increment({commit}) {
                 commit("increment"); //this.$store.commit("increment")
            }
         
        },
        
       //通过getter中的方法来获取state值
        getters: {
            getCount(state) {
                return state.count
            }
        }
        })
         
        export default store
    
    

    App.vue

        <template>
        <div id="app">
                <button @click="increment">增加</button>
                <!-- 有时候不能直接 强制使用store里面的状态 this.$store.state.count -->
                {{this.$store.getters.getCount}}
        </div>
        </template>
         
        <script>
        export default {
            methods: {
            increment(){
                    //this.$store.dispatch("increment")触发actions函数"increment"
                    this.$store.dispatch("increment")
                }
            }
        }
        </script>
    
    

    五、源码分析:

    现在我们已经了解vuex能实现和像vue双向数据绑定–更新试图的功能,下面我们重点说说vuex源码的实现:

    5.1、store注入组件install方法

    解答问题:vuex的store是如何注入到组件中的?

    首先使用vuex,需要安装插件:

    Vue.use(Vuex); // vue的插件机制,安装vuex插件
    
    

    当ues(Vuex)时候,会调用vuex中的install方法,装在vuex!
    下面时install的核心源码:

    Vue.mixin({
            beforeCreate() {
                if (this.$options && this.$options.store) {
                    //找到根组件 main 上面挂一个$store
                    this.$store = this.$options.store
                    // console.log(this.$store);
    
                } else {
                    //非根组件指向其父组件的$store
                    this.$store = this.$parent && this.$parent.$store
                }
            }
        })
    

    可见,store注入 vue的实例组件的方式,是通过vue的 mixin机制,借助vue组件的生命周期 钩子 beforeCreate 完成的。即 每个vue组件实例化过程中,会在 beforeCreate 钩子前调用 vuexInit 方法。

    解答问题:vuex的state和getters是如何映射到各个组件实例中响应式更新状态呢?

    5.2、new vue实现双向数据绑定:

            this._s = new Vue({ 
                data: {
                    // 只有data中的数据才是响应式
                    state: options.state
                }
            })
    

    5.3、getters实现

     //实现getters原理
            let getters = options.getters || {}
            // console.log(getters);
            // this.getters = getters; //不是直接挂载到 getters上 这样只会拿到整个 函数体
            this.getters = {};
            // console.log(Object.keys(getters))  // ["myAge","myName"]
            Object.keys(getters).forEach((getterName) => {
                // console.log(getterName)  // myAge
                // 将getterName 放到this.getters = {}中
                // console.log(this.state);
                Object.defineProperty(this.getters, getterName, {
                    // 当你要获取getterName(myAge)会自动调用get方法
                    // 箭头函数中没有this               
                    get: () => {
                        return getters[getterName](this.state)
                    }
                })
            })
    

    从上面源码,我们可以看出Vuex的state状态是响应式,是借助vue的data是响应式,将state存入vue实例组件的data中;Vuex的getters则是借助vue的计算属性computed实现数据实时监听。

    5.4、mutations实现

    
    let mutations = options.mutations || {}
            // console.log(mutations);
            this.mutations = {};
            Object.keys(mutations).forEach(mutationName=>{
                // console.log(mutationName);
                
                this.mutations[mutationName] = (payload) =>{
                    this.mutations[mutationName](this.state,payload)
                }
            })
    

    实现同步加:
    动态效果图:

    5.5、actions实现

    // actions的原理 
            let actions = options.actions || {}
            this.actions = {};
            forEach(actions,(actionName,value)=>{
                this.actions[actionName] = (payload)=>{
                    value(this,payload)
                }
            })
    

    5.6、commit dispatch的实现

        commit(type,payload){
            this.mutations[type](payload)
        }
    
        // type是actions的类型  
        dispatch=(type,payload)=>{
            this.actions[type](payload)
        }
    

    六、原理总结:

    Vuex是通过全局注入store对象,来实现组件间的状态共享。在大型复杂的项目中(多级组件嵌套),需要实现一个组件更改某个数据,多个组件自动获取更改后的数据进行业务逻辑处理,这时候使用vuex比较合适。假如只是多个组件间传递数据,使用vuex未免有点大材小用,其实只用使用组件间常用的通信方法即可。

    附加参考vuex源码,可以调试一下的

    参考链接:https://www.cnblogs.com/hjson/p/10500770.html

    面试常见:https://blog.csdn.net/xu838209490/article/details/80334283

    展开全文
    weixin_44667072 2019-09-22 17:20:48
  • https://segmentfault.com/a/1190000021715850 2、字节跳动面试官:请说一下vuex工作原理(重点就几行代码而已啦) 不知为何掘金的文章最近都流行以 "字节跳动面试官" 作为开头,不蹭一波都不好意思说逛过掘金了。...

    给 「前端开发博客」 加星标,每天打卡学习

    长按二维码即可识别“进入网页”查看哟~

    1、深入理解JavaScript的作用域和作用域链

    (一)作用域是什么了解作用域之前先看一下变量和函数,变量和函数都有一定的访问权限,就是必须满足条件或者在某个范围之内才能访问,这个范围就是作用域。

    https://segmentfault.com/a/1190000021715850

    2、字节跳动面试官:请说一下vuex工作原理(重点就几行代码而已啦)

    不知为何掘金的文章最近都流行以 "字节跳动面试官" 作为开头,不蹭一波都不好意思说逛过掘金了。23333 最近是真到了面试的季节,那么就说一下 Vuex 的源码吧。看完你会发现,Vue和Vuex的实现原理主要就那么几行代码。Vue双向绑定 要说 Vuex 的双向绑定那么必须先从 Vue 的双向绑定开始 Vue 的双...

    https://segmentfault.com/a/1190000021717329

    3、mixins设计模式以及在js中的使用

    在Javascript设计模式一书中介绍了一种设计模式-Mixin 模式。在一些著名的组件库中,Mixins也是十分常见的关键字。但,菜狗的我在日常的开发中好像没怎么用过mixin。看来我和大佬之间的第一个差距就是mixins的使用了(笑) 那到底什么是mixins ,以及怎么使用呢?初识Mixins 在Learning JavaScript Design Patt...

    https://juejin.im/post/5e26b177f265da3e4f19f1b6

    4、大前端之路 -webpack 速成(二)

    当引入一个模块的时候,只引入 我们使用过的代码,那些没引用过的代码,我们就不打包了 另外 tree shaking 只支持 es module ,import模块的引入。不支持 common js 的引入。如果是生产模式 mode: 'production', 复制代码 那么这个tree shaking 默认就是打开的。另外要注意 要在package.json 这边 额外添加 ...

    https://juejin.im/post/5e377db0f265da3e294a50f6

    5、Webpack源码分析 – loader及优化

    https://juejin.im/post/5e40161a6fb9a07cce74d3db

    6、Vue.js渐进式JavaScript框架

    点击蓝色 “达达前端” 关注我哦! 加个 “星标” ,每天一篇文章,一起学编程  2020年02月09日 什么是vue.js?vue.js是一款渐进式的JavaScript框架。什么是渐进式?渐进式就是指可以由浅入深的,由简单到困难的一...

    https://segmentfault.com/a/1190000021714617

    7、5分钟看懂系列:HTTP缓存机制详解

    HTTP 缓存可以说是HTTP性能优化中简单高效的一种优化方式了,缓存是一种保存资源副本并在下次请求时直接使用该副本的技术,当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服...

    https://segmentfault.com/a/1190000021716418

    8、基于 HTML5 WebGL 的智慧城市(一)

    https://www.cnblogs.com/htdaydayup/p/12047850.html

    9、今天,我们来聊一聊WebSocket原理

    WebSocket = “HTTP第1次握手” + TCP 的“全双工“通信 的网络协议。主要过程:首先,通过 HTTP 第一次握手保证连接成功。其次,再通过 TCP 实现浏览器与服务器全双工( full-duplex )通信。(通过不断发 ping 包、 pang 包保持心跳) 最终,使得 “服务端” 拥有 “主动” 发消息给 “客户端” 的能力。...

    https://juejin.im/post/5e414c84518825492d4dd14c

    10、vue前端动态路由实现用户权限过滤

    在后台系统这类的项目中,用户角色和权限的区分是不可分割的一部分需求。常见的用户权限区分形式有两种,一是前端请求接口拿到后台配置的用户权限信息;另一种是直接由前端来定义每种用户角色的权限页面,然后在生成对应的路由。今天我们来了解前端配置路由权限的方式。角色权限页面配置 在配置角色对应的路由页面时...

    https://juejin.im/post/5e412cae6fb9a07c820f9d97

    11、前端开发日报存档

    看得不过瘾,网站有过去几年的每日前端好文章日报存档,关注收藏起来不迷路。

    http://caibaojian.com/c/news

    12、掘金小册全网八折

    热门前端掘金小册,全网八折:前端面试之道、Vue组件精讲、使用WebPack定制前端开发环境、重学前端(极客时间)、前端开发核心知识(GitChat)等,阅读原文浏览全网教程!

    http://caibaojian.com/goods

    加入微信群????,每日分享全网好文章!

    展开全文
    lgno2 2020-02-12 18:08:39
  • 自从学习了Vue框架,其中必不可少的会用到vuex这个核心插件,而且在做项目的时候,基本都会使用,可能你会使用vuex状态管理,但是对vuex原理存在着或多或少的的疑惑或不解,这篇文章就...

    自从学习了Vue框架,其中必不可少的会用到vuex这个核心插件,而且在做项目的时候,基本都会使用,可能你会使用vuex状态管理,但是对vuex原理存在着或多或少的的疑惑或不解,这篇文章就针对vuex原理进行研究,希望能帮助到大家,如果有不准确的地方,大家多多指教。。。

    Vuex是什么?

    Vuex是专门为Vue服务,用于管理页面的数据状态、提供统一数据操作的生态系统,相当于数据库mongoDB,MySQL等,任何组件都可以存取仓库中的数据。其中vuex类似的 还是有Redux,Redux大多用于React,针对Redux后续在做补充,现在就让我们好好了解下Vuex到底是个啥东西?

    概念理解性(必读)

    Vuex采用MVC模式中的Model层,规定所有的数据必须通过action--->mutaion--->state这个流程进行来改变状态的。再结合Vue的数据视图双向绑定实现页面的更新。统一页面状态管理,可以让复杂的组件交互变的简单清晰,同时在调试时也可以通过DEVtools去查看状态。

    在当前前端的spa模块化项目中不可避免的是某些变量需要在全局范围内引用,此时父子组件的传值,子父组件间的传值,兄弟组件间的传值成了我们需要解决的问题。虽然vue中提供了props(父传子)commit(子传父)兄弟间也可以用localstorage和sessionstorage。但是这种方式在项目开发中带来的问题比他解决的问题(难管理,难维护,代码复杂,安全性低)更多。vuex的诞生也是为了解决这些问题,从而大大提高我们vue项目的开发效率。

    抛出问题 使用Vuex只需执行 Vue.use(Vuex),并在Vue的配置中传入一个store对象的示例,store是如何实现注入的?state内部是如何实现支持模块配置和模块嵌套的?在执行dispatch触发action(commit同理)的时候,只需传入(type, payload),action执行函数中第一个参数store从哪里获取的?如何区分state是外部直接修改,还是通过mutation方法修改的?调试时的“时空穿梭”功能是如何实现的?

    vue和vuex的区别与联系

    看一下这个vue响应式的例子,vue中的data 、methods、computed,可以实现响应式。

    视图通过点击事件,触发methods中的increment方法,可以更改state中count的值,一旦count值发生变化,computed中的函数能够把getCount更新到视图。

    <div id="app">
            <button @click="increment"></button>
            {{getcount}}
        </app>
        new Vue({
            el: "#app",
            // state
            data () {
             return {
                count: 0
             }
            },
             // view
            computed: {
                getCount(){
                    return this.count
                }
            },
            // actions
            methods: {
             increment () {
                this.count++
             }
            },
        })
    

    那么vuex又和vue这个响应式的例子有什么关系呢?我们可以用vuex实现和vue同样的响应式功能。其实他们原理时一样的,vuex中也有四个属性值:state、getters、mutations、actions。。在没有actions的情况下:数据:state --> data 获取数据:getters --> computed 更改数据:mutations --> methods

    视图通过点击事件,触发mutations中方法,可以更改state中的数据,一旦state数据发生更改,getters把数据反映到视图。

    那么actions,可以理解处理异步,而单纯多加的一层。

    既然提到了mutions actions这时候 就不得不提 commit dispatch,这两个有什么作用呢?

    在vue例子中,通过click事件,触发methods中的方法。当存在异步时,而在vuex中需要dispatch来触发actions中的方法,actions中的commit可以触发mutations中的方法。同步,则直接在组件中commit触发vuex中mutations中的方法。

    下面我们看下vuex中能像vue中实现改变状态,更新视图的功能:

    Vuex.js

    const store =  new Vuex.Store({
        
        state: {
            count: 0
        },
        
        //state的值只能通过mutations来修改
        mutations: {
            increment(state) {
                state.count++
            }
        },
        
       //this.$store.commit("increment")触发mutations中函数"increment"
        actions: {
            increment({commit}) {
                 commit("increment"); //this.$store.commit("increment")
            }
         
        },
        
       //通过getter中的方法来获取state值
        getters: {
            getCount(state) {
                return state.count
            }
        }
        })
         
        export default store
    
    

    App.vue

        <template>
        <div id="app">
                <button @click="increment">增加</button>
                <!-- 有时候不能直接 强制使用store里面的状态 this.$store.state.count -->
                {{this.$store.getters.getCount}}
        </div>
        </template>
         
        <script>
        export default {
            methods: {
            increment(){
                    //this.$store.dispatch("increment")触发actions函数"increment"
                    this.$store.dispatch("increment")
                }
            }
        }
        </script>
    
    

    现在我们已经了解vuex能实现和像vue双向数据绑定--更新试图的功能,下面我们重点说说vuex源码的实现:

    store注入组件install方法

    解答问题:vuex的store是如何注入到组件中的?

    首先使用vuex,需要安装插件:

    Vue.use(Vuex); // vue的插件机制,安装vuex插件
    
    

    当ues(Vuex)时候,会调用vuex中的install方法,装在vuex! 下面时install的核心源码:

    Vue.mixin({
            beforeCreate() {
                if (this.$options && this.$options.store) {
                    //找到根组件 main 上面挂一个$store
                    this.$store = this.$options.store
                    // console.log(this.$store);
    
                } else {
                    //非根组件指向其父组件的$store
                    this.$store = this.$parent && this.$parent.$store
                }
            }
        })
    

    可见,store注入 vue的实例组件的方式,是通过vue的 mixin机制,借助vue组件的生命周期 钩子 beforeCreate 完成的。即 每个vue组件实例化过程中,会在 beforeCreate 钩子前调用 vuexInit 方法。

    解答问题:vuex的state和getters是如何映射到各个组件实例中响应式更新状态呢?

    new vue实现双向数据绑定:

            this._s = new Vue({
                data: {
                    // 只有data中的数据才是响应式
                    state: options.state
                }
            })
    

    getters实现

     //实现getters原理
            let getters = options.getters || {}
            // console.log(getters);
            // this.getters = getters; //不是直接挂载到 getters上 这样只会拿到整个 函数体
            this.getters = {};
            // console.log(Object.keys(getters))  // ["myAge","myName"]
            Object.keys(getters).forEach((getterName) => {
                // console.log(getterName)  // myAge
                // 将getterName 放到this.getters = {}中
                // console.log(this.state);
                Object.defineProperty(this.getters, getterName, {
                    // 当你要获取getterName(myAge)会自动调用get方法
                    // 箭头函数中没有this
                    get: () => {
                        return getters[getterName](this.state)
                    }
                })
            })
    

    从上面源码,我们可以看出Vuex的state状态是响应式,是借助vue的data是响应式,将state存入vue实例组件的data中;Vuex的getters则是借助vue的计算属性computed实现数据实时监听。

    mutations实现

    
    let mutations = options.mutations || {}
            // console.log(mutations);
            this.mutations = {};
            Object.keys(mutations).forEach(mutationName=>{
                // console.log(mutationName);
                
                this.mutations[mutationName] = (payload) =>{
                    this.mutations[mutationName](this.state,payload)
                }
            })
    

    实现同步加:动态效果图:

    actions实现

    // actions的原理
            let actions = options.actions || {}
            this.actions = {};
            forEach(actions,(actionName,value)=>{
                this.actions[actionName] = (payload)=>{
                    value(this,payload)
                }
            })
    

    commit dispatch的实现

        commit(type,payload){
            this.mutations[type](payload)
        }
    
        // type是actions的类型
        dispatch=(type,payload)=>{
            this.actions[type](payload)
        }
    

    原理总结:

    Vuex是通过全局注入store对象,来实现组件间的状态共享。在大型复杂的项目中(多级组件嵌套),需要实现一个组件更改某个数据,多个组件自动获取更改后的数据进行业务逻辑处理,这时候使用vuex比较合适。假如只是多个组件间传递数据,使用vuex未免有点大材小用,其实只用使用组件间常用的通信方法即可。

    常见面试题:

    1、vuex有哪几种属性?
    答:有五种,分别是 State、 Getter、Mutation 、Action、 Module

    2、vuex的State特性是?
    答:
    一、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于与一般Vue对象里面的data
    二、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
    三、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中

    3、vuex的Getter特性是?
    答:
    一、getters 可以对State进行计算操作,它就是Store的计算属性
    二、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
    三、 如果一个状态只在一个组件内使用,是可以不用getters

    4、vuex的Mutation特性是?
    答:
    一、Action 类似于 mutation,不同在于:
    二、Action 提交的是 mutation,而不是直接变更状态。
    三、Action 可以包含任意异步操作

    5、Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?
    答:
    一、如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
    二、如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用,并包装成promise返回,在调用处用async await处理返回的数据。如果不要复用这个请求,那么直接写在vue文件里很方便。

    6、不用Vuex会带来什么问题?
    答:

    一、可维护性会下降,你要想修改数据,你得维护三个地方

    二、可读性会下降,因为一个组件里的数据,你根本就看不出来是从哪来的

    三、增加耦合,大量的上传派发,会让耦合性大大的增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。

    但兄弟组件有大量通信的,建议一定要用,不管大项目和小项目,因为这样会省很多事

    参考:https://www.cnblogs.com/hjson/p/10500770.html

    展开全文
    lgno2 2020-04-29 07:30:00
  • weixin_30878361 2019-10-01 23:59:47
  • MCCLS 2021-07-27 17:20:56
  • weixin_45295262 2020-11-28 22:51:57
  • weixin_30879169 2018-10-24 08:35:00
  • hanjuyuan 2021-07-24 16:48:17
  • qq_34230117 2020-02-10 07:49:42
  • weixin_42898315 2021-02-17 21:39:29
  • weixin_44003190 2020-02-15 20:41:07
  • qq_38143787 2021-05-19 16:45:17
  • weixin_43392489 2020-09-06 07:48:42
  • weixin_43151921 2019-05-13 23:31:59

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,180
精华内容 2,072
关键字:

vuex原理面试

vue 订阅