前端框架面试题

一、VUE的基本使用

1. v-html:会有XSS风险,会覆盖子组件

2. computed:有缓存,data不变则不会计算

3. watch:默认是浅监听,可以deep:true 如果是引用类型的值 newValue和oldValue是一样的 因为是引用类型 值是指针地址 他们指向一样

4. v-for和v-if不能一起使用 v-for的会优先v-if计算 也就是在v-for每次循环的时候 v-if都会执行一遍 官网不推荐使用

5. $event 传参获取到的就是原生的event对象 事件被挂在到当前元素

6. 组件通信 父子组件 props $emit 

兄弟组件 自定义事件 event.$on event.$emit (event是vue的实例)最好在beforeDestroy 时候 emit.$off,否则可能造成内存泄漏 所以 在 event.$on('onAddTitle',this.addTitle) 给一个函数名调用this.addTitle

beforeDestroy 可以做什么?卸载事件监听,卸载自定义事件

7. 生命周期 挂载 - 更新 -销毁   

beforeCreate/created(事件,周期初始化,还未开始渲染)  beforeMount/mounted (页面已经渲染完了) - beforeUpdate/updated - beforeDestroy/destroyed

父 index 子 list 生命周期执行顺序 index created -> list created -> list mounted -> index mounted  ;update和destroy也如此

8. vue的高级特性

①:自定义v-model (场景:颜色选择器)

②:$nextTick 

