记录下遇到的面试题目

138 阅读14分钟

1.介绍下自己

我是谁+从哪里来+我做过什么+有什么成绩+为什么能胜任

示例:面试官你好,我叫xx,19年毕业,毕业之后就开始从事前端相关工作。之前任职于XX公司,担任软件开发工程师一职,在职期间主要使用的技术栈为(React + ts + mobx + hooks),之前任职于XX公司,担任软件开发工程师一职,在职期间主要使用的技术栈为(Vue + uni-app)进行多端开发,包括小程序,app,h5,对线上问题处理、性能调优、线程并发能问题都有自己的理解,对行业相关业务的研发设计流程也十分熟悉。

2.jsx或者tsx是如何编译成可以使用的代码的

tsx会通过babel转换为jsx,React中的JSX是受到微软的JSX启发而创建的一种语法,微软的JSX与JavaScript的JSX没有直接关联。JSX写出来的代码并不能直接在目前任何一款浏览器上运行,因此在运行前需要预先使用工具把它翻译成浏览器所能识别的ES5代码,目前主流的是使用 WebPack + Babel 进行编译、打包。Webpack处理 js | jsx | mjs类型文件的时候用babel-loader进行翻译,最终生成浏览器可直接运行的ES5代码。

简单来说 jsx 通过babel-loader转换为 js代码,以React.createElement函数形式存在,createElement函数返回一个ReactElement函数,ReactElement函数返回一个的虚拟节点,虚拟节点中嵌套虚拟节点(节点树),就形成了虚拟DOM,最后通过ReactDOM.render方法转化为真实DOM

3.用hooks实现类似store的效果,这种和store相比,优点在哪里,缺点在哪里,什么情况下使用hooks实现的store,什么时候使用mobx的store

4.vue3和vue2区别

一. 根节点不同

vue2中必须要有根标签。

vue3中可以没有根标签,会默认将多个根标签包裹在一个fragement虚拟标签中,有利于减少内存。

二. 组合式API和选项式API

在vue2中采用选项式API,将数据和函数集中起来处理,将功能点切割了当逻辑复杂的时候不利于代码阅读。

在vue3中采用组合式API,将同一个功能的代码集中起来处理,使得代码更加有序,有利于代码的书写和维护。

三. 生命周期的变化

  • 创建前:beforeCreate -> 使用setup()
  • 创建后:created -> 使用setup()
  • 挂载前:beforeMount -> onBeforeMount
  • 挂载后:mounted -> onMounted
  • 更新前:beforeUpdate -> onBeforeUpdate
  • 更新后:updated -> onUpdated
  • 销毁前:beforeDestroy -> onBeforeUnmount
  • 销毁后:destroyed -> onUnmounted
  • 异常捕获:errorCaptured -> onErrorCaptured
  • 被激活:onActivated 被包含在<keep-alive>中的组件,会多出两个生命周期钩子函数。被激活时执行。
  • 切换:onDeactivated 比如从 A 组件,切换到 B 组件,A 组件消失时执行

四.v-if和v-for的优先级

在vue2中v-for的优先级高于v-if,可以放在一起使用,但是不建议这么做,会带来性能上的浪费

在vue3中v-if的优先级高于v-for,一起使用会报错。可以通过在外部添加一个标签,将v-for移到外层

五.diff算法不同

vue2中的diff算法

遍历每一个虚拟节点,进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方。 用patch记录的消息去更新dom

缺点:比较每一个节点,而对于一些不参与更新的元素,进行比较是有点消耗性能的。 特点:特别要提一下Vue的patch是即时的,并不是打包所有修改最后一起操作DOM,也就是在vue中边记录变更新。(React则是将更新放入队列后集中处理)。

vue3中的diff算法

在初始化的时候会给每一个虚拟节点添加一个patchFlags,是一种优化的标识。 只会比较patchFlags发生变化的节点,进行识图更新。而对于patchFlags没有变化的元素作静态标记,在渲染的时候直接复用。

六. 响应式原理不同

vue2通过Object.definedProperty()的get()和set()来做数据劫持、结合和发布订阅者模式来实现,Object.definedProperty()会遍历每一个属性。

vue3通过proxy代理的方式实现。

proxy的优势:不需要像Object.definedProperty()的那样遍历每一个属性,有一定的性能提升proxy可以理解为在目标对象之前架设一层“拦截”,外界对该对象的访问都必须通过这一层拦截。这个拦截可以对外界的访问进行过滤和改写。

当属性过多的时候利用Object.definedProperty()要通过遍历的方式监听每一个属性。利用proxy则不需要遍历,会自动监听所有属性,有利于性能的提升

总结

  • Vue3 相比 Vue2 来说,Vue3 重写了虚拟 Dom 实现,编译模板的优化,更高效的组件初始化。
  • 原先通过definedProperty遍历所有属性,改为proxy自动监听每一个属性
  • 生命周期修改
  • 由组合式api改为了选项式api,更易于书写和维护
  • 根节点调整,原先必须要有根节点,现在可以不需要了
  • 强制v-for和v-if不能同时使用

