前端面试

140 阅读33分钟

CSS

CSS盒模型

在HTML页面里,所有的元素都可以看成是一个盒子
盒子的组成:内容content、内边距padding、边框border、外边界margin
类型:标准盒模型 margin + border + padding + content IE盒模型 margin + content
控制盒模型的模型:box-sizing:content-box(默认值) box-sizing:border-box(IE盒模型)

CSS选择器的优先级

CSS选择器优先级:
!important > 行内样式 > id选择器 > 类选择器 > 标签选择器 > 全局选择器

隐藏元素的方法有哪些

消失 display:none
透明度为0 opacity:0
不可见 visibility:hidden
绝对定位隐藏 position:absolute

px和rem区别

px是像素,显示器上给我们呈现的画面像素,每个像素大小是一样的,绝对单位长度
rem相对单位,相对于html根元素字体大小,如果没有设置根元素字体大小,则相对于默认大小16px 。可以理解为是html字体大小的多少倍
em:相对于父元素的字体大小。如当前父元素的字体尺寸未设置,则相对于浏览器的默认字体尺寸。可以理解为是父元素字体大小的多少倍
vw:vw是相对视口(viewport)的宽度而定的,长度等于视口宽度的1/100
vh: vh是相对视口(viewport)的高度而定的,长度等于视口高度的1/100

重绘与重排

重排:布局根据样式计算出盒子模型在页面上位置和大小
重绘:计算好盒子模型的位置、大小后,根据盒子特性进行绘制
对DOM大小、位置修改后,重排,对样式如颜色、背景的修改,重绘

让一个元素水平、垂直居中

1、定位+margin 子绝父相,top什么的都是0,margin:auto
2、定位+transform 子绝父相,top 50% left 50% transform:translate(-50%,-50%)
3、flex布局 justify-content:center 水平居中 align-item:center 垂直居中
4、grid布局
5、table布局

CSS哪些属性可以继承

1、字体属性
2、文本属性
3、元素可见性
4、表格布局的属性
5、列表的属性

清除浮动的方法有哪些

浮动溢出:容器不设高度且子元素浮动时,容器高度不能被内容撑开。此时,内容会溢出到容器外面影响布局。这种现象被称为浮动(溢出)
image.png 额外标签法:浮动元素之后添加一个空div并设置clear:both
使用overflow触发BFC:浮动元素的父容器上设置overflow:hidden
伪元素清除法:利用after伪元素为浮动的父容器添加看不到的内容,然后设置clear:both,display:block;content:''

预处理器

预处理语言增加了变量、函数、混入等强大的功能
SASS LESS <style lang="less" scoped>

如何实现自适应布局

无限适配的核心原理: 把屏幕划分为一定的份数,然后通过js动态的监测屏幕尺寸、宽度实时计算并设置HTML元素的基础字体大小

scoped

样式只在当前页面生效,给每个class 加一个id,必添加

vue怎么做样式穿透

有时外部插件,样式不能生效,因为加了scoped所有样式不能给到组件
解决方法: 父元素 /deep/ 子元素
或者直接在全局样式里来修改 插件的样式
外层容器 ::v-deep 组件 { }

JS

JS由哪三部分组成

ECMAScript:JS的核心内容,描述了语言的基础语法
文档对象模型 DOM
浏览器对象模型 BOM

JS有哪些内置对象

String Boolean Number Array Object Function Math Data RegExp
Math.abs(num) Math.ceil(num) Math.floor(num) Math.max(...[14, 3, 77, 30]);
基本类型在栈内存里 复杂数据类型存放在堆内存

操作数组的方法

  • push 最后插入 改变原数
  • pop 最后删除 改变原数
  • sort排序 改变原数
  • shift最前删除 改变原数
  • unshift最后插入 改变原数
  • reverse 反转 改变原数
  • concat 合并
  • join 转字符串
  • slice用来截取,返回新数组
  • splice 修改数组(index规定添加/删除项目的位置,删除数,添加的内容 )
  • map 对数组中的每个元素进行处理,将其转换为另一个值,最终返回一个新的数组
  • filter 过滤
  • find 返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
  • every 判断是否全部满足
  • some 检测数组中是否至少有一个元素满足指定的条件
  • isArray 是否数组
  • findIndex 返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1
  • IndexOf 返回数组中存在的索引,比findIndex用法少一点,只能单纯的两两对比,不能做复杂的对比,比如忽略大小写,忽略数据类型等

数组去重

1、Array.from(new set(arr))
[...new Set(arr1)]
2、循环利用indexOf找是否存在了,不存在push到新数组

JS对数据类的检测方式有哪些

typeof() 基本数据类型
instanceof() 引用数据类型
constructor 都可以
Object.prototype.toString.call( xxx ) 完美

闭包

函数嵌套函数,内部函数被外部函数返回并保存下来,就会产生闭包
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制,一直保存在内存中
使用场景:防抖节流,函数嵌套函数避免全局污染
image.png

前端内存泄漏

JS已经分配内存地址的对象,但长期没有释放或没清除,造成内存被长期占用,导致运行缓慢甚至崩溃
因素:未声明直接赋值的变量;未清空的定时器;过度的闭包;一些引用的元素没有被清除