Vue是异步渲染的框架(data改变之后,dom不会立即渲染,$nextTick会在DOM渲染之后触发,以获取最新DOM,也就是data改变后,立即去获取dom数据是获取不到的,但是可以在nextTIck中获取到

③:slot

作用域插槽、具名插槽

父组件想要往子组件插一些东西

④:动态、异步组件

动态组件: :is="component-name" 需要根据数据,动态渲染的场景,即组件类型不确定

异步组件:import()函数 在某个特定条件下这个组件才会渲染 v-if判断 然后 ()=>import('../../') ; 按需加载,异步加载大组件;

⑤:keep-alive

缓存组件,频繁切换,不需要重复渲染,出现在Vue常见性能优化面试题

第一次触发mouted 后面就不会触发生命周期函数

分页

⑥:mixin

多个组件有相同的逻辑,抽离出来

mixin并不是完美的解决方案,会有一些问题

Vue3 提出的composition API旨在解决这些问题

缺点:变量来源不明确,不利于阅读;多mixin可能造成命名冲突;mixin和组件可能会出现多对多的关系,复杂度较高

 9.vuex

 10.vue-router (hash history)

二. Vue原理篇

 1. Vue响应式

①:监听对象变化

2.0核心API Object.defineProperty (监听对象)

缺点:深度监听,需要一次性递归,一次性计算量大;无法监听新增属性/删除属性 (Vue.set,Vue.delete);无法监听原生数组,需要特殊处理

proxy有兼容性问题:兼容性不好,无法polyfill

function defineReactive(target,key,value){
//注意如果是 info:{name:'123'},值是对象
  observe(target)
  Object.defineProperty(target,key, {
    get(){
        return value
    }
   set(newValue){
    if(newValue!=value){ //value一直在闭包中,再get也是可以获取到值的
//这里有种情况 就是赋值是对象 info.name = {age:11},所以需要再次监听
        observe(target)
        value = newValue
        updateView() //更新视图
    }
    }
})
}    
function observe(target){
    if(type target!=='object'&&target===null){
        return target //不是对象或数组
    }
//监听数组时候 判断是否是数组 下面有代码
//是对象的情况
for(let key in target){
    defineReactive(target,key,target[key]
    }
}        

②:监听数组变化

const oldArrayProto = Array.prototype
//创建新对象,原型指向oldArrayProto,再扩展的方法不会影响原型
const arrProto = Object.create(oldArrayProto)
['push','pop','shift','unshift','splice','sort','reverse'].forEach(value=>{
    arrProto[value] = function(){
     updateView()  //更新视图
     oldArrayProto[value].call(this,...arguments) //监听7个方法 更新视图 然后执行真正意义上的数组方法
    }
})
// 在observe深度监听时候 如果是数组则将value的原型 赋值为arrProto
function observe(value){
  if(Array.isArray){
    value.__proto__ = arrProto
    }
}

 2.vdom (用JS模拟DOM)

vdom核心概念:h、vnode、patch、diff、key等

存在的价值:数据驱动视图,控制DOM操作

 3.diff算法总结

patchVnode

addVnodes

removeVnodes 

updateChildren(key的重要性)

4.模板编译 (组件渲染和更新过程?)

with语法:改变{}内自由变量的查找规则,当做obj属性来查找;如果找不到匹配的obj属性,就会报错;with要慎用,它打破了作用域规则,易读性变差

 _c:createElement

_v:createTextVNode

_s:toString

_l:renderList

模板编译为render函数,执行render函数返回vnode

基于vnode再执行patch和diff

使用webpack vue-loader 会在开发环境下编译模板(重要) 开发环境就会编译好 就不用在浏览器运行时候去编译 性能优化

组件 渲染/更新 过程

初次渲染过程:

①:解析模板为render函数 (或在开发环境已完成,vue-loader)

②:触发响应式,监听data属性 getter和setter

③:执行render函数,生成vnode, patch(elem,vnode)

更新过程:

①:修改data,触发setter(此前在getter中已被监听)

②:重新执行render函数,生成newVnode

③:patch(vnode,newVnode)

异步渲染:

①:$nextTick (vue组件是异步渲染的)

②:汇总data的修改,一次性更新视图

③:减少DOM操作次数,提高性能

总结:

1:渲染和响应式的关系;渲染和模板编译的关系;渲染和vdom的关系

2:初次渲染;更新过程;异步渲染

5、前端路由原理

①:hash

稍微复杂一点的SPA都需要路由 路由模式:hash、H5 history

hash特点:hash变化会触发网页跳转,即浏览器的前进、后退

hash变化不会刷新页面,spa必需的特点

hash永远不会提交到server端(前端自生自灭)

 监听 onhashchange 事件

hash变化 包括:JS修改url ;手动修改url的hash;浏览器的前进、后退

②:h5 history

用url规范的路由,但跳转时不刷新页面

history.pushState(from,'',to)  window.onpopstate(监听浏览器的前进、后退)

服务端:无论访问什么样的路由都需要返回index.html的页面 由前端路由去判断渲染哪一块

6.vue面试真题演练

①:v-show和v-if的区别 

v-show:通过css display控制显示和隐藏

v-if:组件真正的渲染和销毁,而不是显示和隐藏

频繁切换显示状态用v-show,否则v-if

②:为何在v-for使用key

必须用key,且不能是index和random

diff算法中通过tag和key来判断,是否是sameNode

减少渲染次数,提升渲染性能

③:描述Vue组件的生命周期(父子组件)

④:vue组件如何通讯

父子组件props和$emit

自定义事件 event.$on/$emit/$off

vuex

⑤:描述组件渲染和更新过程

⑥:双向绑定v-model实现原理

input元素的value = this.name

绑定input事件this.name = $event.target.value

data更新触发re-render

⑦:对MVVM的理解

 ⑧:为何组件data必须是个函数

定义的每个vue文件都是一个类,在各个地方去使用时候 其实是实例化这个类,如果是对象的话,修改data之后其他地方都会改变,引用类型保存的是指针地址

⑨:ajax请求应该放在哪个生命周期

mounted

js是单线程,ajax异步获取数据

放在mounted之前没有用,只会让逻辑更乱,因为在那之前如果js没有执行完成,也不会去执行异步请求

⑩:如何将组件所有props传给子组件

<User v-bind="$props"> 在user组件正常接收,列出需要的组件

11:如何实现v-model

 12.多个组件有相同的逻辑,如何抽离

mixin 以及mixin的一些缺点

13.何时使用异步组件

加载大组件;路由异步加载

14.何时需要使用keep-alive

缓存组件,不需要重复渲染

15.何时需要使用beforeDestory

解绑自定义事件 event.$off

清楚定时器

解绑自定义的DOM事件,如window.scroll

16.作用域插槽

 17.Vuex中action和mutation有何区别

action中处理异步,mutation不可以

mutation做原子操作

action可以整合多个mutation

18.请用vnode描述一个DOM结构

{tag:'div',props:{},children:[]}

19.Vue如何监听数组变化

Object.defineProperty不能监听数组变化

重新定义原型,重写push,pop等方法,实现监听

19.diff算法的时间复杂度

O(n)

20.Vue为何是异步渲染,$nextTick有何用

异步渲染(以及合并data修改)以提高渲染性能

$nextTick在DOM更新完之后,触发回调

21.Vue常见性能优化

①:合理使用v-show和v-if

②:合理使用computed(有缓存)

③: v-for时加key,以避免和v-if同时使用

④:自定义事件,DOM事件及时销毁

⑤:合理使用异步组件

⑥:合理使用keeplive

⑦:data层级不要太深

⑧:使用vue-loader在开发环境做模板编译(预编译)

7.Vue3

Proxy实现响应式

基础使用

const proxyData = new Proxy(data,{
 get(target,key,receiver){
  //只处理本身(非原型的)属性
const ownKeys = Reflect.ownKeys(target)
if(ownKeys.includes(key)){
console.log('get',key)
}
  const result = Reflect.get(target,key,receiver)
  console.log('get',key)
  return result
 },
set(target,key,val,receiver){
//重复的数据,不处理
const oldVal = target[key]
if(val===oldVal){
return true
}
  const result = Reflect.set(target,key,val,receiver)
  console.log('set',key,val)
  return result
 },
deleteProperty(target,key){
  const result = Reflect.deleteProperty(target,key)
  console.log('deleteProperty',key)
  return result
 }
})

Reflect作用

和proxy能力一一对应

规划化,标准化,函数式

替代掉Object上的工具函数

proxy实现响应式好处

深度递归,性能更好 (在每次获取值的时候去递归)

可监听 新增/删除 属性 

const ownKeys = Reflect.ownKeys(target)
if(ownKeys.includes(key)){
   console.log('get',key) //已存在的
}else{
 //新增的属性
}  

可监听数组变化

弊端:无法兼容所有浏览器,无法polyfill

二、React

1. this默认是undefined

2.event 不是原生的event事件 SyntheticEvent(组合事件) constructor是个class (原生的是MouseEvent

可以通过event.nativeEvent获得原生事件

vue触发的节点和绑定的节点 都是当前的节点

react 触发是当前节点 但绑定在document上面 通过event.nativeEvent.currentTarget:绑定节点 event.nativeEvent.target:触发节点

参数传参,最后一个参数是event

总结:react所有的事件都被挂载在document上,和DOM事件不一样,和Vue事件也不一样

3.受控组件和非受控组件

受控组件:input value onChange时候去重新赋值  类似于vue的v-model 表单绑定的value受state控制 ;

非控组件:组件的value不受state控制,内部存储其自身状态的组件 可以通过ref查看

4.setState

①:不可变值

 不要修改原来state的状态

//数组
this.setState({
data:this.state.data.push(100) ,//会修改原来的值
data1:this.state.data1.concat(100) //不会修改原来的值,会返回新的值
})

//对象

不能直接对this.state.obj的属性进行设置,可以返回一个新值作为替换

this.setState({
obj:Object.assign({},this.state.obj,{a:100})
})

在shouldComponentUpdate时候 会对比前后值来确定是否改变视图,以此优化性能

②:可能是异步更新

渲染是异步的 ,修改值之后页面渲染时正常的,但是控制台打印是同步的,结果就会出错,

this.setState({},()=>{
//这里可以拿到最新的值,有点像vue的$nextTick
})

setTimeout 是同步的

自定义的DOM事件是同步的(原生事件中是同步)

直接使用时异步的

钩子函数和合并事件是异步的

③:可能被合并

传入对象可能会被合并类似 Object.assign 

初始是 this.state = {name:'alhh'}

后期setState的对象会和上面的合并 说明setState不会覆盖老的state而是合并操作

this.setState({})

传入函数 不会被合并 ,会更新完状态后再和老的合并

this.setState((prevState,props)=>{
return {
count:this.state.count+1
}
})

5.组件生命周期

 父子组件生命周期和vue一样

6.React高级特性

①:函数组件

如何区别函数组件和class组件

instanceof React.Component  为true的是class组件

class组件如果只接收一个props去渲染页面, 不涉及state和其他逻辑 就可以使用函数组件

函数组件:输入props,输出JSX,没有实例,没有生命周期,没有state,不能扩展其他方法

②:非受控组件

ref defaultValue defaultChecked 手动操作DOM元素

只是通过ref去获取值 没有onChange去赋值

使用场景:

必须手动操作DOM元素,setState实现不了

文件上传 <input type="file">

某些富文本编辑器,需要传入DOM元素

受控组件 vs 非受控组件

优先使用受控组件,复合React设计原则(数据驱动视图)

必须操作DOM,再使用非受控组件

③:Portals(传送门)

组件默认按照既定的层次嵌套渲染,如何让组件渲染到父组件外?

ReactDom.createPortal(jsx,location)

第一个参数是jsx,第二个参数是想要放的位置

使用场景:

父组件形成bfc,overflow:hidden

父组件z-index太小

fixed需要放在body第一层级

④:context

公共信息(语言,主题)如何传递给每个组件?用props太繁琐,用redux小题大做

最外层Provider:<Theme.Provider value={}>

需要用到的地方 函数组件:<Sider.Consumer> {value=><p>{value}</p>}  class组件:const ThemeContext = React.createContext('light') statis contextType = ThemeContext 然后this.context就可以获取到了

⑤:异步组件

import()

React.lazy:React.lazy(()=>import('../'))

React.Suspense:<React.Suspense fallback={<div>loading....</div>}

⑥:性能优化

shouldComponentUpdate(SCU):通过返回true或者false 来控制render的渲染  默认返回的是true ;必须配合“不可变值”一起使用,否则修改两者相同则不会触发SCU;不建议使用深度比较

React默认:父组件有更新,子组件则无条件更新(触发了render函数,render里面有子组件)

PureComponent 适合class组件和React.memo 适合函数组件(使用了浅比较:只比较第一层)底层是SCU

不可变值 immutable.js

⑦:高阶组件 HOC(接收一个组件参数,返回一个组件)

mixin已被React弃用

⑧:Render Props

render属性传入组件来控制渲染

 

 7.redux

单向数据流

中间件在dispatch中加逻辑 

 8.react原理

函数式编程:纯函数,不可变值

diff算法

只比较同一层级,不跨级比较

tag不相同,则直接删除重建,不再深度比较

tag和key都相同,就认为是相同节点,不再深度比较

①:合成事件

所有事件挂载到document上

event不是原生的,是SyntheticEvent合成事件,event.nativeEvent是原生事件对象

和Vue事件不同,和DOM事件也不同

 合成事件优点:

更好的兼容性和跨平台 (自己实现一套事件逻辑)

全部挂载到document,减少内存消耗,避免频繁解绑

方便事件的统一管理(如事务机制)

②.setState和batchUpdate

setState主流程

batchUpdate机制

 1.哪些命中batchUpdate机制

    生命周期(和它调用的函数)

    React中注册的事件(和它调用的函数)

    React可以“管理”的入口  (是在入口中设置)

 2.哪些不能命中batchUpdate机制

    setTimeout setInterval等(和它调用的函数)

   自定义DOM事件(和它调用的函数)

    React "管不到“的入口

transaction(事务)机制

 先执行一个开始的逻辑 -》函数体执行 -》执行一个结束的逻辑

③:更新的两个阶段

patch会被分为两个阶段

reconciliation阶段,执行diff算法,纯JS计算

commit阶段 将diff结果渲染DOM

④:注意:可能会有的性能问题

JS是单线程,且和DOM渲染共用一个线程

当组件足够复杂,组件更新时计算和渲染都压力大

同时再有DOM操作需求(动画,鼠标拖拽等)将卡顿

解决方案:fiber(内部运行机制)

将reconciliation阶段进行任务拆分 (commit无法拆分)

目的是:DOM需要渲染时暂停,空闲是恢复;什么时候知道DOM需要渲染 通过window.requestIdleCallback(某些浏览器不一定支持)

9.React面试题目

①:组件之间如何通信

父子组件props

自定义事件

Redux和Context

②:JSX本质是什么

createElement

执行返回vnode

③:context是什么,如何应用

父组件,向其下所有子组件传递信息

如一些简单的公共信息:主题色、语言等

复杂的公共信息 用redux

④:shouldComponentUpdate用途

性能优化

配合”不可变值“一起使用

⑤:redux单向数据流

原文地址:https://www.cnblogs.com/alhh/p/14001806.html