5.react16和react16之前的区别

6.vue底层和react底层

7.vue和react的优缺点

Vue

特点

  • 轻量级架构
  • 双向数据绑定
  • 指令
  • 插件化

优点

  • 简单好学
  • 快速
  • 模块友好
  • 强大

缺点

  • 缺少高阶教程
  • 不支持IE8
  • 生态环境不如react

React

特点

  • 声明式设计,可以轻松描述应用
  • 高效:通过模拟dom,最大程度减少性能开销
  • 灵活:可以很好的和已知库配合

优点

  • 速度快
  • 跨浏览器兼容
  • 模块化
  • 单项数据流
  • 预渲染,有助于爬虫seo
  • 兼容性好:可以通过不同的打包工具打包

缺点

  • react不是一个完整的框架,需要搭配reactRouter和flux才能写大型应用

8.图片加载缓慢优化,打包优化,前端安全优化

9.react 首次渲染一个组件到页面中需要做哪些

  1. 拿到 React.createElement 返回的 react 节点【就是一个对象】

    最终会拿到一个树形结构的对象,如果是组件也会生成对应的 react 节点,就是 type 的值是 Component

  2. 通过 render 方法进行渲染 render方法进行渲染要做的事情有很多:

    • 如果是组件节点,则会在执行渲染的过程中保存对应的 Hooks 以及触发对应的 hooks【比如说像 useState 是要立即触发的,useEffect 是要留存下来等到后续 dom 挂载完毕以后触发的】
    • 如果是 react 元素节点,不会生成对应的真实 dom,而是生成一个描述对象【描述了当前要创建的真实 dom 的一些信息,以及这个描述对象要做的操作】这个描述对象叫 fiber
  3. 通过整个清单会依次将清单内部的东西编译成真实 dom,然后插入父元素的子节点 appendChild

  4. 等整个渲染流程结束以后,得到一个完整的真实 dom 树,然后插入到页面中

  5. 触发对应的生命周期事件

10.说说fiber

在react15及之前是没有fiber的,react执行一次更新操作都是同步的,他是通过递归去进行更新,一旦开始就不会中断直到结束。这也就造成了页面性能的低下,体验非常差。

而fiber的出现,他将更新渲染耗时长的大任务变成很多小切片,小切片执行完后就去执行高优先级的任务,比如:用户点击输入等等,将不可中断的渲染变成可中断的渲染,提高了页面的流畅度和性能。

有了fiber,React 渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。它采用的是一种主动让出机制。(可以由游览器给我们分配执行时间片,通过requestIdleCallback实现)

当前页面所对应的 fiber 树称为 current Fiber,同时 react 会根据新的状态构建一颗新的 fiber 树,称为 workInProgress Fiber

11. 浏览器渲染的流程

  1. 解析 HTML 文件,构建 DOM 树,同事主进程去下载 CSS 文件
  2. CSS 文件下载完成后,会解析 CSS 文件成树形的数据结构,结合 DOM 树合并成 RenderObject 树
  3. 这时候会对 RenderObject 树种的元素尺寸、位置等信息进行计算布局
  4. 开始绘制 RenderObject 树的各个属性,如:背景色,透明度等等
  5. 浏览器主进程将默认的图层和复合图层交给 GPU 进程,GPU 进程再将各个图层合成(composite),最后显示出页面

12. 性能优化

1. 异步加载js文件

我们知道在浏览器碰到 script 标签时,如果没有deferasync,浏览器就会立即加载并执行对应的 js 文件,就会造成阻塞。 因此我们可以使用deferasync是去异步加载外部的 JS 脚本文件,他们都不会阻塞页面的解析。(异步加载js文件,减少阻塞)

2. 回流和重绘

回流和重绘对游览器的性能消耗都是比较大的,回流必将引起重绘,重绘不一定会引起回流。因为,我们需要尽量避免造成页面的回流。 - 使用RequestAnimationFrame 函数实现动画,而不是使用setTimeout()setInterval() - 创建一个新的渲染层(减少回流)

    -   有明确的定位属性(relative\fixed\sticky\absolute)
    -   透明度(opacity 小于 1)
    -   有 CSS transfrom 属性(不为 none)
    -   当前有对于 opacity\transform\fliter\backdrop-filter 属性的动画

-   创建合成层。合成层会开始 GPU 加速页面渲染,但不能滥用

    -   对 opacity\transform\fliter\backdrop-filter 应用了 animationtransition(需要时 active 的 animation 或者 transition)
    -   will-change 设置为 opacity\transform\top\left\bottom\right;(提前通知浏览器元素将要做什么动画,让浏览器提前准备合适的优化设置)
    -   有 3D transform 函数:比如 translate3d\scale3d\rotate3d 等

