# 前端面试
# 自我介绍
面试官你好,我叫 charles,从事前端已经 8 年时间,熟悉 flutter,vue,react,node,数据结构和算法,做过很多的项目,其中包括独立开发和团队开发的项目,例如后台管理系统,手机端,app,H5,PC,toB,toC 都是有过接触的,我介绍完了。
# CSS 水平垂直居中
- Flex 布局,justify-content:center,align-items:center,不考虑兼容性
- 子元素:margin:50%,transform:-50%。
- 绝对定位
# 元素隐藏
- display:none,不会占用文档流
- visibility:hidden,会占用文档流
# 让谷歌浏览器支持小于 10px 的字体
- scale
- 通过 js 去掉谷歌浏览器的默认行为
# v-if 和 v-show
- v-show 是通过 display:none,css 层面实现的。v-show 的代码是会继续运行的,生命周期继续运行。举例:表单验证。
- v-if 是通过渲染层实现的,是否渲染。
# 多组件传值
- 父传子:prop
- 子传父:通知的形式
- 兄弟组件:vuex,eventbus,LocalStorage,文件,数据库。
# vue 生命周期
vue2 阶段
beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroye,Destroyed。
vue3 阶段:把最后两个生命周期变成了 unmount,unmounted,新增了 composition API,把 beforeCreate,created 换成了 setup。
在日常开发当中,大多数时候可以在 created 里面去操作一些数据,当 created 被回掉的时候,vue 整个实例、状态、计算属性、watch 以及方法都初始化完成了,created 里面做一些数据请求,大部分的事情都可以在 created 里面完成,vue 把虚拟 Dom 渲染成真实 Dom 是需要一些时间的,在这样的一种场景下,在 created 里面是无法获取真实 dom 节点的,此时可以在 mounted 里面获取。beforeUpdate 数据修改后,视图改变前,updated:数据修改后,视图改变后,vue 提供了一个 nexttick 的 API,从 vue3.2 的源码我们可以知道,在 vue 运行时的阶段(runtime 阶段)去渲染的时候通过了一个叫做任务队列的形式去进行渲染的,这个任务队列是基于 Promise.resolve 的一个异步渲染,在
# vue2 和 vue3 的区别
响应性:Object.defindProperty 去实现响应性,Object.defindProperty 的本质是监听对象的 get 和 set 方法,这样的化如果在 vue2 的对象在后期某一个时刻新增了一个属性,因为 js 本身的限制,我们是没有办法监听到这个对象新增了一个属性。vue2 为了解决这个问题,新增了一个 Vue.$set 方法,这个方案的本质就是把这个对象重新过了一遍 Object.defindProperty 这个过程,从程序设计的理念来看,其实是不合理的,所以在 vue3 中出现了反射和代理两个概念,所谓的反射是指 reflect,所谓的代理是指 proxy,通过 proxy 去代理了一个复杂数据类型,通过这样一个复杂数据类型的代理,得到一个代理对象,就是 proxy 的实例,然后通过 reflect 来确定了 this 的指向,对于 proxy 而言,只能去监听复杂数据类型的响应,没法监听简单数据类型的响应,比如说基本的 String,int,所以 vue 提供了一个 Ref 方法,Ref 方法本质上其实是把内容分成了 2 个部分,第一部分叫做复杂数据类型,在 vue 内部会直接通过一个 toRective 的方法指向 Reactive,另外一个简单数据类型,会在内部去处理一个 RefImpl 的接口,通过这个接口实现了两个方法,一个叫 get 标记到 Value,一个叫 set 标记的 Value ,通过 Ref 的形式,我们触法.value 这个形式,本质上是触发他的 value 方法。remtlpt ref.value
运行时:把宿主环境和渲染环境做了隔离
编译器:
parse 阶段:templete 编译成 render 函数,通过 baseParse 把 template 转化为 AST。
transform 阶段:利用 transform 把 ast 转化为 javascriptAST,
generte 阶段:利用 generte 函数把 javascriptAST 拼接成 render 函数
# 项目首屏提速
用户感知提速:加载动画
技术层面的提速:可以考虑 SSR 渲染,首先在服务端进行 preRender 的预渲染,然后把后续的渲染交给 CSR 客户端渲染。
静态资源的渲染:图片的加载,数据的加载。
# 离线功能
第一次打开和第 n 次打开,第一次打开就离线给初始数据,在有网络之后从服务端拿到最新的数据,把旧的数据迭代成新的数据。
离线数据,如果是 app 端存在 sqlite 里面,如果是 web 端存在 localStorage 里面,大数据可以存在 indexDB 里面。
# 闭包
概念:有权访问另一个函数作用域中的变量的函数;一般情况就是在一个函数中包含另一个函数。
原理:闭包的实现原理,其实是利用了作用域链的特性,我们都知道作用域链就是在当前执行环境下访问某个变量时,如果不存在就一直向外层寻找,最终寻找到最外层也就是全局作用域,这样就形成了一个链条。
应用:一个 Ajax 请求的成功回调,一个事件绑定的回调方法,一个 setTimeout 的延时回调,或者一个函数内部返回另一个匿名函数
问题:其实闭包也有有缺点的,它比起普通函数会占用更多的内存。(**全局作用域:**优点:可重复使用,随处可用,缺点:会造成全局污染),由于闭包会缓存上级作用域的变量,导致无法被垃圾回收机制进行回收,会增加内存的使用,导致内存泄露。
总结:闭包保存现场的作用场景。闭包结合了局部变量和全局变量的优点。可以使变量不污染全局,但是又能对变量进行重用。
# 如何解决内存泄露
常见的内存泄漏场景:
被忽视的全局变量引用
被遗忘的定时器(setInterval)或回调(resize)
DOM 引用丢失(就是 dom 被移除了,但是绑定在其身上的各类事件还在)
ES6 的一些语法的使用:Map、Set 等
console 导致的内存泄漏 因为打印后的对象需要支持在控制台上查看,所以传递给 console.log 方法的对象是不能被垃圾回收的。我们需要避免在生产环境用 console 打印对象。
第三方库的引用与销毁
- 比如在 Vue 项目中引入第三方库的示例:避免内存泄漏 (opens new window)
- 比如一些音视频的播放器,在不使用时,要及时的 destory 而非简单的移除其 DOM 元素
# Cookie、Session、LocalStorage 的区别
Cookie 是浏览器访问服务器后,服务器传给浏览器的一段数据,存在于相应头中,浏览器需要保存这段数据,不得轻易删除,此后每次浏览器访问该服务器,都必须带上这段数据,用于识别用户身份,Cookie 的存储空间只有 4k。
cookie 属性项
属性 | 属性描述 |
---|---|
Expires | 过期时间,在设置的某个时间点后该 Cookie 就会失效 |
Domain | 生成该 Cookie 的域名,如 domain="www.baidu.com (opens new window)" |
Path | 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/ |
Secure | 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie |
HttpOnly | 设置后,只能通过 HTTP 响应报文的 Set-Cookie 来新增或更新 cookie ,客户端无法通过脚本的方式来读写 cookie。 |
SameSite | cookie 在跨域时是否应该被发送。 |
Session 是服务器存储用户会话所需的信息,只在当前页面有效。
LocalStorage 是前端用与在本地存储一些信息,大概 5M。
# 什么是 XSS 攻击和 CSRS 攻击
简单的理解:
XSS 攻击: 跨站脚本攻击。
攻击者脚本
嵌入被攻击网站
,获取用户 cookie 等隐私信息。CSRF 攻击: 跨站请求伪造。
已登录用户
访问攻击者网站
,攻击网站向被攻击网站发起恶意请求(利用浏览器会自动携带 cookie)
服务端可以通过设置 Cookie 的 HttpOnly 属性来预防 XSS 攻击,设置 Samesite 属性防止 CSRF 攻击。
# 浏览器的 CSS 会阻塞 DOM 树的渲染吗,会阻塞 JS 的执行吗?
在浏览器的渲染过程中,HTML 会被解析成 DOM 树,CSS 会生成 CSS 样式表,两个合并生成真实渲染树 rendertree,如果 CSS 没有加载、解析或者阻塞了,在生成真实渲染树的时候就会卡住,导致影响后面的 js 的执行。
# 浏览器强制缓存与协商缓存的区别
强制缓存:当第一次访问一个网站后,静态资源如图片、js、css、icon 等都是从服务器下载下来的,如果刷新网页,这些资源没有必要重新请求的,因为没有变动,像这种资源浏览器会默认认为是一些强制缓存,那么强制缓存的资源当第二次访问的时候不会去服务器上重新下载了,会从浏览器本地的缓存去取这些资源。
协商缓存:和后台进行协商这个资源要不要进行缓存,如果是协商缓存的资源,第一次返回的结果里面会带有是否更新 Last Modified 的一个状态和一个唯一标识 etag,只要这个资源在服务端没有变化,当下次访问的时候,后台会对比这个标签 etag 和 Last Modified 的时间,如果说这个资源没有变动,会返回 304 的一个状态,浏览器接收到 304 的状态之后,会认为这个资源没有变化,只需要从本地缓存获取。
# let 和 var 的区别
- let 声明局部变量,var 声明全局变量
- var 声明的变量存在变量提升,可以在声明前访问到
- let 不允许重复声明,var 可以
# 浏览器从输入网址到页面展示的过程
- URL 输入
- 判断有无缓存
- DNS 解析 (opens new window)获取 IP
- 建立 TCP 连接
- 发送 HTTP / HTTPS 请求(建立 TLS 连接)
- 服务器 (opens new window)响应请求
- 浏览器解析渲染页面
- HTTP 请求结束,断开 TCP 连接
# less 和 sass 的区别
Less 和 Sass 的主要不同就是他们的实现方式。
Less 是基于 JavaScript,是在客户端处理的。 Sass 是基于 Ruby 的,是在服务器 (opens new window)端处理的。
关于变量在 Less 和 Sass 中的唯一区别就是 Less 用@,Sass 用$。
# 讲一下响应式布局的实现
前端响应式布局原理与方案 (opens new window)
# 伪数组转为真数组
Array.from
# 浅拷贝和深拷贝
浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
Object.assign、 扩展运算符、 Array.prototype.slice()、 Array.prototype.concat() 等
深拷贝复制变量值,对于引用数据,则递归至基本类型后,再复制。
深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
JSON.parse(JSON.stringify(obj))
lodash.cloneDeep
# 跨域以及如何解决
同源策略:是浏览器的一种安全策略,所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源,如果缺少了同源策略,浏览器很容易受到 XSS、CSRF 等攻击。
解决跨域:
# Jsonp
jsonp的原理就是利用<script>
标签没有跨域限制,通过<script>
标签 src 属性,发送带有 callback 参数的 GET 请求,服务端将接口返回数据拼凑到 callback 函数中,返回给浏览器,浏览器解析执行,从而前端拿到 callback 函数返回的数据。
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
# 跨域资源共享(CORS)
CORS是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求
# )nginx 反向代理接口跨域
通过 Nginx 配置一个代理服务器域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域访问。
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}