事件委托

以冒泡事件为机制实现,就是把子元素的事件绑定到了父元素身上,如果阻止事件冒泡则不成立
阻止事件冒泡:event.stopPropagation()

基本数据类型和引用数据类型的区别

基本数据类型保存在栈内存中,保存的是一个具体的值
引用数据类型保存在堆内存中,保存的是地址,有两个引用类型同时指向一个地址,修改其中一个另一个也会改变

原型链

image.png 在js中,每一个函数类型的数据,都有一个叫做prototype的属性,这个属性指向的是一个对象,就是所谓的原型对象。原型对象是用来存放实例对象的公有属性和公有方法的一个公共对象,所有原型对象都是Object()的实例
如果某个对象查找属性,自己和原型对象上都没有,那就会继续往原型对象的原型对象上去,找形成了像链条一样的结构,这个结构,就是原型链
作用:我们在创建对象、数组、函数等等数据的时候,都自带一些属性和方法,这些属性和方法是在它们的原型上面保存着,所以它们自创建起就可以直接使用那些属性和方法。
感觉很像java里所有对象都继承与Object类,子类继承父类的方法,也集成Object的方法

new 操作符做了什么

1.先创建空对象
2.把空对象和构造函数通过原型链进行连接
3.把构造函数的this绑定到新的空对象身上

js如何实现继承

ES6的class类继承,借用extends关键字

this的指向问题

  • 全局对象中this指向window
  • 全局作用域或普通函数中this指向全局window
  • this永远指向最后调用它的那个对象
  • new关键字改变了this的指向
  • apply,bind,call改变了this的指向(非箭头函数)
  • 箭头函数相当于继承了外部函数的this
  • 匿名函数,永远指向window

script里async和defer的区别

当没有这两个属性的时候,浏览器会立刻加载并执行指定的脚本
有async时,加载和渲染后面元素的过程将和script的加载和执行并行
有defer时,加载和渲染后面元素的过程将和script的加载并行,但执行时间要等所有元素解析完后才执行
VUE项目很少用:模块化开发import解决了依赖关系的问题;webpack等工具会自动处理脚本的加载和执行顺序;动态导入和生命周期函数提供更灵活的加载机制

setTtimeout的最小执行时间

setTtimeout 4ms setInterval 10ms

function func(message){
	;
}
//设置100毫秒后执行func函数
var timer = setTimeout(func, 100, "你好");

function cancel(){
	clearTimeout(timer);   //取消超时调用
}
//设置100毫秒重复执行func函数
function func(args){
  //函数本身的逻辑
  ...
}
var timer = setInterval(func, 100, args);

ES6新特性

1、新增块级作用域(let const)
     不存在变量提升,使用变量只能在声明后
     会形成块级作用域
     不能在同一个作用域内重复声明
2、新增了定义类的语法糖calss
3、新增了数据类型symbol、map、set
4、新增了解构赋值
     从数组或对象中取值,然后给变量赋值
5、新增了函数参数的默认值
6、数组新增了api
7、对象和数组新增了扩展运算符
8、Promise
     为了解决回调地狱的问题
     把异步操作队列化
     自身有all,reject,resolve,race方法
     原型上有then,catch
     状态:pending fulfilled rejected 状态不可逆
     async await
         同步代码做异步的操作,两者必须搭配使用
         async内有异步操作,调用函数会返回promise
9、新增了模块化(import export)
10、新增了generator(也是用来解决异步操作)
11、新增了箭头函数
     不能作为构造函数使用
     箭头函数没有arguments,或者说只有父作用域的arguments
     不能用call、bind、apply改变this指向,永远指向父作用域的this

call、bind、apply区别

都是改变this指向和函数调用
call和apply类似,call传的是参数列表 apply是数组,call用的多
bind传参后不会立刻执行,会返回一个改变了this指向的函数,这个函数还是可以传参,bind()()
在vue中什么时候this不指向当前组件:methods下的箭头函数,事件监听函数如setTimeout等(这时候使用箭头函数或者bind这类来绑定this)

递归

如果一个函数内可以调用函数本身,这就是递归函数
函数内部调用了自己
必须要有退出条件return

深拷贝

深拷贝就是完全拷贝一份新的对象,会在堆内存中开辟新的空间,拷贝的对象被修改后,原对象不会发生改变
主要针对引用数据类型
1、扩展运算符,缺点只能对一维对象的深拷贝,对象里属性不能有对象
2、JSON.parse(JSON.stringify(obj)),缺点是不能拷贝对象中的内部函数
3、利用递归函数来实现
4、lodash库

事件循环

JS是一个单线程的脚本语言(浏览器是多线程的)
主线程 执行栈 任务队列 宏任务 微任务
主线程先执行同步任务,然后采取执行任务队列里的任务,如果在宏任务(setTimeout)之前有微任务,先执行微任务
全部执行完后,等主线程的调用,调用完后再去任务队列中查看是否有异步任务(axios请求),这样的一个循环的过程
---------------------------------------
JS分为同步任务和异步任务
JS的任务都在执行栈顺序执行
执行同步任务,进入主线程,执行异步任务,将其加入任务队列
执行栈里任务执行完,系统读取任务队列,将异步任务的回调事件添加到执行栈中

