Vue在created和mounted这两个生命周期中请求数据有什么区别呢?
在created中,页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态,DOM节点没出来,无法操作DOM节点。在mounted不会这样,比较好。
说说你对keep-alive的理解
keep-alive是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
其有三个参数
include
定义缓存白名单,会缓存的组件;exclude
定义缓存黑名单,不会缓存的组件;- 以上两个参数可以是逗号分隔字符串、正则表达式或一个数组,
include="a,b"
、:include="/a|b/"
、:include="['a', 'b']"
;- 匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配;
max
最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉;- 不会在函数式组件中正常工作,因为它们没有缓存实例;
- 当组件在内被切换,它的activated和deactivated这两个生命周期钩子函数将会被对应执行。
v-if和v-for的优先级是什么?如果这两个同时出现时,那应该怎么优化才能得到更好的性能?
当它们处于同一节点,
v-for
的优先级比v-if
更高,这意味着v-if
将分别重复运行于每个v-for
循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用。
<ul>
<li v-for="item in items" v-if="item.show">{{item}}</li>
</ul>
如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或
<template>
)上。
<ul v-if="items.length">
<li v-for="item in items">{{item}}</li>
</ul>
使用v-for遍历对象时,是按什么顺序遍历的?如何保证顺序?
按Object.keys()的顺序的遍历,转成数组保证顺序。
在v-for中使用key,会提升性能吗,为什么?
主要看v-for渲染的是什么。
如果渲染是一个简单的列表,如不依赖子组件状态或临时DOM状态(例如:表单输入值)的列表渲染输出,不用key性能会更好,因为不用key采用的是“就地更新”的策略。如果数据项的顺序被改变, Vue将不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素。
<template>
<div>
<span v-for="item in lists">{{item}}</span>
</div>
</template>
<script>
export default {
data() {
return {
lists: [1, 2, 3, 4, 5]
}
},
}
</script>
以上的例子,v-for的内容会生成以下的DOM节点数组,我们给每一个节点标记一个身份id,以辨别节点的位置:
[
'<span>1</span>', // id: A
'<span>2</span>', // id: B
'<span>3</span>', // id: C
'<span>4</span>', // id: D
'<span>5</span>' // id: E
]
将lists中的数据进行位置调换,变成
[2,4,3,1,5]
,在没有key的情景下,节点位置不变,但是节点的内容更新了,这就是“就地更新”
[
'<span>2</span>', // id: A
'<span>4</span>', // id: B
'<span>3</span>', // id: C
'<span>1</span>', // id: D
'<span>5</span>' // id: E
]
但是在有key的情景下,节点位置进行了交换,但是内容没有更新
[
'<span>2</span>', // id: B
'<span>4</span>', // id: D
'<span>3</span>', // id: C
'<span>1</span>', // id: A
'<span>5</span>' // id: E
]
如果渲染不是一个简单的列表,用key性能会更好一点,因为vue是采用diff算法来对比新旧虚拟节点来更新节点,在diff算法中,当新节点跟旧节点头尾交叉对比没有结果时,先处理旧节点生成一个健为key,值为节点下标index的map映射,如果新节点有key,会通过map映射找到对应的旧节点,如果新节点没有key,会采用遍历查找的方式去找到对应的旧节点,一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。
// vue源码 src/core/vdom/patch.js 488行
// 以下是为了阅读性进行格式化后的代码
// oldCh 是一个旧虚拟节点数组
// oldKeyToIdx map映射对象
// idxInOld 对比后得到旧节点下标
if (isUndef(oldKeyToIdx)) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
}
if (isDef(newStartVnode.key)) {
// map 方式获取
idxInOld = oldKeyToIdx[newStartVnode.key]
} else {
// 遍历方式获取
idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
}
创建map函数
function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
遍历寻找函数
// sameVnode 是对比新旧节点是否相同的函数
function findIdxInOld(node, oldCh, start, end) {
for (let i = start; i < end; i++) {
const c = oldCh[i];
if (isDef(c) && sameVnode(node, c)) return i
}
}
key除了在v-for中使用,还有什么作用?
还可以强制替换元素/组件而不是重复使用它。在以下场景可以使用
- 完整地触发组件的生命周期钩子
- 触发过渡
<transition>
<span :key="text">{{ text }}</span>
</transition>
当 text 发生改变时,
<span>
会随时被更新,因此会触发过渡。
使用key要什么要注意的吗?
不要使用对象或数组之类的非基本类型值作为key,请用字符串或数值类型的值;
不要使用数组的index作为key值,因为在删除数组某一项,index也会随之变化,导致key变化,渲染会出错。
例:在渲染
[a,b,c]
用 index 作为 key,那么在删除第二项的时候,index 就会从 0 1 2 变成 0 1(而不是 0 2),随之第三项的key变成1了,就会误把第三项删除了。
说说组件的命名规范
组件命名有两种方式,一种是使用链式命名my-component,一种是使用大驼峰命名MyComponent,
在字符串模板中
<my-component></my-component>
和<MyComponent></MyComponent>
都可以使用,在非字符串模板中最好使用
<MyComponent></MyComponent>
,因为要遵循W3C规范中的自定义组件名(字母全小写且必须包含一个连字符),避免和当前以及未来的 HTML 元素相冲突。
为什么组件中data必须用函数返回一个对象?
对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题。
Vue父子组件双向绑定的方法有哪些?
- 通过在父组件上自定义一个监听事件
<myComponent @diy="handleDiy"></myComponent>
,在子组件用this.$emit('diy',data)
来触发这个diy事件,其中data为子组件向父组件通信的数据,在父组件中监听diy个事件时,可以通过$event访问data这个值。- 通过在父组件上用修饰符
.sync
绑定一个数据<myComponent :show.sync="show"></myComponent>
,在子组件用this.$emit('update:show',data)
来改变父组件中show
的值。- 通过
v-model
。
组件的name选项有什么作用?
- 递归组件时,组件调用自身使用;
- 用
is
特殊特性和component
内置组件标签时使用;keep-alive
内置组件标签中include
和exclude
属性中使用。
什么是递归组件?举个例子说明下?
递归引用可以理解为组件调用自身,在开发多级菜单组件时就会用到,调用前要先设置组件的name选项, 注意一定要配合v-if使用,避免形成死循环,用element-vue组件库中NavMenu导航菜单组件开发多级菜单为例:
<template>
<el-submenu :index="menu.id" popper-class="layout-sider-submenu" :key="menu.id">
<template slot="title">
<Icon :type="menu.icon" v-if="menu.icon"/>
<span>{{menu.title}}</span>
</template>
<template v-for="(child,i) in menu.menus">
<side-menu-item v-if="Array.isArray(child.menus) && child.menus.length" :menu="child"></side-menu-item>
<el-menu-item :index="child.id" :key="child.id" v-else>
<Icon :type="child.icon" v-if="child.icon"/>
<span>{{child.title}}</span>
</el-menu-item>
</template>
</el-submenu>
</template>
<script>
export default{
name: 'sideMenuItem',
props: {
menu: {
type: Object,
default(){
return {};
}
}
}
}
</script>
说下$attrs
和$listeners
的使用场景?
$attrs
: 包含了父作用域中(组件标签)不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。 在创建基础组件时候经常使用,可以和组件选项inheritAttrs:false
和配合使用在组件内部标签上用v-bind="$attrs"
将非prop特性绑定上去;$listeners
: 包含了父作用域中(组件标签)的 (不含.native
) v-on 事件监听器。 在组件上监听一些特定的事件,比如focus事件时,如果组件的根元素不是表单元素的,则监听不到,那么可以用v-on="$listeners"
绑定到表单元素标签上解决。
EventBus注册在全局上时,路由切换时会重复触发事件,如何解决呢?
在有使用
$on
的组件中要在beforeDestroy
钩子函数中用$off
销毁。