Vue组件之间通信方式有哪些

vue是组件化开发框架,所以对于vue应用来说组件间的数据通信非常重要。
1. 组件通信常用方式有以下8种:
-  props 
-  $emit/ $on
-  $children/$parent
-  $attrs/ $listeners
-  ref 
-  $root 
-  eventbus 
-  vuex 
注意vue3中废弃的几个API
在vue3中废除$children
移除$listeners
$on、$off和$once实例方法被移除
2.根据组件之间关系讨论组件通信最为清晰有效
-  父子组件 
props / $emit / $parent / ref / $attrs
-  兄弟组件 
$parent / $root / eventbus / vuex
-  跨层级关系 
eventbus / vuex / provide + inject
v-if和v-for优先级问题
文档中曾有详细说明v2|v3;
v2中文文档地址 https://cn.vuejs.org/v2/style-guide/#%E9%81%BF%E5%85%8D-v-if-%E5%92%8C-v-for-%E7%94%A8%E5%9C%A8%E4%B8%80%E8%B5%B7%E5%BF%85%E8%A6%81
https://cn.vuejs.org/v2/style-guide/#%E9%81%BF%E5%85%8D-v-if-%E5%92%8C-v-for-%E7%94%A8%E5%9C%A8%E4%B8%80%E8%B5%B7%E5%BF%85%E8%A6%81
 v3中文文档地址 https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for
https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for

-  实践中不应该把v-for和v-if放一起 
-  在vue2中,v-for的优先级是高于v-if,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在vue3中则完全相反,v-if的优先级高于v-for,所以v-if执行时,它调用的变量还不存在,就会导致异常 
-  通常有两种情况下导致我们这样做: 
-  为了过滤列表中的项目 (比如 v-for="user in users" v-if="user.isActive" )。此时定义一个计算属性 (比如 activeUsers ),让其返回过滤后的列表即可(比如users.filter(u=>u.isActive) )。 
-  为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers" )。此时把 v-if 移动至容器元素上 (比如 ul 、 ol )或者外面包一层 template 即可。 
-  文档中明确指出永远不要把 v-if 和 v-for 同时用在同一个元素上,显然这是一个重要的注意事项。 
-  源码里面关于代码生成的部分,能够清晰的看到是先处理v-if还是v-for,顺序上vue2和vue3正好相反,因此产生了一些症状的不同,但是不管怎样都是不能把它们写在一起的。 
知其所以然:
v2源码
	if (el.staticRoot && !el.staticProcessed) {
    return genStatic(el, state)
  } else if (el.once && !el.onceProcessed) {
    return genOnce(el, state)
  } else if (el.for && !el.forProcessed) {
    return genFor(el, state)
  } else if (el.if && !el.ifProcessed) {
    return genIf(el, state)
  } else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
    return genChildren(el, state) || 'void 0'
  } else if (el.tag === 'slot') {
    return genSlot(el, state)
  }	JavaScript
v3源码
switch(node.type){
case NodeTypes.ELEMENT:
case NodeTypes.IF:
case NodeTypes.FOR;
	__DEV__&&
		assert(
			node.codegenNode != null,
			`Codegen node is missing for element/if/for node. `+
				`Apply appropriate transforms first.`
		)
	genNode(node.codegenNode!, context)
	break
}JavaScript
Vue的生命周期以及每个阶段做的事情
1.每个Vue组件实例被创建后都会经过一系列初始化步骤,比如,它需要数据观测,模板编译,挂载实例到dom上,以及数据变化时更新dom。这个过程中会运行叫做生命周期钩子的函数,以便用户在特定阶段有机会添加他们自己的代码。
2.Vue生命周期总共可以分为8个阶段:创建前后, 载入前后, 更新前后, 销毁前后,以及一些特殊场景的生命周期。vue3中新增了三个用于调试和服务端渲染场景。
| 生命周期V2 | 生命周期V3 | 描述 | 
| beforeCreate | beforeCreate | 组件实例被创建之初 | 
| created | created | 组件实例已经完全创建 | 
| beforeMount | beforeMount | 组件挂载之前 | 
| mounted | mounted | 组件挂载到实例上去之后 | 
| beforeUpdate | beforeUpdate | 组件数据发生变化,更新之前 | 
| updated | updated | 数据数据更新之后 | 
| beforeDestroy | beforeUnmounted | 组件实例销毁之前 | 
| destroyed | unmounted | 组件实例销毁之后 | 
| 生命周期V2 | 生命周期V3 | 描述 | 
| activated | activated | keep-alive 缓存的组件激活时 | 
| deactivated | deactivated | keep-alive 缓存的组件停用时调用 | 
| errorCaptured | errorCaptured | 捕获一个来自子孙组件的错误时被调用 | 
| - | renderTracked | 调试钩子,响应式依赖被收集时调用 | 
| - | renderTriggered | 调试钩子,响应式依赖被触发时调用 | 
| - | serverPrefetch | ssr only,组件实例在服务器上被渲染前调用 | 
-  生命周期流程图: 
 
