1.虚拟DOM
- 在vue的内部的化,其实又虚拟dom的存在,虚拟dom其实是里面内存形对象存数据 真实dom的一种映射
- 虚拟dom:当数据发生变化的时候大量操作的是虚拟dom,而虚拟dom属于内存数据,操作起来性能要高于操作真实的dom,虚拟dom只有在追加的那一刻才会操作真实dom,大大提升了性能
浏览器加载一个html文件的大致流程是这样的:
- 在内存中生成一颗虚拟dom树
- 将内存中的虚拟dom树初始化渲染成真实的dom树
- 当我们修改data里面的数据的时候
- 将之前的虚拟dom树结合新的数据生成一个新的虚拟dom树
- 将此次生成好的新的虚拟dom树与上一次的虚拟dom树结构进行对比,对比差异(diff算法)
- 将对比出来的差异的部分进行重新真实dom结构的渲染
2.Diff算法
在虚拟dom中,在dom的状态发什么变化时,虚拟dom会进行diff运算,来更新只需要被替换的DOM,而不是全部重绘。
在diff算法中,只有平层的比较前后dom树的结点,没有进行深度的遍历。
- 如果系欸但类型改变,直接将旧节点卸载,喜欢新节点
- 节点类型不变,属性或者属性值改变,不会卸载节点,执行节点更新的操作
- 文本改变,直接修改文字内容
- 移动,增加,删除子节点时:
如果想在中间插入节点F,简单粗暴的做法就是卸载c,装载f,卸载d,装载c,卸载e,装载d,装载e
写代码时,如果没有给数组或枚举类型定义一个key,就会采用上面简单粗暴的方法
如果为元素增加key以后,Vue会根据key,直接找到具体的位置进行操作,效率比较高,如下图:
问题:什么是虚拟dom?与key值的关系?
- 当用传统的方式操作DOM的时候,浏览器会从构建DOM树开始从头到尾执行一遍流程,效率很低。而虚拟DOM是用javascript对象表示的,而操作javascript是很简便高效的。虚拟DOM和真正的DOM有一层映射关系,很多需要操作DOM的地方都会去操作虚拟DOM,最后统一一次更新DOM。因而可以提高性能
- 在虚拟dom中,移除 增加 删除子节点的时候,如果没有添加key值,他会采用我们上面所说的简单粗暴的做法,当然如果给元素添加了key属性之后,就不会出现这样的问题了。key属性一方面可以提高虚拟dom的对比性,一方面也会避免出错,还可以避免元素的复用。
3.父子之间的通信传值是如何实现的
- 父组件在调用子组件的时候,可以通过v-bind来为子组件传递数据,父组件数据变化子组件数据也会变化
- 子组件可以通过props的属性接收父组件传递过来的数据
- 如果子组件没有通过props属性接收数据,这个数据就会被挂在子组件模板的根节点。看下图。
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>这是father组件!---> {{pMsg}}</p>
<son :msg="pMsg"></son><!--父组件在调用子组件的时候 绑定一个属性 可以通过props接收 --> 对应第一步
</div>
</template>
Vue.component("father",{
template:"#father",
data:()=>{
return{
pMsg:"hello father"
}
},
components:{
son:{
template"<div>这是son子组件</div>",
props:["msg"] //接收父组件传递来的属性 ->对应第二不
}
}
})
let vm = new Vue({
el:"#app",
data:{
msg:"hello app!"
}
})
问题:为什么组件中data必须是一个函数返回对象的形式呢?
- 目的是让每一个实例可以维护一份被返回对象的独立拷贝。(每个组件使用到的地址不一样)
- 换言之:内部必须要返回一个对象的写法,这样就可以保证每个组件里面用到的数据对象都是唯一的。
4.子父之间的通信传值是如何实现的?
- 在父组件内部声明一条数据
- 在父组件内部再写一个改变自身数据的方法
- 父组件在调用子组件的时候,给其自身绑定一个自定义事件 change <son @change=“changeMsg”>
- 当子组件触发这个事件的时候,相当于父组件的方法被执行了… (this.$emit(自定义事件,参数))
- 子组件就通过$emit将数据传递给了父组件
5.兄弟之间的通信时如何实现的?
- 关系链+ref链可以实现兄弟之间的通信
- events bus 事件总线也可以实现
6.关系链(viewmodel链)
- 组件 实例对象上有这样的属性: p a r e n t , parent, parent,children,$root
- 这样的话就形成了viewmodel链,理论上来说
- 任何两个组件之间的数据都可以相互调用,获取
- 缺点:太乱了 this. r o o t . root. root.children[3].$children[4]…
set(val){
console.log(val)
//this.msg = val; //对于父组件传递来的属性进行了更改
this.$parent.msg = val //相当于找到aaa组件。把滋生的msg赋值给aaa组件的msg
}
7.ref链
- 组件间可以通过 r o o t / root/ root/parent/$children来获取对应的组件
- 父组件还可以主动的通过ref为子组件做标记 也可以给dom做标记
- 也会形成ref链,也可以交互
<template id="aaa">
<div>
<button ref="btn" @click="get">get</button> //这样打标记
<bbb ref="b"></bbb>
</div>
</template>
get(){
// this.$children[0].msg = "get" //是通过关系链(viewModel)去实现 this代表aaa 表示b组件得msg属性
console.log(this.$refs)//{b:vueconponent}指向b组件,前期要做标记ref="b" 被标记的都会显示在这个对象里面
this.$refs.b.msg = "get" //通过ref链给组件做标记
}
8.事件总线
-
EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的“灾难”,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。
-
事件总线可以实现兄弟之间的通信
- 大致步骤
- 创建一个公共的vue实例
- 在mounted()生命周期钩子函数中绑定好事件
- 触发事件 一旦触发所有地方都能接收到,这就是事件总线
9.slot槽口
- 在组建标签内部写入的内容默认会被替换掉
- 如果想要在组件的模板里使用这些内容
- 就在对应位置的模板里写上solt标签,这个slot标签就代表着这些内容
- 具名槽口
- slot标签上通过name属性指定槽口名称 是在模板内
- 然后使用的时候通过slot="槽口名称“ 是在标签内
vue2.6版本推荐的slot槽口的写法
- v-slot在使用时,需要在template标签内,这点大家要注意
- 刚开始还是在模板里面写一个slot标签,通过name属性指定槽口的名称
- 在标签内先写一个template标签里面写内容,在template后面通过 v-slot:槽口名称
<div id="app">
<hello>
<template v-slot:a>
联通卡
</template>
<template v-slot:b="info"><!-- 用作用域槽口 直接拿不到msg -->
<div>移动卡1</div> {{info.msg}}<!-- 拿到是是一个对象,要在组件上绑定一个属性 -->
<div>移动卡2</div>
</template>
</hello>
</div>
<template id="hello">
<div>
<slot name="a"></slot>
<slot name="b" :msg="msg"></slot>
</div>
</template>
10.prop验证
- 我们可以为组件的 prop 指定验证规则。如果传入的数据不符合要求,Vue 会发出警告。这对于开发给他人使用的组件非常有用
- 验证主要分为:类型验证、必传验证、默认值设置、自定义验证
- prop的值可以是对象,在里面可以对父组件传递过来的数据进行验证
son:{
template:"#son",
props:["num"] //1.直接接收父组件传递过来的数据
props:{
num:Number //2.传递过来的必须要是数字类型
num:[Number,String]//3.传递过来的必须时数字 字符串类型
num:{//4.必须时数字类型,默认1000,必填
type:Number,
default:1000,
required:true //必填
}
num:{//5.传递来的数据必须大于5
type:Number,
validator(val){
return val>5
}
}
}
}
11.单项数据流
- Vue/React单向数据流的体现:数据只能从父级组件流向子级组件
- prop时单项绑定的:当父组件的属性变化时,将传递给子组件,但是反过来不会,这是为了防止子组件无意间修改了父组件的状态,来避免应用的数据流变得难以理解。
- 另外,每次父组件更新时,子组件的所有prop都会更新为最新值。这意味着你不应该在子组件内部改变prop,如果你这么做了,vue后台会给出警告。
注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。 message:{val:""}