3. 图片优化 - 图片压缩 - 其实就是减少图片的体积大小,提高网络请求速度 - 精灵图 - 就是像一些 icon 小图片,我们可以将这些图片集成在一张图片里,通过定位等相关技术实现展示需要的图标。 - SVG 替换图片 - 可缩放矢量图形,可以按比例缩小,并支持压缩。放大缩小不会失真。 - 图片懒加载 - 如果页面中存在大量的图片,一次性全部加载就会变的很慢,因此我们可以让页面先加载一个占位图,然后游览页面的时候,随着可视区域的变化,将原先的占位图替换成真实的图片。 - WebP - 将图片转换为 webp 格式,减少体积提高速度。

4. 打包优化

打包的流程(webpack 的运行是一个串行的过程)

  • 初始化参数:从配置文件或 shell 语句中读取合并参数,得到最终的参数

  • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行 run 方法开始编译。

  • 确定入口:根据配置文件中的 entry 参数找到所有的入口文件。

  • 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。

  • 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。

  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。

  • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

  1. 多进程打包

当我们的项目体量逐渐变大的时候,打包就会变得非常漫长,因为他是单线程模式的,只能逐个文件处理。因此开启多进程去打包是非常有必要的。常见的 loader 有thread-loader

使用:只要把 thread-loader 放置在其他 loader 之前, 那 thread-loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行。

  1. webpack缓存

webpack 缓存的方法有很多,比如cache-loaderHardSourceWebpackPluginbabel-loadercacheDirectory标志。这些都可以在重新运行期间节约大量的时间,但是在初次运行的时候会比较慢。

  1. HotModuleReplacement

热模块替换:在程序运行中,替换、添加、删除模块,而无需重新加载整个页面,从而提高了开发时的构建速度。

  1. 优化-减少代码体积 Tree Shaking

开发的时候我们定义了一些工具函数库,或者引入第三方工具函数库或组件。如果没有处理的话,打包时会引入整个库,但是实际上我们可能只是用上极小部分的功能。

这样将整个库都打包进去,体积就太大了。

Tree Shaking是一种术语,通常用于描述一处 Javascript 中没有用上的代码。webpack 已经默认开启该功能,无需其他配置。

13. 安全优化

  1. 跨站脚本攻击(XSS)
    1. 反射型XSS攻击
    2. 基于DOM的XSS攻击
    3. 存储型XSS攻击

xss防御方法

  1. 字符转义
str.relace(/&/g, '&amp;')
  1. csp 设置白名单
<meta http-equiv="Content-Security-Polity:default-src 'self'">
  1. 告诉浏览器只有本域名的资源可以请求

CSRF(跨站请求伪造) 不让第三方请求

  1. Token

实践劫持

  1. X-FRAME-OPTIONS

后台通过设置X-FRAME-OPTIONS:DENY 禁止iframe标签

14. 构造函数是怎么实现的

es5的继承和es6的继承的区别

es5是用的原型链继承和构造函数继承

es6是用class实现继承.通过保留关键字 extends 实现继承, 实际上只是语法糖,本质上还是通过原型链继承

new 做了哪些事

构筑一个空对象, 并且将this变量引用该对象

继承了函数的原型

属性和方法被加入到this引用的对象中,并执行了该函数

新创建的对象由this所引用,并且隐式返回this

function Student(stuName,age){

this.stuName = stuName; this.age = age; //每个学生都有这个方法

this.study = function () { console.log(this.stuName + "正在学习"); };

} //这样我们就可以创建多个对象

let stu1 = new Student("zhangsan",18)

15. vue是怎么对数组进行改写的

16. scroll-view内使用弹窗会出现什么问题,要怎么解决

在scroll-view外侧放置一个通用的modal组件,内部调用方法显示外侧的modal组件。

17. css树怎么生成渲染

生成css树和生成dom树相似,将字符串转换为标记,然后转换为节点,然后构建cssom数(样式树)

![image.png](p3-juejin.byteimg.com/tos-cn-i-k3…

18. typeof 和 instanceof 的区别

typeof 可以用来判断原始数据的基本类型,除了null,其他都可以正确的判断出来

在判断对象的时候除了函数可以正确判断,其他类型都无法正确判断

字符串 'string'

数字 'number'

布尔 'boolean'

undefined 'undefined'

NaN 'number'

null 数组 对象 'object'

函数 'function'

instanceof 通过原型链的方式判断数据类型

instanceof可以正确的判断对象数据类型,但是缺点是无法准确的判断出原始数据类型

yy instanceof xx

18. vue生命周期是否有onshow 和onhide

18. 无框架如何实现onshow和onhide

18. 实现期限内获取数据,超时返回null的方法

class Token {
    setToken(key, value, time) {
        TODO
    }
    getToken(key) {
        TODO
    }
    
}

const token = new Token()

token.setToken('k1', 'v1', 1000) //

token.getToken('k1') // 'v1'

setTimeout(() => {
    token.getToken('k1') // null
}, 2000)

18. 实现左侧宽度固定,右侧宽度自适应,左右两侧的高度取决于高的那一侧的高度

18. flex css的三个参数的作用

18. 说下flex相关的css

18. js是单线程的,那settimeout是如何实现的

18. React.useMemo和React.useCallback的作用

18. 移动端下1px过大的适配方法

18.