针对简历的面试题

195 阅读15分钟

CSS3新特性:

选择器 :hover :focus

边框 :圆角边框 border-radius 盒子阴影box-shadow

背景: 背景尺寸 background-size

渐变 :线性渐变 linear-gradient 径向渐变:radial-gradient

文字效果 text-shadow:文本阴影 text-wrap:文本换行规则

字体: font-face 规则中定义的

转换和变形 : transform translate

过渡;transition

动画:规定动画 @keyframes animation

HTML5新特性:

语义化标签: 语义化标签使得页面的内容结构化,见名知意

增强型表单:提供了更好的输入控制和验证

新增表单属性:placehoder/required/min 和 max

autofocus 属性,是一个 boolean 属性。规定在页面加载 时,域自动地获得焦点。

视频和音频:元素支持三种音频格式文件: MP3, Wav, 和 Ogg

Canvas绘图

SVG绘图

地理位置

拖放: 拖放是一种常见的特性,即抓取对象以后拖到另一个位置

WebWorker:是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能

WebStorage:

使用HTML

1.可以在本地存储用户的浏览数据,

2.web存储更加安全快速,这些数据不会保存在服务器,

3.可存储大量数据而不影响性能,5M

4.以键值对形式存在,

5.web存储的数据只允许该网页使用

6.只能存储字符串类型,对于复杂数据类型可以使用Json对象提供的stringify与parse处理

存储数据有两个对象:

localStorage-没有时间限制的数据存储

sessionStorage: 针对session的数据存储,当用户关闭浏览窗口后会被删除

优点:

存储空间大 cookie为4kb webStorage为5M

节省网络流量:不会传送到服务器,减少了客户端和服务器的交互

显示快速

安全性高:不会随着http header发送到服务器,不会担心截获所以安全性更高一些

操作方便

盒模型:

css3中有两种盒模型

Es6新特性:

变量声明:

let 不允许重复声明

const 不允许修改

解构赋值
字符串/数字/数组/对象扩展的方法:

includes() :是否存在指定字符串

startsWith() :是否存在字符串头部指定字符串

endsWith() :是否存在字符串尾部指定字符串


Number.parseInt() :返回转换值的整数部分


数组的:扩展运算符(...)

箭头函数:自己没有this 它的this指向取决于外部环境、


浏览器的垃圾回收机制

标记清除:

标记简短为所用的活动对象做上标记,清除阶段则把没有标记(也就是非活动对象给销毁)

如何标记:反转某一位(通过一个二进制字符来表示标记)

标记清除算法的过程:

  • 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全标记为0
  • 然后从各个根对象开始遍历,把不是垃圾的节点改成1
  • 清理所有标记为0的垃圾,销毁并回收它们所占用的内存空间
  • 最后,把所有内存中对象标记修改为0,等待下一轮垃圾回收

标签清除的缺点:

内存碎片化: 标记清除算法有一个很大的缺点,就是在清除之后,剩余的对象内存位置是不变的,也会导致空闲内存空间是不连续的,出现了 内存碎片(如下图),并且由于剩余空闲内存不是一整块,它是由不同大小内存组成的内存列表,这就牵扯出了内存分配的问题

分配速度慢:因为即时是使用fist-fit策略,最坏的情况是每次都咬遍历到最后

三种分配策略:

  • First-fit,找到大于等于 size 的块立即返回
  • Best-fit,遍历整个空闲列表,返回大于等于 size 的最小分块
  • Worst-fit,遍历整个空闲列表,找到最大的分块,然后切成两部分,一部分 size 大小,并将该部分返回

fist-fit ;空闲分区按照内存容量递增的次序链接,分配内存时候顺序查找,找到大小能满足要求的第一个空闲分区

如何解决: 使用标记整理算法,标记结束后,标记整理算法会将活着的对象向内存的一端移动,最后清理边界的内存

引用计数:

把对象是否不再需要简化为对象没有被其他对象引用,如果没有被引用,对象就被垃圾 回收

缺点:需要一个计数器,所占内存空间大,因为不知道被引用数量的上限

无法解决循环引用导致的无法回收问题

循环引用: 简短来说就是对象1某个属性指向对象2,对象二的某个属性由指向了对象1

chrome 的v8垃圾回收机制是居于标记清除法:

针对新生区域采用并行回收

阵地老生区,采用增量标记和惰性回收


跨域的原理与解决办法:

什么是跨域:

指的是浏览器不能执行其他网站的脚本,他是由浏览器的同源策略造成的,是浏览器对javascript实施的安全限制

什么是同源:

就是域名 协议 端口号均线同

cors:

因为同源策略严重影响了项目之间的连接,尤其是大型项目,需要多个域名配合完成,所以w3c推出了cors 即 Cross-origin resource sharing (阔哦斯-哦瑞金-瑞所系-摄顶)(跨域资源共享)

cors 需要浏览器和服务器同时支持,目前所有浏览器都支持该功能

cors通信与浏览器的ajax通信没有区别,代码完全一样,浏览器在跨域访问时,会自动添加http头信息,或者发起预检请求,用户对此毫无感知,因此,是否支持跨域氢气,关键在于服务器是否做了cors的配置

浏览器将跨域请求分为两种:

简单请求和非简单请求

简单请求:

请求方法为以下三种之一:

  • get
  • post
  • head

http头信息不超出:

  • accept
  • accept-language
  • content-language
  • last-event-id
  • content-type:application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不满足以上条件的,就属于非简单请求,就比如json格式的请求,他的请求头信息就是content-type :application/json

对于简单请求的处理:

采用先请求后判断的方式,就是先发出cors请求,在请求头中添加origin字段,orgin字段用雷向服务器说明,本次请求来自与那个源(协议 域名 端口),服务器决定是否允许访这个源访问.

服务器判断该源不在自己的允许范围内,就返回一个正常的Http响应,浏览器判断响应头中是否包含Access-control-Allow-Origin字段,如果没有,浏览器就知道服务器不允许跨域访问,就会抛出错误,

如果origin在服务器的允许范围内,服务器的http响应就会包含:

Access-contro-allow-origin:

他的值要么是请求时候的origin字段的值,要么就是一个* (表示接收任意域名的请求)

Access-control-Allow-credentials

他的值是一个布尔值,表示允许发送cookoe,默认情况下,cookie不包括在core请求之中,设置为true,表示服务器明确许可,cookie可以包含在请求中,一起发给服务器

Access-control-Allow-headrs

允许浏览器在cors中发送的头信息

Access-control-allow-methods

允许浏览器在cors中使用的方法

浏览器受到服务器返回的http响应后就知道什么样的cors请求是被允许的

非简单请求:

浏览器会采用预检请求 ,询问服务器,是否支持跨域请求,

在正式请求之前,浏览器会预先发送一个额外的options请求,询问当前网页所在的域名是否在服务器的可许名单之中,以及可以使用那些方法和请求头字段,只有得到肯定答复,浏览器才会正式发出http请求.否则就报错

服务器受到预检请求之后,检查origin accss-control-request-method access-contor-request-headrs 并作出响应,

且响应头中会比简答请求多出Access-contro-max-age

用来指定本次预检请求的有效期 单位是秒

且在有效期间 可以直接发送正式请求,不在发送预检请求

跨域的解决办法:

分别从客户端,和服务器去解决

客户端: 开发调试过程中使用

右键打开Chrome桌面快捷方式(已打开的Chrome网页请先关闭),点击“属性”进入到“快捷方式”选项卡,再选中“目标”项,添加“--disable-web-security --user-data-dir”(见下图)

服务器: 代理转发 配置cors

1.代理转发:

增加代理服务器,和资源服务器放在同于域名下,接口请求走代理服务器,这样变成了同源访问,不存在跨域问题,

注意问题:代理服务器应该有后端的目标服务器性能相匹配,否则就会成为整个系统的性能瓶颈

2.反向代理

1.在开发环境中,只需要配置vue.config.js中的代理选项devServer.proxy

在里面配置path: options组成的对象 只要包含该路径就触发代理机制

2.在生产环境 需要**Nginx**的反向代理来进行

Nginx 相当于起了一个跳板机,这个跳板机的域名也是client.com,让客户端首先访问 client.com/api,这当然没有跨域,然后 Nginx 服务器作为反向代理,将请求转发给server.com,当响应返回时又将响应给到客户端,这就完成整个跨域请求的过程。

3.配置cors 一般公司软负载和应用服务组合为nginx+tomcat

只需要在nginx的配置中增加

location / {  
 add_header Access-Control-Allow-Origin *;
 add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
 add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
 if ($request_method = 'OPTIONS') {
 return 204;
    }
}

优点是不用修改应用代码,缺点就是无法根据请求参数的不同返回不同的结果,也就是无法做到细粒度编程

另一种就是修改应用代码在代码中增加filter就是过滤器,在过滤器中添加响应的判断,这样就可以在解决跨域的同时,满足复杂的业务需求.


hash 模式的实现原理

早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL# 后面的内容。比如下面这个网站,它的 location.hash 的值为 #search

https://www.word.com#search
复制代码

