Vue组件通信的十种解决方案

vue组件通信的几种方式

  1. props / $emit

    ​ 父组件通过props的方式向子组件传递数据,而通过$emit 子组件可以向父组件通信。

  2. $children / $parent

    通过$parent$children就可以访问组件的实例,不建议使用,建议用上面的方法,组件库的封装除外

    要注意边界情况,如在#app上拿$parent得到的是new Vue()的实例,在这实例上再拿$parent得到的是undefined,而在最底层的子组件拿$children是个空数组。也要注意得到$parent$children的值不一样,$children 的值是数组,而$parent是个对象

    ​ 一个例子: (iview里面的方法)

    ​ 递归调用,向上向下寻找对应name的组件,该方法必须在mounted生命周期中使用.

    /**
    * @desc 寻找指定组件实例
    * @params {String} type 向上查找还是向下查找
    * @params {Object} context 当前上下文(一般指this,把this 传进来就可以了)
    * @params {String} componentName 要寻找的指定的组件名
    */
    function findComponents (type, context, componentName) {
    	if (['$parent', '$children'].indexOf(type) < 0) return
    
    	let currentComponent = context[type]
    	if (type === '$parent') currentComponent = [currentComponent]
    	let designatedCom = null
    
    	if (currentComponent.length) {
    		for(const com of currentComponent) {
    			const name = com.$options.name
    
    			if (name === componentName) {
    				designatedCom = com
    				break
    			} else {
    				designatedCom = findComponents(type, com, componentName)
    				if (designatedCom) break
    			}
    		}
    		return designatedCom
    	}
     }
    

    tips: 为什么须在mounted 周期调用?

    分析源码

    src/core/instance/index.js

    import { initMixin } from './init'
    import { stateMixin } from './state'
    import { renderMixin } from './render'
    import { eventsMixin } from './events'
    import { lifecycleMixin } from './lifecycle'
    import { warn } from '../util/index'
    //此处对应new Vue() 然后调用this._init方法
    function Vue (options) {
      if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)
      ) {
        warn('Vue is a constructor and should be called with the `new` keyword')
      }
      this._init(options)
    }
    
    initMixin(Vue)
    stateMixin(Vue)
    eventsMixin(Vue)
    lifecycleMixin(Vue)
    renderMixin(Vue)
    
    export default Vue
    

    然后打开同级目录下的init.js 里面都是初始化操作.第69行

    if (vm.$options.el) {
          vm.$mount(vm.$options.el)
        }
    

    vue中通过$mount实例方法去挂载DOM,之后的逻辑在vue的runtime-with-compiler组件下,具体的内容略,

    大致上$mount原型会调用mountComponent方法 方法里会进行虚拟Vnode的生成,升值之后更新DOM,同时 会实例化一个watcher用于后续的响应式监听和更新

    updateComponent = () => {
          // 生成虚拟 vnode   
          const vnode = vm._render()
          // 更新 DOM
          vm._update(vnode, hydrating)
         
        }
    
      // 实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法  
      new Watcher(vm, updateComponent, noop, {
        before () {
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'beforeUpdate')
          }
        }
      }, true /* isRenderWatcher */)
      hydrating = false
    

    mountComponent 核心就是先实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,在此方法中调用 vm._render 方法先生成虚拟 Node,最终调用 vm._update 更新 DOM

    所以整个Component Tree的结构是在mounted周期完成构建的,故 以上

  3. provide/ inject

    父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。

    注意: 这里不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据

  4. ref / refs

    ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据

  5. eventBus

    eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。实际上就是new一个Vue实例来进行emit/on操作

    // event-bus.js
    
    import Vue from 'vue'
    export const EventBus = new Vue()
    

    注意: 使用eventBus 要记得在组件销毁时使用 $off移出事件监听,因为eventBus是一个Vue实例 不会随着组件的销毁而消失 ,不执行移除操作可能会带来副作用

        // 移除所有监听事件,当本组件被引用多次时,所有引用的监听全部移除
        this.eventBus.$off("notify");
        // 移除指定监听事件
        this.eventBus.$off("notify", this.listen);
    
    
  6. Vuex

  7. $attrs$listeners

    vm.$attrsvm.$listeners 可以解决“父组件”和“子组件”、“孙子组件”、“曾孙子组件”等后代组件的通讯问题

  8. 通过 $root 访问根实例

    通过 $root,任何组件都可以获取当前组件树的根 Vue 实例,通过维护根实例上的 data,就可以实现组件间的数据共享。(笑死,根本维护不起来)

  9. Vue.observable https://v2.cn.vuejs.org/v2/api/#Vue-observable

    让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。

    返回的对象可以直接用于渲染函数计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器,用于简单的场景:

    const state = Vue.observable({ count: 0 })
    
    const Demo = {
      render(h) {
        return h('button', {
          on: { click: () => { state.count++ }}
        }, `count is: ${state.count}`)
      }
    }
    // 或者直接写的和Vuex差不多
    //store.js
    import Vue from 'vue';
    
    export let store =Vue.observable({count:0,name:'李四'});
    export let mutations={
        setCount(count){
            store.count=count;
        },
        changeName(name){
            store.name=name;
        }
    }
    //某个子组件内
    import {store,mutations} from '@/store'
    ...
     computed:{
         count(){
             return store.count
         },
             name(){
                 return store.name
             }
     },
    ...
     methods:{
         setCount:mutations.setCount,
         changeName:mutations.changeName    
     }
    
  10. localStorage / sessionStorage 为了凑够十条

    通过 window.localStorage.getItem(key) 获取数据

    通过 window.localStorage.setItem(key,value) 存储数据

    不好维护 不建议使用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值