1.BFC了解(什么是BFC,如何实现BFC,解决什么问题)
BFC是
浏览器自带的一种CSS渲染模式,
也是一种盒子模型
四种方式开启BFC盒子
-
float不是none (有float属性且不为none,此时盒子自动变成BFC盒子)
-
overflow属性不是visible(有overflow属性且不为visible,此时盒子自动变成BFC盒子)
-
position属性不是static和relative
-
display属性为以下值 : table-cell 、 inline-block 、 table-caption
开启BFC 盒子变化
-
当一个块级盒子触发BFC之后,其内部形成独立环境,在里面做任何事都不会对盒子外部有影响。
-
在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列
BFC解决哪些问题
-
清除元素内部浮动
-
解决盒子margin合并问题(塌陷也可以解决)
-
实现元素宽度自适应多列布局
-
制作右侧盒子自适应宽度的问题(左侧盒子宽度固定,右侧宽度不固定)
-
当在父元素中只设定一个盒子浮动,另一个不浮动时,会造成第二个盒子在第一个盒子的下方,被覆盖掉一部分(但文字不会被覆盖)。
-
-
2.事件委托
-
1.什么是事件委托
-
给父元素注册事件,委托给子元素处理
-
-
2.事件委托原理:==事件冒泡==
-
3.事件委托注意点
-
this : 指向父元素
-
e.target : 指向触发事件的子元素
-
-
4.事件委托场景 :
给动态新增元素注册委托事件
3.如何判断数据类型
- typeof有两种数据类型无法检测: null 、array
- Object.prototype.toString.call(数据)
4.浅拷贝与深拷贝
-
1.浅拷贝:拷贝地址, 修改拷贝后的数据原数据也会变化
-
2.深拷贝:拷贝数据, 修改拷贝后的数据原数据不会变化
-
推荐 json : let obj = JSON.parse( JSON.stringify( 对象 ) )
-
递归
-
5.for in与for of区别
1.功能不同
for-in是遍历数组的下标
for-of是遍历数组的元素
2.原型的属性
for-in会遍历原型的属性
for-of不会遍历原型的属性
3.数据类型
for-in可以遍历Object类型
for-of不可以遍历Object类型
总结:如果只想要属性值/元素,使用for-of效率更高
6.get请求与post请求区别
-
1.传参方式不同
-
get在url后面拼接(请求行)
-
post在请求体传参
-
-
2.大小限制不同
-
get有大小限制,不同浏览器大小限制不同。 一般2-5 MB
-
post没有大小限制
-
-
3.安全性不同
-
get参数直接暴露在url,不安全(一般查询类数据都是get)
-
post参数在请求体中,更加安全(一般登录注册必须是post)
-
-
4.传输速度不同
-
get传输速度快
-
post传输速度慢
-
7.一个页面从输入url到呈现过程
-
1.DNS域名解析: 将url中的域名解析成ip地址
-
2.TCP三次握手: 建立安全的网络传输协议
-
2.1 什么是TCP : 一种 传输控制协议
-
2.2 TCP作用 : 保证HTTP网络传输是 安全 + 可靠的 (检测客户端 与 服务器的网卡是不是通的)
-
2.3 TCP三次握手 :
第一次: 浏览器 -> 服务器 (你能听到我说话吗?,检测浏览器:发送)
第二次: 服务器 -> 浏览器 (我听到了,你能听到我说话吗。 检测浏服务器: 接收 + 发送)
第三次: 浏览器 -> 服务器 (嗯,我听到了. 检测浏览器: 接收)
-
-
3.HTTP建立连接
-
3.1 客户端发送请求
-
3.2 服务器处理请求
-
3.3 服务器响应请求
-
-
4.渲染引擎开始渲染响应返回的HTML文本
-
4.1 解析html生成:dom树
-
4.2 解析css生成:样式树
-
4.3 dom树 与 样式树 合并得到 渲染树
-
4.4 呈现页面
-
8.防抖节流
-
函数防抖:单位时间内,频繁触发事件,只会触发最后一次
-
函数防抖实际开发应用场景: 实时获取输入框文本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="text" placeholder="请输入文本">
<script>
/*
1.函数防抖 : 单位时间内,频繁触发事件,只会触发最后一次
2.经典应用场景 : 输入框输入事件
3.函数防抖流程 :
3.1 声明全局变量存储定时器ID
3.2 每一次触发事件, 先清除上一次定时器。 然后将事件处理代码放入本次定时器中
*/
let timeID = null
document.querySelector('input').oninput = function(){
//清除上一次定时器
clearTimeout(timeID)
//开启本次定时器. 500ms内用户没有触发,自动执行定时器代码。
timeID = setTimeout(()=>{
console.log(`发送ajax请求,搜索内容为${this.value}`)
},500)
}
</script>
</body>
</html>
-
函数节流:单位时间内,频繁触发事件,只会触发一次
-
函数节流应用场景 : 解决高频事件,频繁触发事件浪费性能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body{
width: 3000px;
height: 3000px;
}
</style>
</head>
<body>
<script>
/*
1.函数防抖 : 单位时间内,频繁触发事件,只会触发一次
2.经典应用场景 : 滚动条事件
3.函数防抖流程 :
3.1 声明全局变量存储上一次触发交互时间
3.2 每一次触发事件, 获取当前时间 与 上一次时间做比较。判断是否超过节流间隔
3.3 如果 超过节流时间,则执行事件处理代码。 并且存储本次触发时间。
*/
let lastTime = null
window.onscroll = function(){
//判断时间间隔
let currentTime = Date.now()
if( currentTime - lastTime >= 500 ){
console.log('执行滚动条事件处理代码')
//存储本次触发时间
lastTime = currentTime
}
}
</script>
</body>
</html>
9.什么是跨域
跨域是浏览器一种安全策略,如果你的ajax地址与页面地址不同源,也就是协议名和ip地址和端口号不一致的时候,浏览器就会拒收服务器响应的数据。一般在开发中,公司后台主要使用cors技术来解决跨域,前端不需要做任何处理,后台大概好像是设置一个响应头'Access-Control-Allow-Origin '.前端出来在config.js中设置代理服务器,服务器与服务器不存在跨域.
10.前端web优化方案
-
1.减少HTTP请求数
-
这是必须要放在第一个回答的,因为这是优化web最优方案
-
具体示例 : 精灵图、多个文件合并成一个
-
精灵图适用场景 : (1)图片不经常更换的,比如按钮图标 (2)图片不能太大
-
-
-
2.资源压缩
-
就是一般我们第三方包有一个min版本,就是通过压缩文件体积来优化web
-
-
具体示例 :响应min文件
-
3.合理利用浏览器缓存
-
某些接口的数据是固定的,服务器就没有必要每一次都响应数据。可以让浏览器进行缓存。
-
具体示例:例如省市县数据, 这种数据一般不会变化
-
11.请说一下你对promise的理解
promise是ES6新增的一个构造的数,主更是用干好决开发中的回调地默问题。promise对象有三种工作状态,分别是进行中,已成功,已失败。一旦创建promise就会立即进入进行中状态,此时立即执行promise里面的代码
promise状态改变有两种,从进行中到已成功,此时对应resolve方法(通过resolve修改状态为已成功)从进行中到己失败,此时对应reject方法
promise状态一旦改变就不会再变,resolve和reject分别对应promise实例的then和catch
promise相关的几个静态方法
- promise.race():接收多个promise实例,可以得到最先处理完毕的结果(可能成功,可能失败)
- promise.all():接收多个promise实例,都成功会触发then,有一个失败就会触发catch
- promise.any():接收多个promise实例,可以得到最先处理成功的结果,都失败会触发catch
12.事件循环Event Loop执行机制
-
1.进入到script标签,就进入到了第一次事件循环.
-
2.遇到同步代码,立即执行
-
3.遇到宏任务,放入到宏任务队列里.
-
4.遇到微任务,放入到微任务队列里.
-
5.执行完所有同步代码
-
6.执行微任务代码
-
7.微任务代码执行完毕,本次队列清空
-
寻找下一个宏任务,重复步骤1
-
以此反复直到清空所以宏任务,这种不断重复的执行机制,就叫做事件循环
-
13.Vue的最大优势是什么?
-
双向数据绑定,
-
数据驱动视图,
-
组件化开发
-
数据和视图分离
-
单页面应用可以实现页面数据局部刷新
14.Vue常用修饰符有哪些?
.prevent: 提交事件不再重载页面;
.stop: 阻止单击事件冒泡;
.once: 只执行一次这个事件
.enter:监听键盘enter键
15.说出至少4个Vue指令及作用
-
v-on 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面
-
v-bind 动态绑定 作用: 及时对页面的数据进行更改, 可以简写成:分号
-
v-slot: 缩写为#, 组件插槽
-
v-for 根据数组的个数, 循环数组元素的同时还生成所在的标签
-
v-show 显示内容
-
v-if 显示与隐藏
-
v-else 必须和v-if连用 不能单独使用 否则报错
-
v-text 解析文本
-
v-html 解析html标签
16.v-show与v-if区别
我是这么理解v-if和v-show的。 v-if本质其实是动态的创建 或者 删除元素节点。一般不用频繁切换,要么显示,要么隐藏的情况,我都会用 v-if。因为v-if 是情性的,如果初始值为false,那么这些元素就直接不创建了,这样就可以节省一些初始渲染开销。v-show本质是在控制元素的 css 样式,display: none;,一般元素需要频繁的切换显示隐藏,用 v-show。因为v-if在频繁切换会大量的创建和删除元素,消耗性能。
17.为什么组件中的data是一个函数
因为组件需要复用 , 如果组件一个对象,那么多个页面会公用一个地址。就会导致一个地方发生改变,其他页面也会跟着改变。如果组件是一个函数,每一次复用就会调用这个函数,生成一个新的地址 , 就可以做到每一个组件之间的数据互不影响
18.Vue中:key作用, 为什么不能用索引
最高逼格答案: 因为用了索引和没用, 没啥区别(用了等于没用)
-
:key是给v-for循环生成标签颁发唯一标识的, 用于性能的优化
-
因为v-for数据项的顺序改变,Vue 也不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素
19.Vue中有时候数组会更新页面,有时候不更新,这是为什么
因为vue内部只能监测到数组顺序/位置的改变/数量的改变, 但是值被重新赋予监测不到变更, 可以用 Vue.set() / vm.$set()
这些方法会触发数组改变, v-for会监测到并更新页面
-
push()
-
pop() 删除原数组的最后一项,并返回删除的值
-
shift()
-
unshift()
-
splice() 删除
-
sort() 排序(两参数比大小)
-
reverse()
这些方法不会触发v-for更新
-
slice() 添加分隔符
-
filter()
-
concat()
20.
方法和计算属性和侦听器区别
-
方法
-
需要主动调用触发
-
不会缓存
-
-
计算属性
-
监听多个属性:只要计算属性内部数据变化就会触发
-
有缓存机制(必须要说的)
-
-
侦听器
-
监听一个属性
-
不会缓存(这个可以不用回答)
-
21.vuex的触发流程是什么
点击按钮的时候,通过dispatch触发actions,actions中发请求,请求的结果到达之后,通过commit触发mutations,并且对mutaions的每一次触发都可以通过devtools来观测到。在mutations中修改state之后,由于state中的数据是响应式的,所以凡是用到state数据的组件都会自动更新。如果不涉及到异步操作,也可以直接在视图中直接commit触发mutaion来修改state
22.组件之间传参方式
1.父传子:先在父组件上通过自定义属性传递变量,再在子组件上通过props接收,在接收时有两种接收方式(数组形式,对象形式),可以规定传来的数据类型
2.子传父:先在子组件中定义一个事件,再通过this.$emit去调用这个函数,$emitf方法有两个参数,第一个参数是定义的函数名,第二个参数就是子组件要传的变量,最后在父组件通过v-on接收并重新定义函数名
3.子访问父相互传使用 $refs
4.父子相互访问 $parent / $root / $children
5.非父子组件(兄弟)组件之间的通信,EventBus,事件总线.祖先通讯:provide
(提供) 和 inject
(获取)可以实现祖先与后代( 跨级之间通讯 )组件之间的通信
6.作用域插槽
7.vuex
23.Vue数据双向绑定的原理是什么?
Vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过
Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发 Compile中绑定的回调,则功成身退。
第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化->视图更新;视图交互变化(input)->数据model变更的双向绑定效果。
24.口述全局注册组件
1.在components文件夹中创建一个index.js文件
2.在main.js中导入这个文件
3.Vue.ues(params)方法中 params一般是以对象作为参数 ,对象中有一个方法 install
4.install方法会自动执行 方法中的是形参 vue
5.用export default暴露出去 install方法会自动执行 方法中的是形参 vue
6.通过Vue.sue(component( 组件名))然后以使用了
25.vue 项目优化解决方案都有哪些
1. 使用 mini-css-extract-plugin 插件抽离 css
2. 配置 optimization 把公共的 js 代码抽离出来
3. 通过 webpack 处理文件压缩
4. 不打包框架、库文件,通过 cdn 的方式引入
5. 小图片使用 base64
6. 配置项目文件懒加载
7. UI 库配置按需加载
8. 开启 Gzip 压缩
26.如何实现自定义指令?它有哪些钩子函数?还有哪些钩子函数参数?
自定义指令包括以下两种。
·全局自定义指令:vue.js对象提供了 directive方法,可以用来自定义指令。directive方法接受两个参数,一个是指令名称,另一个是函数。
。 局部自定义指令:通过组件的 directives属性定义。
它有如下钩子函数。
。bind:在指令第一次绑定到元素时调用。
。inserted:在被绑定元素插入父节点时调用。
。 update:在所在组件的 VNode更新时调用。
。componentUpdated:在指令所在组件的 VNode及其子 VNode全部更新后调用。
。unbind:只调用一次,在指令与元素解除绑定时调用。
钩子函数的参数如下。
。el:指令所绑定的元素。
。binding:指令对象。
27. vue生命周期钩子 : vue从初始化到销毁过程中会执行函数
1.请简述vue有哪些生命周期钩子
vue生命周期共分为四个阶段8个物子初始化阶段: beforecreate 、created
挂载阶股 : beforeMount 、mounted
更新阶段; beforeUpdate 、 updated
销毁阶段: beforeDestroydestroyed
2.请回vue第一次渲染会执行哪些钩子
beforecreate
created
beforeMount
mounted
3.请问vue生命钩子钩子哪些会执行多次beforeUpdateupdated
4.请问生命周期钩子有哪些是常用的
created : 最早可以操作data,一般发送ajax
mounted: 最早可以操作dom,一般操作dommounted
28、说一下 localstorage/sessionStorage/cookie 之间的差异?
- localstorage会永久储存,只能手动删除,不会自动把数据发给服务器,仅在本地存储
- sessionStorage仅会在当前会话下有效,关闭页面或浏览器就会被清除,不会参与和服务器的通信
- cookie 只在设置cookie过期时间之前一直有效,即使窗口或浏览器关闭。有个数限制,一般不能超过20个数据不能超过4k,如果使用cookie保存过多数据会带来性能问题
29.常见http的状态码
1.HTTP协议是:约定浏览器与 服务器 数据格式2.HTTP协议组成部分:请求报文 和 响应报文3..请求报文: 浏览器发送数据格式
(1)请求行:请求方法 和 请求路径
(2)请求头:浏览器发送的数据格式
(3)请求体:请求参数4.响应报文:服务器响应的格式
(1)响应行 :响应状志码 ,服务器IP
2xx :请求成功
200:请求成功
3xx:重定向(服务器主动 修改 浏览器地址)
301:被请求的资源已永久移动到新位置
302 重定向
4xx:前段出错
400:参数错误
401:未验证(Unauthorized 用户没有登录)
403:没有权限(用户没有权限访问)
404:请求路径错误
405:请求方法错误
413:文件大小超出限制
5xx:服务器出错
500: 服务器内部错误
502:服务器维护
(2)响应头 :服务器响应的数据格式
(3)响应体:响应数据
30.vue-router的钩子函数都有哪些(导航守卫)
-
关于vue-router中的钩子函数主要分为3类
-
1.全局钩子函数beforeEach(全局前置守卫,所有路由生效)
-
beforeEach函数有三个参数,分别是:
-
to:router即将进入的路由对象
-
from:当前导航即将离开的路由
-
next:function,进行管道中的一个钩子,如果执行完了,则导航的状态就是 confirmed (确认的)否则为false,终止导航。
-
-
-
2.单独路由独享组件(只对这个路由生效)
* beforeEnter,
-
3 组件内钩子
-
beforeRouterEnter,(渲染路由组件前)
-
beforeRouterUpdate,(路由改变)
-
beforeRouterLeave(路由离开)
-
31mutation和action的使用区别
action的功能和mutation是类似的,都是去变更store里的state,不过action和mutation有两点不同:
1、action主要处理的是异步的操作,mutation必须同步执行,而action就不受这样的限制,也就是说action中我们既可以处理同步,也可以处理异步的操作
2、action改变状态,最后是通过提交mutation
32.请说出路由配置项常用的属性及作用
-
路由配置参数:
-
path : 跳转路径
-
component : 路径相对于的组件
-
name:命名路由
-
children:子路由的配置参数(路由嵌套)
-
props:路由解耦
-
redirect : 重定向路由
-
33.小程序登录你是怎么做的?
1. 前端通过调用 wx.login()
拿到 code。
2. 前端通过调用 wx.request()
发送 code 到后端。
3. 后端调用 code2Session 方法并传递 appid(小程序的id)、appsecret(小程序秘钥)、code 等参数到微信的后台。
4. 微信的后台返回 session_key(当前我们公司的后端和微信后端通信时的秘钥) 和 openid(用户的唯一标识)到我们的后台。
5. 我们的后端生成 token(并把 token 和 openid 进行关联,说白了通过 token 就能知道是哪个用户),并把 token 返回到小程序端。
6. 前端存储 token 到本地,后续的所有请求都带上这个 token 到后端。
34.小程序支付你是怎么做的?
-
创建订单。
○ 请求创建订单的 API 接口(后端提供的):把(订单金额、收货地址、订单中包含的商品信息)发送到服务器。
○ 服务器响应的结果:订单编号。
-
订单预支付。
○ 请求订单预支付的 API 接口(后端提供的):把(订单编号)发送到服务器。
○ 服务器响应的结果:发起微信支付时候的参数。
-
发起微信支付:调用
uni.requestPayment()
这个 API,并传递订单预支付对象,发起微信支付。 -
微信支付成功后,跳转到支付结果页,再次传递订单编号调用后端接口查询最终支付的状态并展示。
35.说一下小程序中的生命周期?
分为应用级别的和页面级别的。
应用级别的:onLaunch(小程序启动,触发一次)、onShow(从后台进到前台)、onHide(从前台到后台)。
页面级别的:onLoad(监听加载,一般在里面获取其他页面传递过来的参数)、onShow(页面开始展示)、onReady(页面渲染完毕)
组件级别的(lifetimes):created(组件实例创建)、attached(挂载完成)、ready(渲染完成)
组件所在页面的(pageLifetimes):show(组件所在的页面展示的时候)、hide、resize(组件所在的页面的尺寸发生变化的时候触发)
36.登录的流程?
前端要收集数据 => 校验数据 => 校验通过后把数据提交到后端 => 后端根据接收到的用户信息生成 token 并返回到前端 => 前端拿到 token 之后存储到 Vuex(目的:是为了方便和响应式)和本地(目的:持久化)。
如何把本地的数据和 Vuex 保持同步?需要在初始化 Vuex 数据的时候从本地去一下就行了。
37.开发流程?
1. 产品经理画出原型图,召集前端、后端、测试等人员开会讨论需求的合理性。
2. 前端组长会根据原型图进行任务拆分。
3. 我们的项目一般都是有组长初始化好并上传到内部的 Gitlab 平台。
4. 然后我们把 develop 分支拉过来。
5. 基于此分支再创建自己的功能分支,在自己功能分支进行开发,完毕之后合并 develop 分支。
38.说一下权限的处理流程?
用户登录成功之后,后端会返回当前用户的标识,例如:
['/news', '/sports', '/channel', ...]
前端拿到这个标识之后,去筛选出有权限的路由。
[{ path: '/news', component: News }, { path: '/sports', component: Sports }, { path: '/channel', component: Channel }, ]
然后接下来做了两件事情!
第一件事情把筛选出来的有权限的路由(动态路由)通过 addRoutes / addRoute 添加到了路由实例(router),一旦添加到了路由实例,当前用户就有具有了访问某个页面的权限。
第二个事情把筛选出来的动态路由也添加到了 Vuex 一份,只要目的是为了给侧边栏使用。
按钮级别的权限只需要封装一个全局的指定或方法,这个方法只做一件事情,接收一个标识,内部判断一下这个标识在不在后端返回的功能数组里面,在的话就返回 true,不在就返回 false,接下来只需要在做按钮控制的地方,调用一下这个方法,传过去当前功能标识,根据这个方法返回的是 true 还是 false,对这个按钮做一个禁用或启用,显示或隐藏的操作。
function fn(tag) {
return [后端返回的, 'DEL_USER', 'b', 'c'].includes(tag)
}
<button v-if="fn('DEL_USER')">删除用户信息</button>
39.用户没有登录该如何不让他访问内页?
围绕着两方面去说:在哪做的?用了什么事情?
我会在全局路由前置导航守卫(beforeEach)内部做一些处理,如果有 token 就直接放行,如果没有 token,就看一下访问的页面(to.path)在不在白名单,如果在也执行 next 放行,否则拦截到登录页。
40.图片预览怎么做的?
第一种
<input type="file" hidden id="oInput" />
<img id="oImg" alt="">
<button id="oBtn">上传</button>
<script>
oBtn.onclick = function() {
oInput.click()
}
oInput.onchange = function(e) {
const f = e.target.files[0]
// blob url => 临时的图片地址
const blobUrl = URL.createObjectURL(f)
oImg.src = blobUrl
}
</script>
第二种
<input type="file" hidden id="oInput" />
<img id="oImg" alt="">
<button id="oBtn">上传</button>
<script>
oBtn.onclick = function() {
oInput.click()
}
oInput.onchange = function(e) {
const f = e.target.files[0]
const reader = new FileReader()
// 把文件信息 f 读取为 base64 格式的
reader.readAsDataURL(f)
reader.onload = function() {
oImg.src = this.result
oImg.width = 600;
}
}
</script>
1 准备一个 type file 框并隐藏,当type=file图片就会实现上传功能
2.准备一个上传的按钮
3.点击上传按钮的时候触发input file框的click事件
4.监听inputfile 框的change事件,通过e.target.files拿到文件信息
5.根据文件信息通过 URL.createObjectURL 生成 blobUrl
41. 介绍一个你封装的组件?用到了哪些技术点?
每一个 .vue 文件就可以说是一个大的组件,一般称为业务组件。
第三方的 UI 组件,Element UI、Vant。
每一个路由页面的面包屑组件、每一个路由页面的公共标题、编辑和新增功能的组件封装、上传组件...
传值和检验、插槽和作用域插槽、自定义事件。
作用域插槽:可以在父组件拿到子组件的数据,在父组件加工处理之后再交给子组件去使用。
42.Axios 封装?
怎么用:utils/request.ts
封装了具体的代码、api/xxx
具体模块请求函数的封装、views/xxx
视图去调用 api 中的请求方法。
怎么封:首先创建了 axios 实例,封装了 baseUrl、timeout 超时时间、transformResponse、请求拦截器、响应拦截器。
请求拦截器干了啥:统一携带 token、请求前开启进度条(nprogress)
响应拦截器干了啥:数据解包、关闭进度条(成功);统一错误处理、无感刷新(token 过期根据 refresh_token 再换取新 token,然后悄悄的把失败的请求再发出去)...
43.后端是怎么给你提供接口文档的?
我们公司用的是 SwaggerUI,后端用的根据写好的接口生成接口文档的框架。
44.call apply bind的使用
同
- 第一个参数都是用来改变函数内部this指向的
- 都可以利用后续传参
异
- 传参方式不同 : call是单个传参,apply是数组、伪数组传参
- 执行机制不同 :
call和aplly会立即执行
,bind不会立即执行
而是得到一个修改this的新函数
45.什么是伪数组,伪数组怎么转真数组
- 具有
length
属性 - 安索引方式存储数据
- 不具有数组的方法
push
pop
等方法 - 转真数组:
Array.from()
,[].slice.call
,Array.prototype.slice.call,...运算符
46.https和http的区别
1、https的端口是443,而http的端口是80,且两者的连接方式不同
2、http传输是明文的,而https是用ssl进行加密的,https的安全性更高;
3、https是需要申请证书的,而http不需要
47.哈希路由和history路由的区别以及原理
1.hash路由在地址栏URL上有#,而history路由没有,会好看一些
2.我们进行回车刷新操作,hash路由会加载到地址栏对应的页面,而history路由一般会404报错(刷新是网络请求,没有后端准备时就会报错)
3.hash路由支持低版本的浏览器,而history是HTML5新增的API
4.hash的特点在于它虽然出现在了URL中,但是不包括在http请求中,对于后端没有影响,所以hash不会重新加载页面,这也是单页面应用的必备
5.history运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了pushState()和replaceState()f方法(需要特定的浏览器支持),提供了对历史记录修改的功能,不过在进行修改时,虽然改变了当前的URL,但是浏览器不会马上向后端发送请求
48.你知道和 Vue 相关的性能优化有哪些?
1、代码模块化,咱们可以把很多常用的地方封装成单独的组件,在需要用到的地方引用,而不是写过多重复的代码,每一个组件都要明确含义,复用性越高越好,可配置型越强越好,包括咱们的css也可以通过less和sass的自定义css变量来减少重复代码。 2、for循环设置key值,在用v-for进行数据遍历渲染的时候,为每一项都设置唯一的key值,为了让Vue内部核心代码能更快地找到该条数据,当旧值和新值去对比的时候,可以更快的定位到diff。 3、Vue路由设置成懒加载,当首屏渲染的时候,能够加快渲染速度。 4、更加理解Vue的生命周期,不要造成内部泄漏,使用过后的全局变量在组件销毁后重新置为null。 5、可以使用keep-alive,keep-alive是Vue提供的一个比较抽象的组件,用来对组件进行缓存,从而节省性能。 6、修改vue.config.js中的配置项,把productionSourceMap设置为false,不然最终打包过后会生成一些map文件,如果不关掉,生成环境是可以通过map去查看源码的,并且可以开启gzip压缩,使打包过后体积变小。 7、按需引入,咱们使用的一些第三方库可以通过按需引入的方式加载。避免引入不需要使用的部分,无端增加项目体积。比如在使用element-ui库的时候,可以只引入需要用到的组件。 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等.
49.父子组件嵌套的生命周期钩子
- 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
- 父beforeUpdate->子beforeUpdate->子updated->父updated
- 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
50.v-model实现原理
1.v-model实现原理
:的实现原理其实很简单,可以 为在input框里面通过v-bind动态绑定一个value,然后在input框里面通过@input方法去动态获取input输入的值,然后重新给变量赋值就可以
51.type of和instance of相关及其拓展
typeof 会返回一个变量的基本类型, instanceof 返回的是一个布尔值
instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
而 typeof 也存在弊端,它虽然可以判断基础数据类型( null 除外),但是引用数据类型中,除了 function 类型以外,其他的也无法判断
可以看到,上述两种方法都有弊端,并不能满足所有场景的需求
如果需要通用检测数据类型,可以采用Object.prototype.toString,调用该方法,统一返回格式“[object Xxx]”的字符串.
52.什么是diff算法?
diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。
简单来说Diff算法就是在虚拟DOM树从上至下进行同层比对,如果上层已经不同了,那么下面的DOM全部重新渲染。这样的好处是算法简单,减少比对次数,加快算法完成速度。
有两个特点
比较只会在同层级进行, 不会跨层级比较
在diff比较的过程中,循环从两边向中间比较
diff算法的步骤
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文 档当中 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异 把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了
53.$route和$router的区别?
routes : 数组。 路由匹配规则
router : 对象。 路由对象
$router : 对象。 用于跳转路由 和 传递参数
$route :对象。 用于接收路由跳转参数
54.异步组件和动态组件
异步组件:
-
vue的一种性能优化的方法,可以实现组件的
按需加载
-
组件通过
import函数
引用,什么时候需要什么时候加载 -
有利于项目的性能优化,
提高
页面的加载速度
-
路由懒加载
就是使用了异步组件的原理
动态组件
- 让
多个组件
同使用一个挂载点
,并且组件间可以动态切换
,这个挂载点就是component 标签
- 简单来说是在 component 标签上添加一个
is属性
,属性值
(即currentTabComponent)是控制组件间的切换
的 - 可配合
keep-alive
使用,这样切换的时候就可以不用频繁渲染
1.keep-alive是vue的内置组件,可以包含动态组件,当组件之间进行切换时,可以保持组件的状态,在内存中缓存不活动组件的实
例,而不是销毁它们)
2.并且自身也不会渲染
成一个DOM元素,不会显示在父组件链中.
55.MVVM的原理?
MVVM既Model-view-ViewMOdel的简写,是一种软件架构设计模式。
Model(数据模型)就是负责存储页面的业务数据,以及相应数据的操作逻辑。(泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开)
View(视图)负责页面的显示逻辑。(前端主要由 HTML 和 CSS 来构建)
ViewModel(视图模型)是MVVM模式的核心部分,负责连接view 和 model,实现两者的同步更新。
首先 ViewModel 通过接口连接 Model 层,实现了数据交互。将获取的数据进行转换处理,生成符合 View 层使用的 ViewModel (视图数据模型)
然后 ViewModel 通过双向绑定连接了View层,所以 ViewModel 内容的改变会实时展现在 View 层,而用户通过 view 层的交互改变视图,可以更新 ViewModel 层的数据。
// ViewModel还有两个主要部分组成
// 监听器(Observer):对所有数据的属性进行监听
// 解析器(Compiler):对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数
通过ViewModel可以实现 View 和 Model 的解耦,而且前端开发不必再通过操纵 DOM 去更新视图,只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新,真正实现数据驱动开发。
56.小程序打包上线的过程中遇到什么问题没?
如果小程序中涉及到一些request的请求接口,记得把不校验合法域名勾选上。另外注意,小程序的接口不允许http,只支持https
我们可以将上传的版本设置为体验版本,这样更方便地测试我们的小程序的功能
分包?
1.在项目根目录中,创建分包的根目录,命名为 subpkg。
2.在 pages.json 中,和 pages 字段平级的位置声明 subPackages 数据节点,用来定义分包相关的结构。
3.在 subpkg 目录上鼠标右键,点击【新建页面】选项,并填写页面的相关信息。
4.创建成功后开发工具会自动在 pages.json 下的 subPackages.pages 中生成对应分包的配置。
57.路由之间是怎么跳转的?有哪些方式
-
1、<router-link to="需要跳转到页面的路径">
-
2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面
-
3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面
-
4、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数
58.nextTick的原理
1.nextTick是等待下一次DOM更新刷新的工具方法。(其实一句话就可以把$nextTick这个东西讲明白:就是你放在 $ nextTick 当中的操作不会立即执行,而是等数据更新、DOM更新完成之后再执行,这样我们拿到的肯定就是最新的了。
再准确一点来讲就是 $nextTick方法将回调延迟到下次DOM更新循环之后执行。
2.Vue有个异步更新策略,意思是如果数据变化,Vue不会立刻更新DOM,而是开启一个队列,把组件更新函数保存在队列中,在同一事件循环中发生的所有数据变更会异步的批量更新。这一策略导致我们对数据的修改不会立刻体现在DOM上,此时如果想要获取更新后的DOM状态,就需要使用nextTick。
3.开发时,有两个场景我们会用到nextTick
①created中想要获取DOM时;
②响应式数据变化后获取DOM更新后的状态,比如希望获取列表更新后的高度。
58.跟keep-alive有关的生命周期是哪些?
-
1.在开发Vue项目的时候,大部分组件是没必要多次渲染的,所以Vue提供了一个内置组件keep-alive来缓存组件内部状态,避免重新渲染.
-
2.生命周期函数:在被keep-alive包含的组件/路由中,会多出两个生命周期的钩子:activated 与 deactivated。
- keep-alive的参数:include只有名称匹配的组件会被缓存。 exclude任何名称匹配的组件都不会被缓存。
-
activated钩子:
在在组件第一次渲染时会被调用
,之后在每次缓存组件被激活时调用。 -
Activated钩子调用时机:
第一次进入缓存路由/组件
,在mounted后面,beforeRouteEnter守卫传给 next 的回调函数之前调用,并且给因为组件被缓存了,再次进入缓存路由、组件时,不会触发这些钩子函数,beforeCreate created beforeMount mounted 都不会触发 -
deactivated钩子:
组件被停用(离开路由)时调用
-
deactivated钩子调用时机:使用keep-alive就不会调用beforeDestroy(组件销毁前钩子)和destroyed(组件销毁),因为组件没被销毁,被缓存起来了,这个钩子可以看作beforeDestroy的替代,如果你缓存了组件,要在组件销毁的的时候做一些事情,可以放在这个钩子里,组件内的离开当前路由钩子beforeRouteLeave => 路由前置守卫 beforeEach =>全局后置钩子afterEach => deactivated 离开缓存组件 => activated 进入缓存组件(如果你进入的也是缓存路由)
-
-
60.虚拟DOM和真实DOM的区别?为什么在vue中会用到虚拟dom?
虚拟dom(VNode),假的,不是真实的dom
真实的DOM在浏览器通过dom.api操作的,复杂的对象
虚拟DOM:可以通过this.$slots.default查看
真实的dom是一个对象,它的属性非常多,在浏览器中做dom操作,会比较消耗性能
虚拟dom是一个对象,它的属性相比较于真实的dom就比较少---用少量的属性描述一个dom,无法在浏览器中直接显示
————————————————
有两点好处:
1.虚拟dom比真实dom体积小,操作是相对来说消耗性能少,如果在页面中删除一个dom,会引起重绘,影响后边元素的布局
1):虚拟Dom不会进行回流和重绘操作
2):虚拟dom进行频繁的修改,然后一次性比较并修改真实DOM中需要改的部分,最后并在真实DOM中进行回流和重绘,减少过多DOM节点的回流和重绘
3)真实Dom频繁的回流和重绘效率非常低
61.移动端开发碰到过哪些问题?
62.前端安全了解吗,说一下 XSS 和 CSRF,以及怎么规避?
XSS(Cross Site Scripting):跨站脚本攻击,是利用网页的漏洞,通过某种方式给网页注入恶意代码,使用户加载网页时执行注入的恶意代码。
CSRF
或者 XSRF:
跨站请求伪造(Cross-site request forgery),是一种挟制用户在当前已登录的 Web
应用程序上执行**非本意的操作**的攻击方法。
xss规避方法: 设置HttpOnly (在 cookie
中设置 HttpOnly
属性后, js
脚本将无法读取到 cookie
信息。)
// 转义字符串
//白名单
csrf规避方法:验证码
//token或者是数据请求来源
63.移动端 1px 问题你是怎么处理?
1.background-image,多背景渐变
2.CSS3,box-shadow
3.:before,:after,transform(建议使用)
64.HTTP 304?
304表示缓存(强缓存和协商缓存)
强缓存(max-age="设置缓存的时间")。意思就是只要时间没超时,就会在请求的时使用缓存的文件,不会重新请求资源。
协商缓存:可以通过设置请求头中的 no-cache 和 no-store 字段选择使用协商缓存或者不使用缓存
区别:
- 强制缓存发生在浏览器端, 协商缓存发生在服务器端
- 强制缓存在浏览器强制刷新的情况下不会生效, 而协商缓存则不受影响。(调试代码测试时候,要注意)
65.前端如何更好的渲染十万条数据?
数据懒加载 (获取视图的方法:1.getBoundingClientRect方法 2.IntersectionObserver)
虚拟列表
时间分片:通过递归来渲染DOM,刚开始可以是20个,20个渲染完后再渲染剩下的,循环如此,将其全部渲染完。又因为浏览器的渲染机制是“宏任务—微任务—GUI渲染—宏任务...”。遂第一个 loop 执行后,先等页面渲染完,再执行下一轮的 setTimeout(宏任务)
使用 setTimeout 来做分片会有问题,就是当我们快递下拉时,会出现闪屏或白屏现象?调用这个requestAnimationFrame方法解决.
总结:最优选是虚拟列表,DOM 树上只挂载有限的DOM;懒加载和时间分片的缺点在于插入大量的DOM,占内存运行时会造成卡顿
66.只会引起重绘不会引起重排?
透明度,阴影
67.让你独立负责一个项目你会考虑哪些东西?
技术栈、语法规范、项目类型(选型,背景,愿景,价值,周期安排)、面向的群体、更新迭代的方向。
68.vue3的生命周期钩子
vue生命周期共分为四个阶段8个物子初始化阶段: beforecreate 、created
挂载阶股 : beforeMount 、mounted
更新阶段; beforeUpdate 、 updated
销毁阶段: onBeforeUnmount、onUnmounted
69.生成PDF的几种方式
1、后台生成PDF(thinkphp利用MPDF插件)
2.利用命令行将网页链接生成PDF(下载wkhtmltox)接网页链接
3.利用js生成PDF
引入以下两个js插件
html2canvas.js
jspdf.debug.js
70.iframe 标签,怎么通信?
1.iframe
是html
元素,用于在网页中内嵌另一个网页。
2.iframe
默认有一个宽高,存在边界
3.iframe
是一个行内快级元素,可以通过display
修改
1.发送信息:
当我们要向指定iframe发送信息时,首先要获取该iframe自己的window对象,然后使用该window对象的postMessage发送消息。
otherWindow.postMessage(data, orgin,[transfer]);
data是待发送的数据
orgin是发送的地址,为‘*’表示无限制,该参数必传,否则会报错
transfer是
2.接受信息:
在要接受信息的地方,我们使用window的onmessage事件来接受消息,该事件会返回一个事件对象,其中data包含了返回的数据,orgin返回发送源。
3.安全问题:当我们明确知道orgin是谁时,不要使用‘*’,当要接受信息时,先判断orgin是否是我们要接受的源,在做后续操作。
71.Mixin的优缺点?
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
生命周期不会覆盖:如果配置了相同的生命周期函数,先执行混入的,再执行组件自身的。
data 和 methods:相同的会覆盖,组件内的会覆盖 mixin 中的。
优点:
- 提高代码复用性
- 无需传递状态
- 维护方便,只需要修改一个地方即可
缺点:
- 命名冲突
- 滥用的话后期很难维护
- 不好追溯源,排查问题稍显麻烦
- 不能轻易的重复代码
72.声明式导航和编程式导航区别
声明式导航:在浏览器中,点击链接实现导航的方式,叫做声明式导航。如:普通网页中点击<a>链接,vue中点击<router--link>都属于声明式导航。
编程式导航:在浏览器中,调用API方法实现导航的方式,叫做编程式导航。如:普通网页中调用location.href跳转到新页面的方式,属于编程式导航。
vue-router中的编程式导航
1.this.$router.push("hash地址")
2.this.$router.replace("hash地址")
3.this.$router.go(数值n)
72.冒泡排序的原理
冒泡排序的原理
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
举例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 定义一个数组
let array = [12,34,35,8,10,20,21,52]
function sum(array) {
/* 双重for循环 */
for (let i = 0; i < array.length-1; i++) {
// 外层循环
for (let j = 0; j < array.length-1+i; j++) {
// 内层循环
if (array[j]>array[j+1]) {
// 比较大小
let ts= array[j+1]
// 交换位置
array[j+1]= array[j]
array[j]=ts
}
}
}
return array
}
sum(array)
console.log(array);
</script>
</body>
</html>
73.箭头函数与普通函数有什么区别?
1箭头函数声明的都是匿名函数.
2.箭头函数不能用于构造函数,不能使用new.
3.箭头函数中this的指向不同,不能修改this指向
4.箭头函数没有原型
74.JS-获取DOM元素的五种方法
1.根据id名获取元素:document.getElementById;
2.根据标签名获取元素:document.getElementsByTagName,返回一个数组;
3.根据类名获取元素:document.getElementsByClassName,返回一个数组;
4.根据name属性值获取元素:document.getElementsByName,返回一个数组;
5.根据选择器获取元素:
document.querySelector:获取一个元素,推荐使用 用法类似写css;
document.querySelectorAll:返回一个元素数组。
获取样式
document.getComputedStyle
总结:面试题后续会持续更新,如有不对,欢迎探讨!