vue组件通信的几种方式
-
props
/$emit
父组件通过
props
的方式向子组件传递数据,而通过$emit
子组件可以向父组件通信。 -
$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
周期完成构建的,故 以上 -
provide
/inject
父组件中通过
provide
来提供变量, 然后再子组件中通过inject
来注入变量。注意: 这里不论子组件嵌套有多深, 只要调用了
inject
那么就可以注入provide
中的数据,而不局限于只能从当前父组件的props属性中回去数据 -
ref
/refs
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据 -
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);
-
Vuex
略
-
$attrs
与$listeners
vm.$attrs
和vm.$listeners
可以解决“父组件”和“子组件”、“孙子组件”、“曾孙子组件”等后代组件的通讯问题 -
通过 $root 访问根实例
通过
$root
,任何组件都可以获取当前组件树的根 Vue 实例,通过维护根实例上的data
,就可以实现组件间的数据共享。(笑死,根本维护不起来) -
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 }
-
localStorage / sessionStorage
为了凑够十条通过 window.localStorage.getItem(key) 获取数据
通过 window.localStorage.setItem(key,value) 存储数据
不好维护 不建议使用