vue面试
基本使用
1.插值和指令
v-html
有xss风险
2.computed和watch
computed:有缓存效果,data中的值不变则不运行
如何深度监听
在监听数组对象时,监听不到内部的变化,只有打开深度监听
在watch中添加:deep:true
监听引用类型,拿不到oldval
watch: {
name(oldVal, val) {
// eslint-disable-next-line
console.log('watch name', oldVal, val) // 值类型,可正常拿到 oldVal 和 val
},
info: {
handler(oldVal, val) {
// eslint-disable-next-line
console.log('watch info', oldVal, val) // 引用类型,拿不到 oldVal 。因为指针相同,此时已经指向了新的 val
},
deep: true // 深度监听
}
}
3.class和style
<template>
<div>
<p :class="{ black: isBlack, yellow: isYellow }">使用 class</p>
<p :class="[black, yellow]">使用 class (数组)</p>
<p :style="styleData">使用 style</p>
</div>
</template>
<script>
export default {
data() {
return {
isBlack: true,
isYellow: true,
black: 'black',
yellow: 'yellow',
styleData: {
fontSize: '40px', // 转换为驼峰式
color: 'red',
backgroundColor: '#ccc' // 转换为驼峰式
}
}
}
4.v-if和v-show
5.v-for
- :key不要直接使用index,因为两个嵌套的for,index会重复,并且性能不高
- v-if不能跟v-for放一起,因为v-for先执行, 导致多次if的判断 ,所有要放在不同标签
6.event
-
event.proto.constructor 是原始的构造函数
-
taget
-
currenttagt
-
event是原生的
-
2事件被挂载到当前元素
7,表单
v-model
- .trim
- .lazy 输入完在执行
- .number 转换为数字
表单
v-model可以是数组,多选的话,值由value来控制
8.props $emit
属性传递
:属性=value
props:['属性']
触发事件
this.$emit('自定义事件名',val)
自定事件名 = '下面函数名' //这里时不要写参数的,参数在methods的函数写
总结:以左边为媒介
9.自定义事件
兄弟间传值(任何两个组件)
调用事件
event.$emit('自定义事件名',val) //注意和父子传值区别,event
event.$on('自定义事件名',触发的函数名) //绑定事件
beforeDestory(){
event.$off('自定义事件名',触发的函数名) //及时销毁,以免内存泄漏
}
10.生命周期
单文件
- created:vue实例创建完毕
- mounted:页面渲染完
- updataed:更改data数据时
- detoryed:触发 vm.destory()时,在beforeDestory要解除绑定、销毁子组件及事件监听器
多个组件
- ”父子子父“嵌套的顺序执行执行
- 销毁的时候先销毁子组件,再销毁父组件
高级特性
1,自定义v-model
2,$nextTick
-
Vue是异步渲染
-
data改变之后不会立刻渲染, 所以如果下一步直接获取dom的话, 获取到的是还没有更新的dom
-
this.$nextTick是页面渲染完后的一个回调,以获取最新的dom节点
-
$ref:用来获取dom元素
this.$nextTick(()=>{
this.$ref.node
})
3,slot
- 基础用法
默认值可以再slot里随便写的
- 作用域
是为了从子组件获取数据
//子组件
<slot :自定义名="data">
<slot>
//父组件
<tempelate v-slot="scope">
{{scope.自定义名}}
- 具名插槽
为了插入指定插槽,当插槽多时
//子组件
<slot name="data">
<slot>
//父
<tempelate v-slot="data">
4,动态组件
根据不同的组件名,渲染不同的组件
<component :is="组件名">
5,异步加载组件
什么时候用到再去加载,如果一开始就加载导致进入页面慢
<自定义组件 v-if='false'>
components:{
自定义组件:()=>import('../aas/ddd') //当false变成true再引用
}
6,keep-alive
-
缓存组件
-
频繁切换,不需要渲染(tab栏
-
vue性能优化
<keep-alive>
tab栏 //组件内的组件,切出去后不会被销毁 //与v-show相比,更适合复杂的切换
</keep-alive>
7,mixin
8,老师说的:可以不用深入,但要知道,最好能跟项目经验结合起来
9,vuex
state
getter
mutation
action
10.vue-router
-
懒加载
-
hash和history,后者必须服务端支持
配置路由时
{path:login/:id
components:()=>import('../aas/ddd')
}
原理
1组件化基础
很久以前的组件化
传统组件只是静态改变了数据,跟新依然要操作dom
数据驱动视图(mVVM,setState)
vue-mVVM
react-setState
2MVVM
view----viewmodel—model
template data
3响应式
data发生变化,立即触发视图跟新
原理上就是,单纯的改变属性是无法直接响应,
而是要触发个函数才会影响视图
核心API:Object.defineProperty
基本用法
const data = {name :"lisi"};
Object.defineProperty(data, "name", {
get: function () {
console.log("get"); //获取date.name时会触发这个函数
return name;
},
set: function (newval) {
console.log("set"); //给date.name赋值时触发set
name = newval;
},
});
console.log("data :>> ", data);
data.name = "wocao";
深度监听
需要递归到底
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
// 深度监听
observer(value)
// 核心 API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 深度监听
observer(newValue)
// 设置新值
// 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
value = newValue
// 触发更新视图
updateView()
}
}
})
}
几个缺点
需要递归到底,计算量大
无法监听新增属性和删除属性(Vue.set Vue.delete)
监听数组
Object.defineProperty无法监听数组,这时要重新定义数组原型
//创建对象 将对象原型指向数组原型 可以在对象上扩展新的方法而不影响原始原型
const arrproto = Object.create(Array.prototype);
['push',"shift"].array.forEach(element => {
arrproto[element]=function(){
console.log("跟新视图");
//执行原始push
Array.prototype[element].call(this,...arguments)
//this指向函数调用者,
}
});
//监测为数组是,把他的原型换成自定义原型
target.protype = arrproto
4,虚拟dom
- vdom是vue和react核心
- diff算法是vdom最核心的部分
vdom:用js模拟dom结构,js运行快,计算出最小的变更,操作dom .diff则是比较vdom
snabbdom来学习vdom
diff算法 diff:对比
2js可diff
2树可diff,如vdom
树dif的复杂度
两颗树做比较,每个节点都互相做比较,就会比较n的平方次, 还排序就是n3次方,算法不可以
解决
只比较同层
tag不同直接删掉重建,不深度比较
tag和key,都相同,则认为是相同节点,不深度比较
5模板编译
js的with语法
- {}里的自由变量,用obj.a的方式查找
- 匹配不到obj属性,会报错
vue template complier 将模板编译成render函数
执行render函数生成vnode,patch(diff算法)可将vnode渲染成html
vnode为dom的js版,也就是虚拟dom
总结
- 响应式: 监听data属性 getter setter (包括数组)
- 模板编译: 模板到 render函数 ,再到vnode
- vdom: patch(elem,vnode) patch(vnode,newnode)
6,vue组件的渲染和跟新过程
初次渲染
- 解析模IC板生产render函数(或是在开发环境已完成,vue-loader)
- 触发响应式, 监听data属性 getter setter
- 执行render函数, 生成vnode , patch(elem,vnode)
跟新过程
- 修改data,触发setter(此前在getter中已被监听)
- 重新执行render函数, 生成newnode
- patch(vnode,newVnode)
响应式(紫色):监听属性,更改属性会通知,看看是不是之前收集过
模板渲染(黄色):执行render会触发getter,收集依赖
虚拟dom(绿色)
异步渲染
- $nextTick: 多次修改data时,等dom渲染完时再回调
- 汇总data的修改,一次性进行渲染
- 减少操作dom次数,提高性能
7,路由
hash
网页组成部分
特点
- hash变化触发跳转,及前进后退
- 不会刷新页面
- 不会提交到服务端
hash变化
<script>
// hash 变化,包括:
// a. JS 修改 url
// b. 手动修改 url 的 hash
// c. 浏览器前进、后退
window.onhashchange = (event) => {
console.log('old url', event.oldURL)
console.log('new url', event.newURL)
console.log('hash:', location.hash)
}
// 页面初次加载,获取 hash
document.addEventListener('DOMContentLoaded', () => {
console.log('hash:', location.hash)
})
// JS 修改 url
document.getElementById('btn1').addEventListener('click', () => {
location.href = '#/user'
})
</script>
H5history
- 用url规范的路由,但跳转不刷新
- history.pushState(state,”,page1):跳转到page1
- window.onpopstate=(event)=>{} :监听浏览器前进后退 event.name能够拿到当前页的state
- 后端要配置: 简单点说, 后端无论拿到什么路由,都返回index.html
总结
hash—window.onhashchange
history—history.pushState 和 window.onpopstate
真题模拟
1 v-show 和 v-if
v-show是加了dispaley:none的显示隐藏
v-if是条件渲染销毁,而不是显示隐藏
使用频繁用v-show
2为何在v-if中使用key
增强性能,diff算法,判断tag和key, 如果都相同 ,就判断为 是相同的节点, 就不会再向下比较
不能使用index和random
3描述生命周期(单组件和父子组件)
beforecreated new Vue 初始化生命周期和事件完
created 初始化注入和校验完
beforemounted vm.$mount(el)指定el元素 指定template模板 将template编译到render渲染函数
mounted 创建vm.$el替换el ,页面渲染挂载完成
beforeupdataed data被修改后
updataed 跟新完成,data改变,虚拟dom重新渲染
beforedestory vm.$destory后
destory 销毁子组件及事件监听器 解除绑定
子组件先于父组件
4,vue组件如何通讯
- 1父子 v-on props
- 2,子父 this.emit 自定义事件
- 3,兄弟 event.$emit event .on绑定 event.off解绑
- 4,vuex
5,描述组件渲染更新的过程
组件render函数,渲染虚拟dom的过程中,会触发getter,getter将收集这个属性监听起来. 当data中属性发生改变,触发setter, 会通知,有没有收集了这个属性, 有的话,这重新渲染vdom
6,v-model的实现原理
通过input事件 this.data=$evnet.target.value
:value=data
改变data, 触发render,重新渲染
7,对mvvm的理解
数据驱动视图
view = >监听 => model
view <=指令<=model
8,computed有何特点
缓存,data不改变就不执行
9为何data必须是个函数
vue文件,本质是个构造函数. 当实例化两个,改变data值时, 如果data是对象, 那么另一个的data也会改变. 但如果是函数,返回的是个全新的数据对象
10ajax放在哪个生命周期
mounted
因为 生命周期是单线程 , ajax是个异步 , 如果放mounted之前 , 他也会排到队列里 , 等渲染完成
11 多个组件有相同逻辑,如何抽离
mixin
12何时使用异步组件
加载大组件
路由异步加载
需要用时再去加载
13何时使用keeplive
给组件缓存
想tab栏静态
14何时使用beforeDestory
解绑事件 event.$off
清除计时器
解绑自定义dom事件 如 window scroll
15什么是作用域插槽
可以拿到父组件的数据
v-slot=‘scope’
slot :scope=‘data’
16vuex中的action和mutation的区别
action处理异步,mutation处理同步
mutation更多是单个操作
action可以整合多个mutation
17vue常用的路由模式
hash
history(需后端配合)
18 如何配置vue-router异步加载
components:()=>(import ‘…/’)
19用vnode描述一个dom结构
<div class=aa>
<text style='color:red'>dd</text>
</div>
{
tag:div,
props:{
classname:aa
}
children:[
{
tag:text
style:cloor:red
children:aa
}
]
}
20监听data变化的核心api是什么
Obeject.difineProperty
深度监听:递归调用
监听数组:自定义数组原型 Array.property.
21响应式原理
监听data变化
组件渲染更新流程图
22diff算法的时间复杂度
o(n)
原本是n的3次方
通过同层级比较,以及tag和key进行优化
23,Vue为何是异步渲染,$nexttick有什么用
异步渲染(合并data修改),为了提高性能
nexttick是组件渲染完的回调函数,用来拿到最新的dom
24,vue常见性能优化
v-if 和 v-show
computed
v-for key , 避免 v-for 和v-if同时用
自定义事件\ dom事件 及时销毁
异步组件
keeplive
data层级不要太深, 底层监听会 递归 ,影响性能
webpack
前端通用,如 图片懒加载
ssr
简述diff算法的过程
如何将组件所有props穿给子组件
$porps
v-bind = “prop”
如何自己实现v-model