hash 路由模式的实现主要是基于下面几个特性:

  • URLhash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
  • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制 hash 的切换;
  • 可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URLhash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URLhash 值;
  • 我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

(2)history 模式的实现原理

HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState()history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:

window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
复制代码

history 路由模式的实现主要基于存在下面几个特性:

  • pushStaterepalceState 两个 API 来操作实现 URL 的变化 ;
  • 我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
  • history.pushState()history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。

react组件封装的注意点:

  1. 一般组件要支持style与className,👍方便组件自定义样式

    1. 考虑联合字面量+字符串,提供代码提示
    2. 复用React或第三方的类型,快速获得代码提示
    3. 🔔自己写代码的技巧:TSX中封装组件,一般需要先定义props类型,后使用。类似vue中的props

针对项目的面试问题:

你是怎么实现路由懒加载的?

1.导入组件方式改为React.lazy()+回调函数,回调函数返回值为import()+组件的地址

2.使用React.Suspense 包裹路由

3.React.Suspense 的属性fallback传入加载中显示的内容

路由懒加载的好处?

1.分批次加载可以提高页面的加载速度

2.异步加载会将页面数据缓存,再次切换回页面会直接从缓存中获取数据


CSS Modules使用的步骤?

1.样式文件命名为xx.module.scss

2.使用js方式导入样式

CSS Modules是如何解决样式问题的?

1.此方法引入被打包好的样式文件中样式名就编程了组件名加一串哈希值

2.引入的对象中属性名是之前类名,值为改变后的类名

3.此时只要,全部将之前的类名改为对象.属性名的格式就ok

4.但是有两个问题 1.全部都该为这样麻烦 2.样式名中有横杠的话需要加小括号

5.所以使用推荐写法 除了根元素,其他元素别:global包裹

6.这样的写法就只用根元素使用对象点语法写类名,其他的类名不用改变


customize-cra react-app-rewired postcss-px-to-viewport 分别是做什么的?

1.因为react的webpack配置没有对外暴漏出来,所以使用 customize-cra 配合react-app-rewired 来添加和 覆盖脚手架的 webpack 配置,安装完成之后需要修改为carco start

2.postcss-px-to-viewport 插件可以使代码中使用px,打包后自动转化为vw,但是要配置视口宽度


什么是组件化开发?

主要做的就是,拆分功能,封装组件,单独维护

就是将页面的某一部分独立出来,将这一部分的 数据层(M)、视图层(V)和 控制层(C)用黑盒的形式全部封装到一个组件内,暴露出一些函数和属性供外部组件调用。

开发一个页面,就像是搭积木一样,将各个组件拼接到一起,最后融合到一起,就是一个完整的系统。

组件化开发优点:

降低耦合性,提高聚合性

耦合性:是指模块之间的依赖关系

模块间联系越多,其耦合性越强,同时表明其独立性越差

耦合:一个软件结构内不同模块之间互连程度的度量。

高聚合:一个模块内部各个元素彼此结合的紧密程度的度量。


微信支付流程:

前端流程分成四个大的步骤:

1.获取用户信息和微信临时凭据来换取token

1.在用户点击事件中调取wx.getUserProfile中获取用户信息,必传入一个对象,描述,声明个人信息用途.

2.wx.login 获取code 用户临时凭证

3.将获取用户信息和token合并传给后端获取token

2.创建订单获取订单编号

1.准备订单需要的参数+token,发送请求获取订单编号

3.根据订单编号创建支付流程,获取订单编号

1.将订单编号+token传给后端,返回Payment需要的参数

4.调起微信支付,完成功能

1.wx.requestPayment 传入上一部后端返回的参数

什么是websocket

是一种在单个TCP连接上进行全双工通信的协议

特点:

最大特点:服务器可以主动向客户端推送信息

建立在 TCP 协议之上,服务器端的实现比较容易。

数据格式比较轻量,性能开销小,通信高效

可以发送文本,也可以发送二进制数据

没有同源限制,客户端可以与任意服务器通信。

应用:

1.即时通讯,聊天

2.热更新:webpack-dev-server

如何通过socket.io 实现通信

1.通过io(地址+配置如token 和 传输协议['websocket'])

2.通过实例对象.on 监听第一次连接

3.通过实例对象.emit 向服务端发送消息

4.通过实例对象.close() 关闭连接


实现自动回复机器人

在进入聊天机器人页面时,接就是页面首次加载完成后,

1.通过io创建socket实例对象

2.通过on方法监听第一次连接,在参数的回调函数中,编辑信息通知用户,已连接服务器.

3.在用户的发送按钮绑定事件,触发emit方法将数据发送给后台,同时展示在页面

