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 首次渲染一个组件到页面中需要做哪些
-
拿到 React.createElement 返回的 react 节点【就是一个对象】
最终会拿到一个树形结构的对象,如果是组件也会生成对应的 react 节点,就是
type的值是 Component -
通过 render 方法进行渲染 render方法进行渲染要做的事情有很多:
- 如果是组件节点,则会在执行渲染的过程中保存对应的 Hooks 以及触发对应的 hooks【比如说像 useState 是要立即触发的,useEffect 是要留存下来等到后续 dom 挂载完毕以后触发的】
- 如果是 react 元素节点,不会生成对应的真实 dom,而是生成一个描述对象【描述了当前要创建的真实 dom 的一些信息,以及这个描述对象要做的操作】这个描述对象叫 fiber
-
通过整个清单会依次将清单内部的东西编译成真实 dom,然后插入父元素的子节点 appendChild
-
等整个渲染流程结束以后,得到一个完整的真实 dom 树,然后插入到页面中
-
触发对应的生命周期事件
10.说说fiber
在react15及之前是没有fiber的,react执行一次更新操作都是同步的,他是通过递归去进行更新,一旦开始就不会中断直到结束。这也就造成了页面性能的低下,体验非常差。
而fiber的出现,他将更新渲染耗时长的大任务变成很多小切片,小切片执行完后就去执行高优先级的任务,比如:用户点击输入等等,将不可中断的渲染变成可中断的渲染,提高了页面的流畅度和性能。
有了fiber,React 渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。它采用的是一种主动让出机制。(可以由游览器给我们分配执行时间片,通过requestIdleCallback实现)
当前页面所对应的 fiber 树称为 current Fiber,同时 react 会根据新的状态构建一颗新的 fiber 树,称为 workInProgress Fiber
11. 浏览器渲染的流程
- 解析 HTML 文件,构建 DOM 树,同事主进程去下载 CSS 文件
- CSS 文件下载完成后,会解析 CSS 文件成树形的数据结构,结合 DOM 树合并成 RenderObject 树
- 这时候会对 RenderObject 树种的元素尺寸、位置等信息进行计算布局
- 开始绘制 RenderObject 树的各个属性,如:背景色,透明度等等
- 浏览器主进程将默认的图层和复合图层交给 GPU 进程,GPU 进程再将各个图层合成(composite),最后显示出页面
12. 性能优化
1. 异步加载js文件
我们知道在浏览器碰到 script 标签时,如果没有defer和async,浏览器就会立即加载并执行对应的 js 文件,就会造成阻塞。 因此我们可以使用defer和async是去异步加载外部的 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 应用了 animation 或 transition(需要时 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转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。 -
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
- 多进程打包
当我们的项目体量逐渐变大的时候,打包就会变得非常漫长,因为他是单线程模式的,只能逐个文件处理。因此开启多进程去打包是非常有必要的。常见的 loader 有thread-loader
使用:只要把 thread-loader 放置在其他 loader 之前, 那 thread-loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行。
- webpack缓存
webpack 缓存的方法有很多,比如cache-loader、HardSourceWebpackPlugin、babel-loader的cacheDirectory标志。这些都可以在重新运行期间节约大量的时间,但是在初次运行的时候会比较慢。
- HotModuleReplacement
热模块替换:在程序运行中,替换、添加、删除模块,而无需重新加载整个页面,从而提高了开发时的构建速度。
- 优化-减少代码体积 Tree Shaking
开发的时候我们定义了一些工具函数库,或者引入第三方工具函数库或组件。如果没有处理的话,打包时会引入整个库,但是实际上我们可能只是用上极小部分的功能。
这样将整个库都打包进去,体积就太大了。
Tree Shaking是一种术语,通常用于描述一处 Javascript 中没有用上的代码。webpack 已经默认开启该功能,无需其他配置。
13. 安全优化
- 跨站脚本攻击(XSS)
- 反射型XSS攻击
- 基于DOM的XSS攻击
- 存储型XSS攻击
xss防御方法
- 字符转义
str.relace(/&/g, '&')
- csp 设置白名单
<meta http-equiv="Content-Security-Polity:default-src 'self'">
- 告诉浏览器只有本域名的资源可以请求
CSRF(跨站请求伪造) 不让第三方请求
- Token
实践劫持
- 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数(样式树)
 {
TODO
}
getToken(key) {
TODO
}
}
const token = new Token()
token.setToken('k1', 'v1', 1000) //
token.getToken('k1') // 'v1'
setTimeout(() => {
token.getToken('k1') // null
}, 2000)