vue常见面试题
v-if与v-show的区别:v-showv-show通过CSS dispaly控制显示和隐藏v-if组件真正的渲染和销毁,而不是显示和隐藏频繁切换状态使用v-show,否则使用v-if为何要在v-for中使用key必须使用key,且不能是index和randomdiff算法中通过tag和key来判断,是否sanmeNode减少渲染次数,提升渲染性能(如果发现是sameNode,那么会将node的
·
目录
v-if与v-show的区别:
- v-showv-show通过CSS dispaly控制显示和隐藏
- v-if组件真正的渲染和销毁,而不是显示和隐藏
- 频繁切换状态使用v-show,否则使用v-if
对MVVM开发模式的理解:
- Model 代表数据模型,数据和业务逻辑都在Model层中定义;
- View 代表UI视图,负责数据的展示;
- ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作
- Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步
VUE组件data为什么必须是函数
- vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的
methods,computed,watch
- computed:可以进行缓存,只有在它的相关依赖发生改变时才会重新计算值,常适用于与一个数据受多个数据影响
- method:只要发生重新渲染,methods调用总会执行该函数
- watch:一般用于一个数据影响多个数据,或者在数据变化时来执行异步操作
为何要在v-for中使用key
- 必须使用key,且不能是index和random
- diff算法中通过tag和key来判断,是否sanmeNode
- 减少渲染次数,提升渲染性能(如果发现是sameNode,那么会将node的位置进行移动)
描述Vue组件生命周期(父子组件)
- beforeCreate(创建前) 在数据观测和初始化事件还未开始
- created(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来
- beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
- mounted(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
- beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
- updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
- beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
- destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
vue组件如何通讯
- 父子组件传值:父组件给子组件传值,子组件通过props进行接受;子组件向父组件传值,通过$emit方法传递参数,在父组件中穿件子组件时,需要监听$emit的事件
- 兄弟组件传值:eventBus,创建一个事件中心(是new的一个vue实例),通过event.$on和$event.$emit发布订阅的方式实现通讯,可参考(vue - 组件之间的通信)
- vuex
描述组件和渲染的过程
- 初次渲染:
- 解析模板为render函数(或在开发环境下已完成,vue-loader)
- 触发响应式,监听data属性,setter和getter
- 执行render函数,返回生成vnode,再执行patch(elem, vnode),页面渲染(在执行render函数时,就已经执行了data的getter,但不会引起视图变化的不会触发get)
- 更新过程
- 修改data,触发setter(此前在getter中已被监听)
- 重新执行render函数,生成newVnode
- 执行patch(vnode, newVnode)
- 流程图参考如下:
何时要使用beforeDestory
- 解绑自定义事件event.$off
- 清除定时器
- 解绑自定义的DOM事件,如window scroll等
什么是作用域插槽
- 作用域插槽给了子组件将数据返回组组件的能力,子组件一样可以复用,同时父组件也可以重新组织内容和样式
- 一下是一个简单的demo:
Vuex中action和mutation有何区别:
- action处理异步,mutation不可以
- mutation专注于修改state,而action是提交的mutation,而不直接变更状态
- action可以整合多个mutation
vue-router常用的路由模式
- hash(默认):hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误
- H5 history:且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误
如何配置vue-router异步加载
请用vnode描述一个DOM结构
监听data变化的核心的API是什么?
- Object.defineProperty
- Object.defineProperty有和缺点
- 深度监听,需要递归到底,计算量大
- 对删除的属性和新增的属性无法实现监听,需要使用Vue.set和Vue.delete
- 无法监听数组,需要特殊处理
- 实现如下:该实现通过重写数组的常用方法实现数组的监听
const data = {
name: 'zhangsan',
age:21,
info: {
address: 'BeiJing'
},
num: [1,2,3]
}
// 重新定义数组的原型,不建议直接重写数组的方法,否则就会污染全局的原型方法
let oldArrayProperty = Array.prototype;
var arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function(){
updateView ();
oldArrayProperty[methodName].call(this, ...arguments);
}
})
function updateView(){
console.log('update view');
}
function observer(target){
if (typeof target !== 'object' || target === null){
return target;
}
if (Array.isArray(target)){
target.__proto__ = arrProto;
}
for (let key in target){
defineReactive(target, key, target[key])
}
}
function defineReactive(target, key, value){
// 深度监听
observer(value);
Object.defineProperty(target, key, {
get(){
return value
},
set(newValue){
// 设置的新值可能还是一个object
observer(newValue);
if (newValue !== value){
value = newValue
updateView();
}
}
})
}
// 执行observer必须等数组方法重写后进行监听
observer(data);
data.name = 'lisi';
data.age = 20;
data.info.address = 'ShangHai';
data.num.push(4)
请描述响应式原理
- 当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter,在属性被访问和修改时通知变化。
- 每个组件实例都有相应的 watcher 程序实例,当data发生变化是,之后setter 被调用,会通知 watcher 重新计算,从而执行render函数,致使它关联的组件得以更新。
- 可参考上面题目的流程图:“描述组件和渲染的过程”
简述diff算法过程
- patch函数:patch(elem, vnode) 和 patch(vnode, newVnode)
- 当第一个参数不是vnode时,创建一个空的vnode关联到dom元素上
- 当两个vnode不一致时,创建新的vnode,将旧的vnode删除
- 当两个vnode一致时,执行patchVnode函数
- patchVnode函数:
- 该函数主要用于对旧vnode和新vnode中的children进行对比,从而进行对应操作
- 当旧vnode的children和新vnode的children同时存在,执行updateChildren
- 当新的children有,旧的text有,那么将旧的vnode的text设置为空,添加新的children
- 当新的children无,旧的children有,删除旧的children
- 当新的vnode有text时,且不等于旧的vnode的text时,将旧的children移除,设置新的text
- updateChildren
- 该函数主要用于对children进行更新,分别从旧的children和新的children中每一项对比,如果有发现同样的vnode,那么再次执行patcVnode函数
- sameVnode:
- 用于判断是否是相同的vnode,通过key和sel进行比较,这也是为什么for循环中一定要有key
Vue为何是异步渲染,$nextTick何用
- 异步渲染(以及合并data修改),以提高渲染性能,否则每次更新数据都会对当前组件进行重新渲染
- $nextTick用于在DOM更新完之后,触发回调
keep-alive
- keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们
- 属性:
- include: 字符串或正则表达式,只有名称匹配的组件会被缓存
- exclude: 字符串或正则表达式,名称匹配的组件不会被缓存》
- max: 数字,最多可以缓存多少组件实例
- 生命周期:
-
activated和deactivated会在keep-alive树内所有嵌套的组件中触发
如:B页面是缓存页面
当A页面跳到B页面时,B页面的生命周期:activated(可在此时更新数据)
B页面跳出时,触发deactivated
B页面自身刷新时,会触发created-mouted-activated
-
-
router.meta 属性来实现
export default {
name: 'hello',
//keep-alive钩子函数:组件被激活时调用
activated() {
console.log('首页被激活');
},
//keep-alive钩子函数:组件消失,被缓存时调用
deactivated() {
console.log('首页被缓存');
},
beforeRouteLeave(to, from, next) {
//设置下一个路由的meta(即首页)
to.meta.keepAlive = true; // 让首页缓存,即不刷新
next();
}
}
Vue常见性能优化:
- 合理使用v-if和v-show
- 合理使用computed
- v-for加key,以及避免和v-if同时使用(v-for比v-if的优先级高,所以每次都要v-for之后再v-if,所以避免使用)
- 自定义事件,DOM事件,定时即使销毁
- 合理使用异步组件
- 合理使用keep-alive(对需要进行缓存的时候keep-alive)
- data层级不要太深
- webpack层面的性能优化
更多推荐
已为社区贡献3条内容
所有评论(0)