vue经典问题
在此,我们详细分析一下几个vue经典问题:
v-if和v-for哪个优先级高
- v-for优先级高,如果v-for和v-if放在同一个元素上,则会先循环再判断条件,所以这导致每次渲染都会先渲染整个列表。
- 解决方法可以把v-if往上一层容器元素放,如果判断条件是列表渲染的内容,则应该用计算属性过滤下要循环的数组。
在源码compiler/codegen/index.js中,只要碰到一个元素,就会执行genElement函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18export function genElement (el: ASTElement, state: CodegenState): string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
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) //for在前面
} else if (el.if && !el.ifProcessed) {
return genIf(el, state) //if在后面
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
key的作用
- key的作用为更高效的更新虚拟DOM。
- vue在patch的过程中,判断两个节点是否是相同节点,key是一个必要条件,如果不定义key,vue会认为两个节点是相同节点,会强制更新,使得整个patch过程比较低效,影响性能。
- 实际渲染列表必须设置key,不要用数组下标,不然如果对这个数组进行元素删除,会串位。
- 从源码中可以知道,vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永远认为这是两个相同节点,只能去做更新操作,这造成了大量的dom更新操作,明显是不可取的。(问:两个节点相同需要更新,还是不同需要更新?)
- patch的过程(patch就是diff),假设在ABCDE的BC中间插入一个F,如果没有定义key,则会做5次更新,1次新增操作,如果定义了key,则会头尾比较(如下),只需要执行一次新增操作。
1 | // 第1次循环patch A |
在源码中,src\core\vdom\patch.js - sameVnode()方法
1 | function sameVnode (a, b) { |
vue在对比两组节点时用的首尾猜测,没有用双循环,节省很多性能。
1 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { |
双向绑定以及它的实现原理
- 双向绑定是指令v-model,v-model是语法糖,默认情况下相当于:value和@input
- 使用v-model可以减少大量繁琐的事件处理代码
- v-model通常在原生表单里使用,在定义组件上如果要使用它需要在组件内绑定value并处理输入事件
diff算法
- diff算法在vue里叫做patch,通过新旧虚拟DOM对比,将变化的地方转换为DOM操作
- 在vue2里,每个组件只有一个watcher,所以要精确找到变化发生的地方,所以要patch
- 组件数据变化时,对应的watcher会通知更新并执行更新函数,它会执行渲染函数获取全新虚拟DOM:newVnode,此时就会执行patch对比上次渲染的oldVnode,和新的渲染结果newVnode。
- patch过程遵循深度优先、同层比较的策略,两个节点之间比较时,如果它们拥有子节点,会先比较子节点;比较两组子节点时,会假设头尾节点可能相同先做尝试,没有找到相同节点后才按照通用方式遍历查找;查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。
vue中组件之间的通信方式?
- 父子组件:props/ $emit / $on / ,$parent / $children ,$attrs / $listeners
- 跨层级:provide / inject ,eventbus,vuex,$root
简单说已说你对vuex理解
- Vuex 是已个专为 Vue.js 应⽤程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- 我在使用vuex过程中有如下理解:先先是对核心概念的理解和运用,将全局状态放入state对象中,它本身一棵状态树,组件中使用store实例的state访问这些状态;然后有配套的mutation方法修改这些状态,并且只能用mutation修改状态,在组件中调用commit方法提交mutation;如果应用中有异步操作或者复杂逻辑组合,我们需要编写action,执⾏结束如果有状态修改仍然需要提交mutation,组件中调用这些action使用dispatch方法派发。最后是模块化,通过modules选项组织拆分出去的各个子模块,在访问状态时注意添加子模块的名称,如果子模块有设置namespace,那么在提交mutation和派发action时还需要额外的命名空间前缀。
- vuex在实现单项数据流时需要做到数据的响应式,通过源码的学习发现是借用了vue的数据响应化特性实现的,它会利用Vue将state作为data对其进⾏响应化处理,从而使得这些状态发生变化时,能够导致组件重新渲染。