vue面试1
生命周期函数(父子)顺序
父beforeCreate =>. 父created ==>. 父beforeMount ==> 子beforeCreate ==> 子created ==> 子beforeMount ==> 子mounted ==> 父mounted
更新顺序
父beforeUpdate ==> 子beforeUpdate ==> 子updated ==> 父updated
卸载顺序
父beforeDestory / 子beforeDestory / 子destroyed / 父destroyed
vue2自定义 v-model
props: ['text'] model: { prop: 'text', event: 'change' } :value @input="$emit('change')"
v-if(存在DOM销毁和创建,可以有多个互斥的分支)和v-show(使用 display: none 来控制是否显示)
v-html 会有XSS 风险,会覆盖 children 里面的内容
watch 深度监听一个对象,拿不到 oldvalue,因为是引用
v-for和v-if 不要在一个标签上面使用(在vue3上 v-if的优先级高于 v-for)
事件对象 $event 是原生的mouseEvent对象 和 react 不同
$event.target 是注册到的 div / @click定义的位置
$event.currentTarget 事件被注册到当前的元素,react是注册到 root 标签上的
但是 vue 不是
vue是异步渲染的,data修改之后 DOM不会立即渲染,多次 data修改做整合,只会触发渲染一次
但是可以在 $nextTick 中获取最新的DOM
slot 插槽
作用域插槽(使用子组件中的 data)
具名插槽(有名称)
动态组件 <component. :is='name'. />
vue2只需要 名称, vue3必须是 一个具体的组件变量
异步组件
components: { FormDemo: () => import(.....) }
用到的时候才会去 加载对应的 js 文件,减小打包体积,提高性能
keepalive 用于频繁切换(常见性能优化)
缓存组件,不需要重复渲染
mixin的问题
变量来源不清晰,不利于阅读,重名(当有多个的时候), 功能不能有效拆分
组件和mixin 可能出现多对多的现象
mixinMounted 先执行 mounted 后执行
组件化:并不是新出现的。 后端原本就有 <%include. 文件。 %>
MVVM: 数据驱动视图,不需要修改DOM,只关注于业务逻辑
vue的响应式,自动触发视图更新
proxy 兼容性不好,且无法 poly fill
defineProperty 的缺点
无法监听到新属性 / 下标 的 值,同样也无法 删除,无法监听原声数组,需要特殊处理
需要使用Vue.set / delete
复杂对象
需要开始深度监听 (递归操作),而且在赋值时也要 深度监听(在 runtime 时执行),计算量大
vdom 核心减少计算次数的难度, 计算出最小的变更,然后执行DOM操作
{ tag: 'div', props: { id: 'div1' }, children: [ ] } 使用JS来模拟DOM的结构
snabbdom 学习 vdom
h / patch 函数 h 函数接受 vdom得出vnode, patch函数负责 挂载/更新/卸载
新旧vnode对比,得出最小更新范围,最后更新DOM
tag都可以有 key 属性
patchVnode / addVnode / removeVnode
updateChildren key的重要性
diff 算法 是vdom中最核心,最关键的部分
并不是 vdom 独创的,如 Linux diff
比较2个JS对象也可以做 diff 库 jiff
树的时间复杂度为 O(n^3) 1000个节点,需要计算1000^3次 怎么办
优化时间复杂度到 O(n) ,怎么实现
1.只比较同一层级,不跨级比较
2.tag不相同,则直接删除重建,不再深度比较
3.tag和key相同,二者都相同,则认为是相同节点
日常中大多数就是同级比较
编译模板
vue template complier 将模板编译为 render 函数
执行 render 函数生成 vnode
with 要慎用,它打破了作用域规则,易读性变差
vue-loader 会在开发环境下编译模板
vue组建中使用render函数代替template
组件渲染更新过程
- 初次渲染过程
1.解析模板为render函数(或者在开发环境下完成 vue-loader)
2.触发响应式,监听data属性 getter setter
3.执行render函数,生成vnode,patch(container, vnode)
当data里面的 数据没有在 template中用到,就不会出发 getter,也就不会被监听到
因为和视图没有关系,没有用到
- 更新过程
1.修改data,触发setter 此前getter中已经被监听
2.重新执行 render函数,生成newVnode
3.patch(vnode, newVnode)
异步渲染
减少DOM操作次数,提高性能
前端路由原理
hash变化会触发网页跳转,即浏览器的跳转
不会刷新页面,SPA必须的特点
hash永远不会提交到 server 端, 重写 window.onhashchange = (event) => { }
history 路由:URL规范的路由,跳转时不刷新页面
history.pushstate / onpopstate
window.pushstate
hash 管理系统,能用就尽量使用,比如不需要 SEO
history 可以考虑(to C 消费者)需要服务端支持
v-for中的key
diff算法中通过 tag 和 key来判断是否是 sameNode 减少DOM操作次数,提高性能
v-model的原理:手动控制。value和 input 事件,重新出发re-render
$props 传递给子组件
ajax 应该放在 mounted中: JS是单线程,放在mounted之前没有用,逻辑只会更加混乱
何时需要beforeDestory
解绑自定义事件 event$off
清除定时器
解除自定义的DOM事件,window.scroll
mutation 做原子操作
action 可以整合多个mutation操作
常见的性能优化
合理使用 v-show/ v-if / computed
v-for 增加 key,以及避免 v-if 一起使用
销毁事件,避免越用越卡
合理使用异步组件 / keep-alive
data 层级不要太深,因为需要深度监听
前端通用性能优化, 比如:图片懒加载
vue3有什么优势
性能更好,体积更小,更好的TS支持,更好的代码组织/逻辑复用
更多新功能,过度依赖 this,无法 treeshaking
vue3升级了那些功能
1.creatApp / emits
2.多事件处理 @click="one($event), two($event)"
3.fragment 以前必须是单一根节点
4.移除 .sync使用v-model代替 / 移除filter
5.异步组件 defineAsyncComponent
6.teleport / suspense
Reflect 反射到代理对象上来
规范化 / 标准化 / 函数化
代替了许多 Object 上的函数
proxy 如何实现监听
也是需要深度监听,并不是和defineProperty一样需要一次性监听
只有在get的时候才递归,而defineProperty需要开始就递归。提高了性能
可以原声监听数组变化
可监听新增和删除
.sync 实现:this.$emit('update: title', value)
watch和watchEffect
都可以监听,watch需要给出具体监听的值,watchEffect 则不需要
watchEffect 初始化会执行一次,要收集需要监听的数据
setup如何获取组件的实例: 没有 this
getCurrentInstance, data数据需要在 onMounted中才有,此时还没有完成初始化
vite为什么这么快
开发环境无需打包,启动很快,因为开发环境使用ES6 module
生产环境使用 rollup 并不会快很多
可以远程引入CDN包
可以动态引入 const add = await import('.....').default 点击按钮后才加载
组合式API 和 react hooks的区别
setup(相当于一个生命周期)只会被调用一次,后者会被多次调用
前者没有 useMemo/ useCallback ,因为只会被调用一次,数据已经是必包中
前者无需考虑调用顺序,后者需要保证hooks的顺序一致