-  结合实践: 
beforeCreate:通常用于插件开发中执行一些初始化任务。
created:组件初始化完毕,可以访问各种数据,获取接口数据等。
mounted:dom已创建,可用于获取访问数据和dom元素;访问子组件等。
beforeUpdate:此时 view 层还未更新,可用于获取更新前各种状态。
updated:完成 view 层的更新,更新后,所有状态已是最新。
beforeunmounted:实例被销毁前调用,可用于一些定时器或订阅的取消。
unmounted:销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器。
注意 :
setup执行时间比created更早 为了兼容vue2、 beforeCreate 和 created 在vue3中可以正常使用,但是不能在setup 中 写 onbeforeCreate 以及 created
Vue双向绑定+响应式原理
数据的变化影响了试图 试图的变化影响了数据 这就是mvvm

-  原理 
mvvm是通过数据劫持实现的,通过Object.defineProperty(),通过对各个属性进行一个getter,setter进行一个监听,当数据发生变化的时候通知订阅者,触发回调函数,更新试图。
-  数据劫持 
mvvm 是一个类 通过Observer (劫持监听所有属性、setter)在通过Watcher(观察者模式)通知变化,在通过添加订阅者,再更新试图。 最后通过Compile解析指令。
指令解析器---------Compile
数据监听器--------Observer
更新试图-----------Watcher
vue是采用数据劫持配合发布者-订阅者模式的方式,通过Object.definerProperty()来劫持各个属性的setter 和 getter ,在数据变动时,发布消息给依赖收集器,去通知观察者更新,做出对应的回调函数,去更新试图。
MVVM 作为绑定的入口,整合了Observer,Compile 和Watcher 三者,通过Observer来监听model 数据变化表,通过Compile 来解析编译模版指令,最终利用Watcher搭起Observer 和Compile 之间的通信桥梁。达到数据变化去影响试图的更新,试图交互变化影响数据model变更的双向绑定效果
-  MVVM框架中要解决的一个核心问题是连接数据层和视图层,通过数据驱动应用,数据变化,视图更新,要做到这点的就需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出更新处理。 
-  以vue为例说明,通过数据响应式加上虚拟DOM和patch算法,开发人员只需要操作数据,关心业务,完全不用接触繁琐的DOM操作,从而大大提升开发效率,降低开发难度。 
-  vue2中的数据响应式会根据数据类型来做不同处理,如果是对象则采用Object.defineProperty()的方式定义数据拦截,当数据被访问或发生变化时,我们感知并作出响应;如果是数组则通过覆盖数组对象原型的7个变更方法,使这些方法可以额外的做更新通知,从而作出响应。这种机制很好的解决了数据响应化的问题,但在实际使用中也存在一些缺点:比如初始化时的递归遍历会造成性能损失;新增或删除属性时需要用户使用Vue.set/delete这样特殊的api才能生效;对于es6中新产生的Map、Set这些数据结构不支持等问题。 
-  为了解决这些问题,vue3重新编写了这一部分的实现:利用ES6的Proxy代理要响应化的数据,它有很多好处,编程体验是一致的,不需要使用特殊api,初始化性能和内存消耗都得到了大幅改善;另外由于响应化的实现代码抽取为独立的reactivity包,使得我们可以更灵活的使用它,第三方的扩展开发起来更加灵活了。 
nextTick的用法和原理
nextTick是用于获取下次DOM更新刷新的使用函数。
-  Vue有个异步更新策略,意思是如果数据变化,Vue不会立刻更新DOM,而是开启一个队列,把组件更新函数保存在队列中,在同一事件循环中发生的所有数据变更会异步的批量更新。这一策略导致我们对数据的修改不会立刻体现在DOM上,此时如果想要获取更新后的DOM状态,就需要使用nextTick。 
-  开发时,比如我希望获取列表更新后的高度就可以通过nextTick实现。 
-  nextTick签名如下: function nextTick(callback?: () => void): Promise<void> 所以我们只需要在传入的回调函数中访问最新DOM状态即可,或者我们可以await nextTick方法返回的Promise之后做这件事。 
-  在Vue内部,nextTick之所以能够让我们看到DOM更新后的结果,是因为我们传入的callback会被添加到队列刷新函数(flushSchedulerQueue)的后面,这样等队列内部的更新函数都执行之后,所有DOM操作也就结束了,callback自然能够获取到最新的DOM值。 
剑飞csdn博客附加$set
watch和computed的区别
-  计算属性可以从组件数据派生出新数据,最常⻅的使用方式是设置一个函数,返回计算之后的结果,computed和methods的差异是它具备缓存性,如果依赖项不变时不会重新计算。侦听器可以侦测某个响应式数据的变化并执行副作用,常⻅用法是传递一个函数,执行副作用,watch没有返回值,但可以执行异步操作等复杂逻辑。 
-  计算属性常用场景是简化行内模板中的复杂表达式,模板中出现太多逻辑会是模板变得臃肿不易维护。侦听器常用场景是状态变化之后做一些额外的DOM操作或者异步操作。选择采用何用方案时首先看是否需要派生出新值,基本能用计算属性实现的方式首选计算属性。 
-  使用过程中有一些细节,比如计算属性也是可以传递对象,成为既可读又可写的计算属性。watch可以传递对象,设置deep、immediate等选项。 
-  vue3中watch选项发生了一些变化,例如不再能侦测一个点操作符之外的字符串形式的表达式; reactivityAPI中新出现了watch、watchEffect可以完全替代目前的watch选项,且功能更加强大。 


















