【面试题】前端面试真题 年前端面试(上):https://developer.aliyun.com/article/1414059
h公司
- 一面
- 盒模型;BFC;
- 高阶函数作用:柯里化(curry);组合函数(compose);管道函数(pipe)
- 页面自适应方案,flexible 本质
- WebSocket 了解嘛?
- 跨域问题解决:JSONP;iframe;postMessage;CORS;Proxy;WebSocket
- vue 双向数据绑定:
数据劫持结合发布者-订阅者模式 - vue 组件通信方式
- vue 生命周期
- vue3.0 特性
- 扩展运算符的应用(ES6)
- 浏览器解析流程
- URL 解析
- DNS 查询
- TCP 连接(三次握手) 第一次握手:建立连接时,客户端发送 syn(建立联机)包到服务器,并进入 syn_send 状态,等待服务器确认; 第二次握手:服务器收到 syn 包,确认包信息,同时自己也发送一个 syn + ack(确认)包并进入 syn_recv 状态; 第三次握手:客户端收到 syn+ack 包,向服务器发送 ack 包,发送完后,客户端和服务器都进入 established 状态,完成三次握手;
- HTTP 请求
- 服务器响应请求
- 页面渲染 解析 HTML,构建 DOM 树(script 会下载 并解析 js,异步下载完执行是仍会阻塞 DOM 解析) 解析 CSS(link 或 style 标签),生成 CSS 规则树(HTML 和 CSS 解析相互独立) 合并 DOM 树和 CSS 规则,生成 render 树 布局 render 树( Layout / reflow 回流),负责各元素尺寸、位置的计算 绘制 render 树( paint 重绘),绘制页面像素信息 浏览器会将各层的信息发送给 GPU,GPU 会将各层合成( composite ),显示在屏幕上
- 二面
- 定制化组件是哪些?
- 组件封装,提升效率,考虑组件化,发布远程通用
- 性能优化之 Performance 面板
- ES5 和 ES6 继承的区别
- H5 混合开发 JSBridge 通信
- h5 手机端兼容性
i公司
- 一面
- H5 混合开发:客户端交互,APP 交互,微信交互;jsbridge 通信,如何封装的,看过吗
- 大数据处理:分页;虚拟列表;后端远程搜索
- 前端性能优化
- 首页白屏解决:缓存;骨架屏;重绘,重排;减少包大小;解决阻塞;
- webpack 打包优化方式
- SPA 单页应用;SSR 服务端原理(NuxtJS);webpack 热更新
- NextTick 的理解
- keep-alive 动态组件
- vuex 模块化实现
- 大数加法[算法]
实现两个字符串型数字的加法运算,返回字符串型的数字的和
注意:无法直接对传入参数进行转换成数字
思路:一位数一位数相加,采用隐式转化的方式相加
sum("1233", "233"); // '1466' function sum(num_str1, num_str2) { // 字符串转字符数组 const num_arr1 = num_str1.split(""); // 字符串转字符数组 const num_arr2 = num_str2.split(""); // 竖式加法计算次数 let sum_length = Math.max(num_arr1.length, num_arr2.length); // 求和结果 const sum = []; // 进位标志位 let CF = 0; // 循环语句为什么加CF进位存在判断呢,因为当最高位产生进位,(如 999 + 9999) // 计算到千分位9的时候,并且没有更多数要参与运算时,仍然需要进位参与一次运算,否则丢失进位。 while (sum_length-- > 0 || CF) { // 求和,注意数组长度不够,按0算 let temp = Number.parseInt(num_arr1.pop() || 0) + Number.parseInt(num_arr2.pop() || 0) + CF; if (temp >= 10) { temp %= 10; // 取个位数 CF = 1; // 产生进位(对于加法,最多产生1个进位) } else { CF = 0; // 未产生进位 } sum.unshift(temp); // 记录求和数据(从头插入) } return sum.join(""); } 复制代码
- 二面(针对项目提问)
- 组件的定制化改造,二次封装还是自己写的
- select 分页实现:滚动,分片,远程搜索,虚拟列表,数据处理部分展示
- 单点登陆的实现;
- 权限管理的实现;自定义指令控制操作权限,按钮级别;数据权限实现
- 大量数据性能优化;表格遇到的问题,怎么解决的;
- 前端性能优化
- 哪个优化提升最明显;
- 首屏渲染为啥那么长,包为啥那么大,如何优化;
- 如何实现 ssr
- 项目中遇到的问题,难题
- pwa 使用场景;
- 和安卓通信交互,和原生的相机,相册如何交互的;如何获取相册图片;照片 js 如何拿到的;input 和原生的相册如何交互,串起来;js 调用原生的相机
- 微信账号和手机号账号绑定实现:微信提供 uuid,绑定原理和过程
- webpack 构建过程,plugin 的实现
- 对未来的团队和技术有啥要求,职业规划类的吧
- 开发方向,自我定位
如何实现一个拖拽指令?
vue.js 自定义指令(拖拽、拖动、移动) 指令 v-drag
j公司
- 挑战性项目;项目难点
- 虚拟 dom 作用:多次数据变化,一次更新;怎么收集数据变化;
- 对象的深拷贝实现,递归调用死循环问题如何解决
- 浏览器缓存对比:cookie;localStorage;sessionStorage
- 权限管理实现:咋实现的?具体怎么处理,代码实现
- 路由权限;菜单权限(路由守卫导航);按钮权限(v-if,自定义指令);接口权限
- 单点登录 SSO 实现
- vue2.0 和 vue3.0 区别
- 2.0
- 双向数据绑定:Object.defineProperty
- 3.0
- 双向数据绑定:Proxy,Reflect
- Composition API:更好的逻辑复用和代码组织
- Fragment:可以用多个根元素
- Teleport(任意门)
- 使用 Typescript 开发
- 重构 Virtual DOM,修改了 diff 算法
- v-model 在 2.0 和 3.0 里面的区别,实现原理
- 破坏性的: 当用在 自定义组件 上的时候,v-model 的 prop 与 event 的默认名已变更:
- prop: value -> modelValue;
- event: input -> update:modelValue;
- 破坏性的: v-bind 的 .sync 修饰符以及 model 选项已删除,取而代之的是 v-model 的一个参数;
- 新的: 现在可以在一个组件上使用多个 v-model 来绑定数据了;
- 新的: 新增了自定义 v-model 修饰符的功能。
- flex 弹性布局
- flex:1,等价于 0 1 auto,各个参数含义
- 实现纵向三栏布局,中间 flex:1,如果中间宽度大于外层宽度,怎样表现?
- z-index 无效问题:生效范围;什么情况下会失效,边界条件
- 父级元素溢出隐藏或者不显示:父元素设置了 overflow:hidden /display:none/ 等,那么子元素如果在父元素外部绝对定位,那么调节子元素 z-index 可能不会显示
- 父级元素层级低,z-index 被覆盖
- 没有设置定位(position)属性
- 父级元素 position 属性为 relative
- 含有浮动(float)属性
- IE 不兼容
- 移动端 1px 边框
- 针对 IOS:border:0.5px solid #E5E5E5
- 使用边框图片
- 使用 box-shadow 实现:试了下,看不出效果
- 使用伪元素:设置绝对定位,和父元素左上角对齐。将伪元素的长和宽先放大 2 倍,然后再设置一个边框,以左上角为中心,缩放到原来的 0.5 倍
- 设置 viewport 的 scale 值
- 协商缓存和强缓存使用场景
- 强缓存:访问服务器获取到数据后缓存下来,过期时间之内不会再重复请求,而是直接从本地缓存数据库读取
- http1.0:Expire 相应头。expires 表示未来资源会过期的时间
- http1.1:Cache-Control。max-age=xxx(缓存的资源将在 xxx 秒后过期);no-cache(需要使用协商缓存来验证是否过期);no-store(不可缓存)
- 协商缓存:每次读取数据都要和服务端通信,增加缓存标志。如果不匹配,表示资源更新,返回新数据;匹配则表示没有更新,返回 304
- http1.0:Last-Modified;If-Modified-Since;弊端是时间的精确度只能在秒,资源没变但时间变了,也会更新
- http1.1:Etag; If-None-Match
- 常见情况
- 输入 url 回车进入:根据实际缓存策略处理,没有设置 no-cache 或 no-store,默认先走强制缓存路线
- 按钮刷新,F5 刷新,右键重新加载:将 cache-control 的 max-age 直接设置成了 0,让缓存立即过期,直接走协商缓存路线
- ctrl+F5 强刷:浏览器会强行设置 no-cache,强制获取最新的资源
- vue-router 实现原理
- 怎么知道页面变化了,更新页面;3.0 通过响应式,监听变量,响应机制;局部刷新
- hash 模式:hashchange
- history 模式:pushState 和 replaceState
- 快排实现:核心递归[算法]
//快排实现 var a = [3, 2, 8, 4, 5, 6, 7, 1]; //递归调用 QuickSort(a, 0, a.length - 1); console.log(a.toString()); function QuickSort(a, l, r) { if (l >= r) { return; } var target = a[l]; var left = l, right = r; while (left < right) { while (left < right && target <= a[right]) { //B往左走寻找比挖出来的第一个萝卜轻,且位置在A右边的萝卜 right--; } a[left] = a[right]; while (left < right && a[left] <= target) { //A要往右寻找比一个萝卜重,且位置在B左边的萝卜 left++; } a[right] = a[left]; } a[left] = target; QuickSort(a, l, left); QuickSort(a, left + 1, r); } 复制代码
k公司
- 自我介绍
- 工作中遇到的难点
- 服务端渲染和客户端渲染的区别,NuxtJS 的实现
- 从输入一个 URL 地址到浏览器完成渲染的整个过程
- 计算机网络浏览器缓存:强缓存和协商缓存
- 前端性能优化
- 跨域方式
- css 选择器及优先级
- flex 布局属性及意义;水平垂直居中实现
- JS 事件循环机制
- js 作用域和作用域链
- 闭包及其作用
- let,const 和 var 区别
- 对象的深拷贝:深拷贝与浅拷贝的区别?
- 浅拷贝:创建新的数据,这个数据有着原始数据属性值的一份精确拷贝
- 基本类型,拷贝的就是基本类型的值
- 引用类型,拷贝的就是内存地址(共享)
- 方式:Object.assign;slice;concat;拓展运算符
- 深拷贝:开辟一个新的栈,对应两个不同的地址
- 方式:_.cloneDeep();jQuery.extend();JSON.stringify();循环递归实现
- 递归碰到循环调用问题,死循环,如何解决?
- 尾递归优化
- 循环重写
- ES6 常用方法和属性
- 类 class,extends
- 模块化 import/export
- 箭头函数
- 模板字符串
- 函数参数默认值
- 解构赋值
- 扩展运算符(...)
- let, var, const
- Promise, .then, .catch, .finally, .all(), .race,
- async/await
- includes;Object.values();Object.entries()
- props 传值无法更新问题
通过 props 传值不随着变化而变化,原因及解决方法(实际问题)
- watch 监听:immediate;deep
- this.$set()
- 使用 v-if
- Object.assign()
- Promise 常用方法
- Promise
- 异步编程的一种解决方案,解决地狱回调
- 三种状态:pending(进行中);fulfilled(已成功);rejected(已失败)
- then,catch,finally
- all,race,allSettled,resolve,reject,try
- async/await:Generator(yield,next) 的语法糖
- 手写 Promise.all;Promise.all 和 Promise.race 的区别?
// Promise.all export const all = (promiseArr) => { let result = []; let count = 0; return new myPromise((resolve, reject) => { for (let i = 0; i < promiseArr.length; i++) { Promise.resolve(promiseArr[i]).then( (res) => { result[i] = res; count++; // 全部promise执行成功之后才resolve if (count === promiseArr.length) { resolve(result); } }, (err) => { reject(err); } ); } }); }; // Promise.race export const race = (promiseArr) => { return new myPromise((resolve, reject) => { for (let i = 0; i < promiseArr.length; i++) { Promise.resolve(promiseArr[i]).then( (res) => { // 只要有任何一个promise状态变更就resolve resolve(res); }, (err) => { reject(err); } ); } }); }; 复制代码
- 爬楼梯方式
// 1 1 // 2 2 // 3 4 111 12 21 3 // 4 1111 121 112 211 13 31 4 22 // 四级台阶有多少种方法? const sum = (n) => { if (n == 1 || n == 2) { return n; } return sum(n - 1) + sum(n - 2); }; 复制代码
其他
- 职业规划,对架构师的理解
- 工程化,组件化,模块化
- 渐进式 Web 应用(PWA)
- WebAssembly
- 重绘和重排
- jquery 和 vue 操作 dom 渲染的区别
- 区别:操作 dom 和操作数据
- 实现及时:虚拟 dom;diff 算法,渲染的优化处理 diff 算法
- 如何实现发包之后改前端配置,不发布也可以更新
- 元素在页面隐藏的方式
- display:none
- visibility:hidden
- opacity:0
- 设置 height、width 模型属性为 0
- position:absolute,将元素移出可视区域
- transform 偏移:同上
- clip-path:裁剪
- CSS 高度塌陷的原因;如何解决(BFC)
- 原因:
- 子元素浮动,父元素就会塌陷
- 当子元素添加了 positive:absolute 属性,父元素高度塌陷,高度为 0
- 解决:
- 触发 BFC:overflow hidden
- 清除浮动:clear both;after 伪元素
- 敏捷开发的理解
- 遇到的问题,怎么解决的
- 自己估计的工作量和实际不符,完不成的情况?
- 频繁变更了需求,会咋办,改咋处理?
- webGL 三维场景渲染优化
- 改变 this 指向的方式:apply,call,bind()
- 有一个请求,没有收到数据,如何排查:postman,联调方式,日志工具
- 如何节省沟通成本
- 和 HR 有需求不同的讨论,如何 battle
- 和后端接口对接,如何确定哪些后端实现,那些前端实现:不能靠感觉,标准是啥?如性能,用户体验等等
- 会对需求提出意见吗?有反驳过产品的需求吗
- 数据埋点如何实现
- 模块化方式,import 和 require 区别
- CommonJs (典型代表:node.js 早期):用于服务端
- 都运行在模块作用域,不会污染全局作用域
- 同步加载的,即只有加载完成,才能执行后面的操作
- 首次执行后就会缓存,再次加载只返回缓存结果,如果想要再次执行,可清除缓存
- require 返回的值是被输出的值的拷贝,模块内部的变化也不会影响这个值
- AMD (典型代表:require.js):异步模块定义
- 所有依赖模块的语句,都定义在一个回调函数中,等到模块加载完成之后,这个回调函数才会运行
- require
- CMD (典型代表:sea.js)
- ES6 的 Module:import/export
- CommonJS 和 AMD 模块,都只能在
运行时确定这些东西 - ES6 设计思想是尽量的静态化,使得
编译时就能确定模块的依赖关系,以及输入和输出的变量 - import()允许您仅在需要时动态加载模块,而不必预先加载所有模块
- 前端性能优化:评价指标;具体优化实现;
- webpack 配置,优化实现;核心概念:plugin,loader;loader 实现原理,如对 css 的处理解析