异步任务分宏任务和微任务,宏任务:script 定时任务 IO操作 UI渲染 微任务:async/await Promise.then
优先处理微任务,可以在UI渲染之前执行,使页面更新

ajax是什么

创建交互式网页应用的网页开发技术
在不重新加载页面的前提下,与服务器交换数据并更新部分内容
通过XmlHttpRequest对象向服务器发送异步请求,然后从服务器拿到数据后,通过js操作DOM更新页面
1、创建XmlHttpRequest对象
2、通过这个对象的open方法和服务器建立连接
3、构建请求所需的数据。通过send方法发送给服务器
4、通过这个对象的onreadystate change事件监听状态
5、接收并处理服务器返回的数据结果
6、把处理的数据渲染更新到html里

get和post有什么区别

get一般是获取数据 post一般是提交数据
get的参数会放到url上,post是放到请求体中
get请求刷新服务器或退回是没有影响的,post请求退回时会重写提交数据
get会被缓存,post不会
get会保存到浏览器的历史记录中,post不会
get请求只能进行url编码,post请求支持很多

promise内部原理,优缺点

Promise对象,封装了一个异步操作并且还可以获取成功或失败的结果
为了解决回调地狱的问题
把异步操作队列化
使用new来创建一个,传一个函数作为参数,这个函数也有两个参数,一个是resolve一个reject
自身all,reject,resolve,race方法
原型上有then,catch
状态:pending fulfilled rejected 状态不可逆
确定,无法取消promise,一旦创建,不能取消
如果不设置回调,内部抛出的错误,无法反馈
若当前处于pending状态,无法得知目前咋哪个阶段
----------------------
then:then方法用于注册当状态变为fulfilled或者reject时的回调函数,promise.then(成功的值,失败的原因)
catch:捕获前面then里的异常,相当于promise.then(null,失败的原因)
resolve、reject 分别返回不同状态的promise对象,让then来处理回调
all:参数是promise对象组成的数组,一个是rejected则返回rejected,全是fulfilled则看谁慢
race:参数也是数组,速度快的执行resolve(),用于对比超时情况

promise和async await的区别是什么

1、都是处理异步请求的方式
2、promise是ES6 async await是ES7
3、async await是基于promise实现的
promise是返回对象,我们要用then,catch方法去处理和捕获异常,且书写方式是链式,容易造成代码重叠,async await是通过try catch进行异常捕捉
async await 最大的优点是让代码看起来像同步一样,只要遇到await就会立刻返回结果,然后再执行后面的操作,promise.then有时会出现请求还没返回,就执行了后面的操作

浏览器的存储方式有哪些

cookies H5前,兼容性好,请求带自带cookies,存储量小
localstorage H5,操作简单,永久存储,保存值类型被限定
sessionstorage 当前页面关闭后就会立刻清理,会话级别的存储方式

token存储在cookies还是sessionstorage

1、存sessionstorage里,后期每次请求需要把党当作请求头传给后台
2、存cookies,会自动发送,缺点是不能跨域,且容易受CSRF攻击

svg格式

基于XML语法格式的图像格式,可缩放矢量图,本质是文本文件
svg可直接插入页面中,成为DOM的一部分,然后用js或css进行操作 或者 <img src="pic.svg" /> 也可以转为base64然后再写入页面中

JWT

JSON Web Token,通过json形式作为在web应用中的令牌,可以在各方之间安全的把消息作为json进行传输
JWT的认证流程:
1、前端把账号密码发送给后端的接口
2、后端核对账号密码成功后,把用户id等其他消息作为JWT负载,把它和头部分别进行base64编码拼接后签名,形成一个JWT(token)
3、前端每次请求,都会把JWT放到HTTP请求的Authorization字段
4、后端会检查是否存在,如果存在就验证JWT有效性
5、验证通过后,使用JWT包含的用户消息进行其他的操作,并返回对应结果

npm的底层环境是什么

npm是node package manager,node的包管理和分发工具

HTTP协议规定的协议头和请求头

1、请求头信息
Accept 浏览器告诉服务器所支持的数据类型
Host 浏览器告诉服务器,访问的哪台主机
Referer浏览器告诉服务器我是从哪来的
User-Agent 浏览器类型版本信息
Date 浏览器告诉服务器我是什么时候访问的
Connection 连接方式
cookies
X-Request-With:请求方式
2、响应头信息
Location 告诉浏览器你要去找谁
Server 告诉浏览器服务器类型
Content-Type 告诉浏览器返回的数据类型
Refresh 控制浏览器的定时刷新

浏览器缓存策略

强缓存(本地缓存)、协商缓存(弱缓存)
强缓存:不发起请求,直接使用缓存里的内容,浏览器把js、css、image等存在内存中
协商缓存:需要向后台发请求,通过判断来决定是否使用协商缓存,如果请求内容没有改变,则返回304,浏览器就用缓存的内容

什么是同源策略