4.服务端发送的数据通过on方法参数的回调函数中,并在回调函数中将数据展示在页面

5.当组件卸载,关闭页面时候 通过close方法关闭连接

其中有三个注意点:

1.io方法创建的实例对象需要通过useRef绑定到ref的current属性上,方便在组件的整个生命周期内调用

2.数据更新到视图是使用useState,因为要依赖当前state跟新所以要将setstate的参数改为函数方式,就可以链式地进行更新,并确保它们是一个建立在另一个之上的

3.想要每次发送或接收消息,屏幕都会自动滚动,使用usreffect监听聊天列表的变化,每次变化,就调用window.scroTo方法,将屏幕上滑


formik+ypm实现表单校验

1.通过useFormik钩子函数创建formik对象,有两个必穿参数默认值initialValues,和提交事件onsubmit

2.将表单的onSubmit事件绑定上formik.handleSubmit

3.将表单绑定为受控组件

value={formik.values.mobile}
onChange={formik.handleChange}

4.使用yup接管表单的校验

 validationSchema:yup.object().shape({})

5.就可以用链式调用的方法去校验规则

介绍一下什么是无感刷新?

作用是:为了避免用户频繁登录

在token过期后,用户再进行操作时候,先用refresh_token获取新的token再进行后续操作

如何具体实现?

方案有两种,

第一种是,再请求拦截器中,通过expires_in字段判断access_token是否过期,如果过期就先挂起请求,先刷新access_token再继续请求

第二种写在拦截器中,用户发起请求,如果返回access_token过期,就先通过refresh_token获取新的token,再此重新发送请求

你们公司access_token 和 refresh_token 多长时间?

refresh_token 7天 access_toekn 2个小时 7200s

怎么处理后端返回的异常

一般分三个情况进行处理,

1.断网 提示用户网络问题

2.非401 与后端协商比较友好的提示抛出

3.401 就先判断有无token ,有token的话,再判断有没有refresh_token,没有的话就返回登录页,如果有的话就携带refresh_token向后端发起请求获取token,再进行一次请求


你是如何做路由鉴权的?

1.将路由Route组件删除component属性,添加render属性,此属性接收一个回调函数,函数的返回值就是该路由显示的内容

2.在回调函数中判断是否有token,有的话就正常返回页面组件,没有token就重定向回login页面,要注意的是要将原本组件的属性透传回去

3.封装组件,返回值为该路由route

1.将path 和 compont 和剩余属性解构出来

2.将path compont 和剩余属性传给 route的属性上 ,因为compont 是组件所一要解构时冒号候改大写开头

4.使用封装好的组件,正常传入path 和 componet就有路由鉴权功能了


你在工作中遇到的比较大的难题是什么?你是怎么解决的?



vue相关

介绍以下vuex如何实现状态共享

1.公共状态数据定义在state中

2.mutations是一个对象,中存放修改state的方法

3.action中负责进行异步操作

4.getters 是state派生出的状态

vue其他传递状态的方式

1.父向子 props

2.子向父 $emit / on 接收

3.ref传参

讲一下PBAC如何进行权限管理

PBAC是基于角色的权限访问控制,核心是用户只和角色相关联,角色是一系列权限的集合.

三要素:

用户:系统中所有账户

角色:一系列权限的集合

权限:菜单 操作 数据 权限

优点:授权与管理非常灵活

如何给用户设置角色,给角色绑定权限

1.每个用户都哟一个角色id列表,存储自身的角色,给用户设置角色就是向后端发起请求修改列表中的数据

2.每一个角色都有一个权id限列表,存储角色权限

3.只要你拥有角色,角色拥有权限,登录之后后端就会将用户的权限标记插入到用户数据中,该数据存储在vuex中

4.将需要权限的路由通过name命名,命名与用户权限id一致

5.在导航守卫中,拿到动态路由也就是需要权限的路由数组,和用户的权限数组对比

6.过滤后通过路由的addRoutes方法追加路由配置,此时权限页面虽然可以访问但是没有能在侧边栏显示

7.将此方法封装在vuex中方便调用,

8.在生成侧边栏的js中调用与导航守卫调用该方法,并拼接静态路由生成侧边栏

控制按钮

1.给按钮绑定v-if v-if绑定函数

2.该函数通过判断用户是否有权限来返回true 或false

函数具体应是实参传入权限id,权限数组判断是否包含该权限,从而控制是否显示按钮


vue-cli 打包分析 优化:

1.安装 webpack-bundle-analyzer vue 直接 npm run preview -- --report ->第四步

2.在carao.confing.js中配置

 webpack: {
    plugins: [
      new BundleAnalyzerPlugin()
    ],
  },