www.aaa.com:8080/index/vue.j…
协议 子域名 主域名 端口号 资源
指协议 域名 端口号 三者一致,其中一个不一样则不是同源,会产生跨域问题
三个运行跨域的标签 img link script
跨域是跨域发送请求,后端也会返回正常结果,只不过被浏览器拦截了
跨域解决方案
JSONP
CORS
websocket
反向代理

防抖和节流是什么

都是为了应对页面中频繁触发事件的优化方案
防抖:避免事件重复触发,多少时间内某个事件只会触发一次,如果在这段时间内又被触发则重写计时
使用场景:频繁和服务端交互、输入框的自动事件
节流:把频繁触发的事件减少,每隔一段时间必然会执行,不受是否重复触发的影响
使用场景:scroll事件

什么是json

纯字符串形式的数据,不提供任何方法,适合在网络中进行传输
什么时候使用:定义接口;序列化;生成token;配置文件package.json

无感登录

在响应中拦截,判断token返回过期后,调用刷新token的接口(优先)
后端返回过期时间,前端判断token过期时间,调用刷新token的接口
写定时器,定时刷新

大文件上传

1、分片上传,将需要上传的文件,按照一定的规则分成相同大小的数据块,初始化一个分片上传任务,返回本次分片上传的唯一标识,按照一定规则依次上传;发送后,服务端会根据数据上传完整性,如果完整则把数据块合并成原始文件
2、断点续传,服务器返回,从哪里开始 浏览器自己处理

H5C3新特性

H5:1、语义化标签;2、新增音频视频;3、画布canvas;4、数据存储;5、增加了表单控件 email url search...;6、拖拽释放API
CSS3:1、新增选择器:属性选择器、伪类选择器;2、增加了媒体查询;3、文字阴影;4、边框;5、盒子模型box-sizing;6、渐变;7、过渡;8、自定义动画;9、背景的属性;10、2D和3D
解决了很多移动端的兼容问题

Vue

v-if和v-show

都可以控制元素的显示和隐藏 1、v-show控制元素display来显示和隐藏,v-if是把DOM元素添加或删除
2、v-if有一个局部编译/卸载的过程,在这个过程中会适当的销毁和重建内部的事件监听,v-show只是简单的css切换
3、v-if效率较低 v-show效率较高

MVVM

Model-View-ViewModel
提供View和ViewModel的双向绑定,只需要关注业务逻辑,不需要操作DOM

v-for中key值的作用

key属性是DOM元素的唯一标识 作用:1、提高虚拟DOM更新,只更新之前不存在的DOM;2、若不设置key可能有bug;3、为了触发切换的过渡效果

生命周期

组件从创建到销毁的过程
1、创建
beforeCreat 属性和方法都不可用
created 实例创建完毕,完成数据监测,可以使用数据,修改数据,不会触发updated
2、挂载
beforeMount 完成模板编译,即将渲染,修改数据
mounted 把编译好的模板挂载到页面,这里可以发送异步请求有也可以访问DOM节点
3、更新
beforeUpdate 组件数据更新前,数据是新的,页面是旧的,组件即将更新,准备渲染,可以改数据
updated render重新做了渲染,数据和页面都是新的,避免在此更新数据
4、销毁
beforeDestroy 示例销毁前,这里实例还能用,可以清除定时器等
destroyed 已被销毁
5、使用keep-alive时多出两个周期
activited 组件激活时
deactivited 组件被销毁时

在created和mounted去请求数据,有什么区别

created 在渲染前调用,通常先初始化属性,然后渲染
mounted 在模板渲染完成后调用,一般都是初始化页面后,再对元素节点进行操作,在这里请求数据可能会出现闪屏的问题
一般用created比较多
父子组件请求的顺序也会不一样,created里请求的话先父后子,mounted里的话是先子后父
请求的数据对DOM有影响使用created
如果请求的数据和DOM无关,或数据需要在已经渲染的情况下使用如cesium echart,可以放在mounted

vue的修饰符有哪些

1、事件修饰符
.stop 阻止冒泡
.prevent 阻止默认行为
.capture 内部元素触发的事件先在次处理
.self 只有event.target是当前元素触发
.once 事件只会触发一次
.passive 立即触发默认行为
.native 把当前元素作为原生标签看待 @click.native="onClick"
2、按键修饰符
.keyup 键盘抬起
.keydown 键盘按下
3、系统修饰符
.crtl
.alt
.meta
4、鼠标修饰符
.left
.right
.middle
5、表单修饰符
.lazy 等输入完成后显示
.trim 删除前后空格 v-model.trim = "value"
.number 输入是数字或转为数字

elementui怎么表单验证

1、在表单中加入:rules属性,在data里写校验规则;
2、内部添加规则,rules="[...]"
3、自定义函数校验,放到data里的rules里,和第一点差不多

vue如何进行组件通信

1、父传子
props 父组件调用子组件处使用自定义属性,然后子组件使用props
子组件通过$parent.xxx来使用、修改父组件的值
2、子传父
$emit 子组件绑定自定义事件,触发执行后,传给父组件,父组件需要用事件监听来接收参数:子 this.$emit('getMsg','我从子组件来'),父 @getMsg="showMsg" showMsg(xxx){获取到子组件的值}
父组件直接拿到子组件,ref this.$refs.child
3、兄弟传
事件总线,new一个新的vue实例,用on和emit来对数据进行传输,