3.npm run build 对项目进行打包完成自动就会弹出 各个导入包所占大小

4.首先对大的包尝试进行按需加载

5.将不方便按需加载,或比较大的包先排除打包,就是配置要排除的包名字,和告诉全局替代的变量名

6.在通过cdn网站 找到对应的包通过 标签引入

CDN加速

1.排除打包

2.在通过cdn网站 找到对应的包通过 标签引入

3.用户加载页面用到对应的包,回向距离更近的cdn服务器获取,

1.降低了延迟,提高访问速度

2.减小了主服务器的压力


解释下如何使用导入导出

导入:

1.下包 xlsx

2.去拿vue-element-admin找到封装好的UploadExcel 组件

3.放到公共组件库中,然后全局注册组件

4.组件on-success可以传入一个回调函数,函数形参就是excle列表中的数据

5.对数据进行拼接发送请求即可

导出:

1.按钮绑定点击事件,点击按钮向后端发起请求获取数据

2.去拿vue-element-admin封装好的工具Export2Excel 该工具依赖file-saverscript-loader

3.使用该工具的export_json_to_excel函数,该函数需要传入对象,对象中包含表头和数据两个属性,表头是数组,数据为二维数组

4.将后台获取到的数据转换为参数对应的格式

如何使用 dompurify对 HTML 内容进行净化处理?

就要了解为什么要对HTML 做净化?

比如要用dangerouslySetInnerHTML将字符串标签渲染为html标签,类似于vue的v-html,此时会容易受到xss攻击,此时使用DOMPurify.sanitize包裹要渲染为html标签的内容, 此时即使该标签被恶意注入了事件,也会返回一个干净的html.

防止什么是Xss攻击?

是比较常见的攻击 WEB 网站方法,攻击者通过注入非法的 html 标签或者 javascript 代码,从而当用户浏览该网页时,控制用户浏览器。


React


js高级

讲一下什么原型,什么是原型链

原型:

简单来说就是每一个javascript对象(除null以外),在 在创建的是时候就会关联另外一个对象,这个对象就是我们所说的原型,每个对象都会从原型继承属性

这个js对象,有一个属性叫__proto__这个对象的__proto__属性就会指向该对象的原型

原型链:

要清楚原型链就要先清楚 构造函数 实例原型(对象) 实例对象的关系

而且该原型对象的constructor属性,指向了该js对象的构造函数

而该构造函数的prototype属性指向了该对象的原型

每一个对象实例(除了null)的__proto__指向原型对象

然而原型实例同样也是对象,可以同Object构造函数生成,所以

原型对象的__proto__指向了Object构造函数的prototype

如果继续往上Object的原型对象的__proto__就是null,

这样一条相互关联的的链状解构就是原型链

我们访问对象属性时,如果对象内部没有的话,就会从该对象的原型对象中查找,如果该对象原型对象中没有的话,就会顺着原型链找到上级的原型对象,直到找到顶级null

这样就能理解创建的数组,对象,字符串等等为什么有那么多属性和方法,因为数组对象是由Array构造函数创建的,数组的原型对象上有众多方法

​
原型:
每一个js对象除了null,在创建时候,都绑定了一个对象.可以通过__proto__去指向该对象,这个对象就是我们所说的原型
​
原型链:
原型链简单的来说就是,相互关联的原型组成的链状结构就是原型链
​
需要我深入的讲一下么?
要了解原型链 就要先了解实例对象 原型链 构造函数的关系
实例对象的_proto__属性指向原型对象
原型对象的constort 指向实例对象的构造函数
构造函数的prototype指向原型对象
而原型对象也是对象 是通过Objcet构造函数创建
所以原型对象向上__proto__指向了Object构造函数的prototype,直到Object的原型对象的__proto__为null像这样的链式关系就叫做原型链,
  更具体一点的话就是
  我们访问对象属性时候,如果该对象内部没有的话就会从该对象的原型去查找,这个原型对象有有自己的原型对象,于是会一直找下与去直到Object的prototype.

闭包

什么是闭包?

是值那些能够访问自由变量的函数

什么是自由变量?

自由变量是指,在函数中使用的,既不是函数参数也不是函数的局部变量的变量

new 操作符做了什么? (new的实现)

一句话来说就是

new运算符创建一个用户定义的对象类型,或具有构造函数内置对象类型

1.创建一个空对象

2.根据原型链,设置空对象的 __proto__为构造函数的oprototype

3.构造函数的this指向该对象,执行构造函数的代码(为新对象添加属性)

4.判断函数的 返回值类型,如果是引用类型,就返回这个引用类型的对象

4.如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用会自动返回这个新的对象。

\