创建一个eventBus.js(也可以直接利用main.jsimport Vue from 'vue';
export const EventBus = new Vue();
发送消息
import { EventBus } from './eventBus.js';
EventBus.$emit('valueChanged', 'Hello from Sibling A');
接收消息
import { EventBus } from './eventBus.js';
created() {
EventBus.$on('valueChanged', (newValue) => {
this.value = newValue;
});
}

4、vuex传值
5、路由传值
6、插槽

slot

插槽:预留的位置,父组件在使用时在组件标签中间写的内容,会填充到slot的位置,即使用组件时,可以往插槽中传递内容
匿名插槽,直接写在组件中的内容,会填充到默认的插槽中(没有用name属性起名字的插槽)

父:
<template> 
    <slot1>
        <h2>studing</h2> 
    </slot1> 
</template><template> 
    <div class="box1">
        box1
        <slot></slot>
    </div> 
</template>

具名插槽,通过name属性给插槽起名字 ,有名字的插槽称为具名插槽

<template v-slot:s1>working</template><slot name="s1"></slot>

作用域插槽:在组件中定义插槽的时候,给插槽中预留位置用在接收data中的传过来的数据; 其他组件在使用这个组件的时候,可以获得该传递过来的数据

<div class="box5"> 
    box5 
    <slot name="s1" :item="info"></slot> 
</div><slot2>
    <template v-slot:s1='scope'>
        <h3>信息:{{scope}}</h3>
        <h3>姓名:{{scope.item.name}}</h3>
    </template>
</slot2>

keep-alive

Vue的一个内置组件,包裹组件的时候,会缓存不活跃的组件实例,而不是销毁他们

axios怎么做封装的

requests.js

import axios from "axios";
//引入进度条
import nprogress from 'nprogress';
//引入进度条样式
import "nprogress/nprogress.css";

//创建实例
const requests = axios.create({
  baseURL:'/api',
  timeout:5000
})
//拦截器
requests.interceptors.request.use((config)=>{
  config.headers.userTempId = localStorage.getItem('UUIDTOKEN')
  config.headers.token = sessionStorage.getItem('TOKEN')
  nprogress.start()
  return config
})

requests.interceptors.response.use((res) => {
  //成功的回调函数
  nprogress.done()
  return  res.data;
},(error) => {
  //失败的回调函数
  console.log("响应失败"+error)
  return Promise.reject(new Error('fail'))
})

export default requests

index.js 封装接口

import requests from "./request";
import ajaxMock from './ajaxMock'
//登录
export const Login =(data) =>{
  return requests({
    url: `/user/passport/login`,
    method: 'POST',
    data
  })
}
//首页三级分类接口
export const reqCateGoryList = () =>{
  return requests({
    url: '/product/getBaseCategoryList',
    method: 'GET'
})
}
...
//获取商品接口
export const reqGoodsList = (params) =>{
  return requests({
    url: '/list',
    method: 'POST',
    data:params
})
}

//获取商品详情
export const reqdetail =(params) =>{
  return requests({
    url: `/item/${params}`,
    method: 'GET'
  })
}

//获取商品详情
export const addOrUpdateShopcar =(skuId,skuNum) =>{
  return requests({
    url: `/cart/addToCart/${skuId}/${skuNum}`,
    method: 'POST'
  })
}

使用接口

import { Login } from '@/api/'

    async userLogin() {
      const result = await Login(this.loginInfo)
      if (result.code === 200) {
        window.sessionStorage.setItem('TOKEN', result.data.token)
        //看路由是否包含query,如有跳转query 没有跳到home
        let toPath = this.$route.query.redirect || '/home'
        this.$router.push(toPath)
      }
    }

封装一个全局弹窗组件

components里新建一个BOX.vue,写好框架、样式 main.js里Vue.component('组件名',引入的组件) 调用 <组件 />

vue路由是怎么传参

params传参(不显示参数)

this.$router.push({name:'index',params:{id:item.id}})
this.$route.params.id

params传参(显示参数)

//子路由配置
{
  path: '/child/:id?',
  component: Child
}
//父路由编程式传参(一般通过事件触发)
this.$router.push({
    path:'/child/${id}',
})

query传参(显示参数)(可以解决页面刷新参数丢失的问题)

//子路由配置
{
  path: '/child,
  name: 'Child',
  component: Child
}
//父路由编程式传参(一般通过事件触发)
this.$router.push({
    name:'Child',
    query:{
    	id:123
    }
})

vue路由的hash模式和history模型有什么区别

默认hash模式,在路由定义history
const router = new VueRouter({
  routes,
  mode: "history"
})

image.png

路由故障

路由出现故障的处理,本质上是reject来处理故障

const originPush = VueRouter.prototype.push;

VueRouter.prototype.push = function (location,resolve,reject){
  if(resolve && reject){
    originPush.call(this,location,resolve,reject)
  }else{
    originPush.call(this,location,()=>{},()=>{})
  }
}

路由拦截是怎么实现

需要在路由配置中添加一个字段

{
    name:'index',
    path:'/index',
    component:Index,
    meta:{
        requireAuth:true
    }
}
router.beforeEach((to,from,next)=>{
        if(to.meta.requireAuth){
            if(store.state.token){
                next()
            }
            else{
            
            }
        }
})

vue的动态路由

要在路由配置里设置meta属性,扩展权限相关的字段,在路由导航守卫里通过判断这个权限标识,实现路由的动态增加和跳转
根据用户登录的账号,返回用户角色
前端根据角色,跟路由表的meta.role进行匹配,用vuex存储的信息和这些路由对比
把匹配搭配的路由形成可访问的路由(常量路由,异步路由,任意路由)
在vuex引入router 然后使用router.addRoutes来进行重新配置路由

computed和watch区别

computed计算属性,watch监听,监听数据的变化
computed适用于需要根据已有的属性计算和处理,得出一个新值的场景;watch监听,适用于需要对数据变化做出响应或者执行异步操作的场景。
computed是支持缓存,依赖的属性值发生变化,计算属性才会重新计算,否则缓存;watch不支持缓存
computed不支持异步,watch可以异步操作
computed是第一次加载就监听,watch是不监听
computed函数必须return watch不用

$nextTick()

$nextTick() 是 Vue 提供的一个异步方法,用于在 DOM 更新之后执行回调函数,

$set()

数据更新,视图不更新的问题,可以用this.$set(target,key,修改后的值)

vuex在什么场景会使用,属性有哪些

state 存储变量
getters state的计算属性
mutations 提交更新数据的方法
actions 和mutations差不多,提交mutations来修改数据,可以包括异步操作,一般在这里请求数据
modules 模块化vuex
使用场景:用户个人信息,购物车模块,订单模块...数据量比较大的情况

import { reqGoodsList } from '@/api'

const state = {
  GoodsList:{}
};
const mutations = {
  GoodsList(state,GoodsList){
    state.GoodsList = GoodsList
  }
};
const actions = {
  async getGoodsList({commit},params){
    const result = await reqGoodsList(params)
    if(result.code === 200){
      commit('GoodsList',result.data)
    }
  }
};
const getters = {};

export default {
  state,
  mutations,
  actions,
  getters
}

import Vue from "vue";
import Vuex from 'vuex'

Vue.use(Vuex)

import home from './home'
import search from './search'
import detail from './detail'
import shopcart from './shopcart'
import user from './user'
import trade from './trade'


export default new Vuex.Store({
  modules:{
    home,
    search,
    detail,
    shopcart,
    user,
    trade
  }
})

  mounted() {
    this.$store.dispatch('getGoodsList', this.searchParams)
  },
  computed: {
    ...mapState({
      goodsList: state => state.search.GoodsList
    })
  },

vuex的响应式处理

vuex是vue的数据管理仓库
vue中可以直接触发methods中方法,vuex是不可以的,为了处理异步,当触发事件的时候,回通过dispatch来访问actions中的方法,actions中的commit会触发mutations中的方法,然后修改state中的值,然后通过getter把数据更新到视图

vuex持久化存储

本身不是持久化存储,1、使用localstroage 2、使用插件

  1. 数据分层存储:token用HttpOnly Cookie存储(防XSS),用户基础信息用localStorage存储
    // 用户信息存储
    static setUser(user) {
      const ciphertext = CryptoJS.AES.encrypt(
        JSON.stringify(user), 
        this.SECRET_KEY
      ).toString()
      localStorage.setItem('user', ciphertext)
    }

    static getUser() {
      const ciphertext = localStorage.getItem('user')
      if (!ciphertext) return null
      const bytes = CryptoJS.AES.decrypt(ciphertext, this.SECRET_KEY)
      return JSON.parse(bytes.toString(CryptoJS.enc.Utf8))
    }

    static clearUser() {
      localStorage.removeItem('user')
    }

3. 自动恢复机制:在store初始化时从持久化存储恢复数据

const state = {
    user: Auth.getUser() || null,
    token: localStorage.getItem('token') || null,
  }

5. 加密处理:敏感数据用AES加密存储,从vuex里取出来就行了

vue中双向绑定的原理

image.png

vue遍历方式

1、普通遍历,对象.forEach
2、对元素统一操作,对象.map
3、查找符合条件的元素,对象.filter
4、查询索引,对象.findindex,对象.some

如何搭建一个脚手架

1、下载node cnpm webpack vue-cli
2、创建项目:
找到对应文件,利用node指令创建 cmd
vue init webpack xxx
回车项目描述、作者
选择vue build ....
或者直接用vue ui来做

对组件的理解

一个重复使用的Vue实例,独一无二的组件名称
可以抽离单独的公共模块,比如头部、底部、提交图片等,提交代码的复用率

如何封装一个组件

1、使用Vue.extend()创建一个组件
2、Vue.components()注册组件,不必须
3、子组件需要数据,可以在props中接收定义
4、子组件修改好数据,要把数据传递给父组件,可以用emit
5、父组件通过属性text-colorbackground-color向子组件传递了颜色值。子组件通过props接收这些值,并使用:style动态绑定到子组件的根元素上。这样,你可以从父组件控制子组件的样式
6、全局组件Vue.component('CateSelect', CateSelect)

封装一个footer组件,并在特定页面才有

定义Footer.vue 写好布局和样式
在APP里引入组件,并设置只有路由信息中包含的show为true才显示

 <Footer v-show="this.$route.meta.show"></Footer>

import Header from './components/Header'
import Footer from './components/Footer'

  components: {
    Header,
    Footer
  },

router里定义路由时,设置是否显示该组件

  { path: '/register',component: Register,meta: {show:false} },
  { path: '/search/:keyword?',component: Search,meta: {show:true},name:"search" },

封装一个组件,需要满足什么条件

1、低耦合,组件间依赖越小越好
2、最好从父级传入信息,不要在公共组件中请求数据
3、传入的数据要进行校验
4、处理事件的方法要写到父组件中

vue代理

vue.config.js 里 配置代理 proxy:'后端ip:端口'
但打包后不能直接使用,

vue的过滤器

vue的特性,用来对文本进行格式化处理
使用他的地方,一个是插值表达式,一个是v-bind
全局过滤器 Vue.filter
本地过滤器 filer:{}

强制刷新

1、localtion.reload
2、this.$router.go(0)
3、provide和inject

vue2和vue3区别

1、双向绑定数据的原理不同
2、是否支持碎片,是否有多个根节点
3、api不同
4、定义数据变量的方法不同
5、生命周期不同
6、传值方式不同
7、指令和插槽不同
8、main.js不同

vue3为什么用proxy

1、proxy可以代理整个对象,defineproperty只代理对象上的某个属性
2、proxy对代理对象的监听更加丰富
3、proxy代理对象会生成新的对象,不会修改被代理对象的本身
4、proxy不兼容IE浏览器

vue3性能比vue2好

diff算法优化
静态提升
事件侦听缓存

vue性能优化

1、编码优化:
不要把所有数据放到data中
v-for时给每个元素绑定事件用事件代理
keep-alive缓存组件
尽可能拆分组件,提高复用性
key的值要保证唯一
合理使用路由懒加载,异步组件

const router = new VueRouter({
routes: [
{
path: '/foo',
component: () => import('./Foo.vue')
},
{
path: '/bar',
component: () => import('./Bar.vue')
}
]
})

数据持久化存储尽量使用防抖和节流优化
2、加载优化
按需加载
内容懒加载
图片懒加载
3、用户体验
骨架屏
4、SEO优化
预渲染
服务端渲染
5、打包优化
SDN形式加载第三方模块
多线程打包
6、缓存和压缩
客户端缓存、服务端缓存
服务端Gzip压缩

首屏优化

1、使用路由懒加载
2、非首屏组件使用异步组件
3、首屏不重要的组件延迟加载
4、静态资源放到CDN上
5、减少首屏JS CSS资源文件的大小
6、尽量减少DOM的数量和层级
7、使用精灵图
8、做一些loading
9、nginx 开启Gzip压缩

如何规划项目文件

public 存放图片 图标 index.html
src
   api 封装的axios
   assets 静态资源 主题样式
   components 组件
   plugins 外部插件
   router 路由
   static 静态资源
   utils 工具js
   view 页面视图
App.vue
main.js
package.json
vue.config.js

打包后问题

1、白屏啥也没有,js的引用路径存在问题导致的

module.exports = {
  publicPath: './'
  }

2、路由模式,可能必须要用history模式,后端处理 3、dist,放到nginx里,配置nginx.conf即可

nuxt.js

基于vue的应用框架,关注的是渲染,可以开发服务端渲染应用的配置
SSR 服务端渲染

SEO如何优化

搜索引擎优化,以便搜索引擎能够更好地抓取和索引网站内容
1、SSR
2、预渲染 prerender-spa-plugin

按需引入element ui

npm i element-ui -S 或 yarn add element-ui
npm install babel-plugin-component -D
在babel.config.js里面新增配置项
在src下面新建一个配置文件进行统一管理
最后在main.js引用

Echarts

title标题组件 show text link
toolbox工具栏 导出图片 数据视图 切换 show orient feature
tooltip 提示 tigger
markPoint 标注点
markLine 图标的标线
xAxis type data
yAxis

git如何合并、拉取代码

拉取代码 git pull '仓库地址'
查看状态 git status
提交到本地缓存区 git add .
提交到本地仓库 git commit -m '修改描述'
提交到远程仓库 git push '仓库地址' master
创建分支 git branch -b xxx
合并分支 git merge '合并分支的名字'
再提交

git冲突

两个分支修改了同一个文件 两个分支修改了同一个文件的名字 1、当前分支上,直接修改代码 add commit 2、在本地当前分支上,修改冲突代码 add commit push

浏览器渲染原理

整个渲染流程分为多个阶段,分别是: HTML 解析、样式计算、布局、分层、绘制、分块、光栅化、画

解析html生成DOM树 ===》 解析css生成CSSOM树 ===》 遇到没解析的css会跳过 ===》 遇到script会等待 ===》 样式计算 ===》 布局,得到布局树(和DOM树不一定对应) ===》 分层 ===》 绘制 ===》 分块 ===》 光栅化 ===》 画

https如何进行加密

是一种安全的HTTP通信协议,它通过在HTTP和TCP之间添加了加密和身份认证的SSL/TLS协议来确保通信的安全性。下面是HTTPS的加密流程:

客户端发起HTTPS连接:客户端(通常是浏览器)向服务器发送HTTPS请求。在请求头中包含了HTTPS,告诉服务器需要使用加密的HTTPS协议进行通信。

服务器发送SSL证书:服务器在收到客户端的HTTPS请求后,会向客户端发送自己的SSL证书。SSL证书中包含了服务器的公钥、服务器的域名等信息,客户端会用来验证服务器的身份。

客户端验证证书:客户端收到服务器的SSL证书后,会验证证书的有效性。验证主要包括检查证书的颁发机构、证书的有效期和服务器域名是否与证书中的域名匹配。

客户端生成对称密钥:如果SSL证书验证通过,客户端会生成一个随机的对称密钥(称为会话密钥)用于后续的数据加密和解密。对称密钥只在本次会话中使用,有助于提高数据传输的效率。

客户端用服务器的公钥加密对称密钥:客户端使用服务器的公钥(在SSL证书中获取)加密生成的对称密钥,然后将加密后的对称密钥发送给服务器。

服务器用私钥解密对称密钥:服务器收到客户端发送的加密对称密钥后,使用自己的私钥(只有服务器持有)解密对称密钥。

数据传输:服务器和客户端双方都拥有相同的对称密钥,之后的通信过程将使用对称密钥进行数据的加密和解密。这样就确保了通信的机密性和完整性。

vue-router底层原理

  1. 路由的基本概念

在深入 Vue Router 的原理之前,我们需要理解几个基本概念:

  • 路由(Route) :一个映射关系,将 URL 路径映射到组件。
  • 路由匹配(Route Matching) :根据当前 URL 找到匹配的路由记录。
  • 导航守卫(Navigation Guards) :在路由跳转过程中执行的一些钩子函数,用于控制路由跳转的行为。
  1. Vue Router 的核心组件

Vue Router 主要由以下几个核心组件构成:

  • Router:Vue Router 的实例,负责管理路由的初始化、解析和匹配等。
  • Route Matcher:一个用于匹配 URL 和路由定义的工具,它决定了当前 URL 应该渲染哪个组件。
  • History Modes:Vue Router 支持三种历史模式(hash, history, abstract),它们决定了 URL 的构成方式。
  1. 路由的初始化过程

Vue Router 的初始化大致可以分为以下几个步骤:

  1. 创建 Router 实例:通过 new VueRouter({...}) 创建路由器实例,传入路由配置。

  2. 创建匹配器(Matcher) :内部使用一个路径匹配器(如 path-to-regexp),根据提供的路由配置生成匹配规则。

  3. 初始化路由模式:根据配置(如 mode 属性)选择合适的路由模式(hash, history)。

  4. 监听 URL 变化:通过浏览器的 popstate 事件或 hashchange 事件监听 URL 的变化。

  5. 匹配并渲染组件:根据当前 URL 和路由配置,使用匹配器找到匹配的路由,渲染对应的组件。

  6. 导航守卫的执行

Vue Router 支持全局守卫、路由独享守卫和组件内守卫,这些守卫可以在路由跳转的不同阶段执行,例如:

  • 全局前置守卫router.beforeEach
  • 全局解析守卫router.beforeResolve
  • 全局后置钩子router.afterEach
  • 路由独享守卫:在路由配置中使用 beforeEnter
  • 组件内守卫:如 beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave
  1. 动态路由和懒加载

Vue Router 支持动态路由和懒加载,可以通过在路由配置中使用动态路径参数或使用 webpack 的代码分割功能实现组件的懒加载,优化应用的加载时间和性能。

promise底层实现

在底层实现中,Promise是基于事件循环(Event Loop)的。以下是Promise实现的基本步骤:

  1. 状态封装:使用一个内部状态变量来跟踪Promise的当前状态(pending, fulfilled, rejected)。
  2. 值存储:使用一个内部值变量来存储Promise解决(resolve)或拒绝(reject)时的值或原因。
  3. 事件监听:使用事件监听器来处理.then().catch().finally()方法中的回调函数。当Promise的状态改变时,会触发这些监听器。
  4. 异步执行:为了保持JavaScript的单线程特性,异步操作通常通过微任务队列(microtask queue)来实现,例如使用Promise自身的.then()方法或者通过浏览器的queueMicrotask()函数。

前端缓存机制

缓存机制随处可见,优秀的缓存机制可以缩短网页请求资源的事件,减少延迟,并且由于缓存文件可以重复利用还可以减少带宽,降低网络负荷,从而提高我们的性能优化

前端缓存主要是指HTTP缓存和浏览器缓存,前端缓存可以加快页面加载速度、减轻服务器负担、减少延迟与网络阻塞、提高用户体验、支持离线使用等,能够有效的提升网站与应用的性能,但同时也面临着缓存过期、用户安全、缓存清除等问题

http缓存指的是: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。

浏览器缓存

如何用css写一个动画

Flex是如何布局的的

Vue.use()的原理是什么

对Vue中的虚拟DOM是怎么理解的