前端面试题收集
2023-08-23 本文已影响0人
h2coder
HTML和CSS
H5的新特性
- 语义化标签
- header,头部标签
- footer,尾部标签
- article,文章主体标签
- section,内容块
- nav,导航栏
- aside,侧边栏
- 媒体标签
- audio,音频标签
- video,视频标签
- 地理定位API
- WebStorage
- localStorage,本地存储,相比cookie,大很多
- sessionStorage,会话存储
- WebSocket,全双工协议,一般用于作为即时通信
- Canvas绘图
CSS3新增的特性
- 弹性布局Flex
- 媒体查询
- 结构伪类选择器
- first-child
- last-child
- nth-child(n)
- 伪元素选择器
- 元素::before
- 元素::after
盒子、图片水平、垂直居中
- 使用Flex布局,
justify-content
和align-center
都设置为center - 使用子绝父相,子元素绝对定位,父元素相对定位,子元素
top:50%
,left:50%
,让子盒子向右、向下移动父盒子宽高的一半,再通过transform-translate(-50%, -50%)
,向左、向上移动自身的一半
CSS盒模型
- 默认CSS盒模型,
box-sizing
的值是content-box
,元素的宽高只是指内容的宽度、高度 - 怪异盒模型,也叫IE盒模型,box-sizing的值是
border-box
,元素的宽高,除了包含内容的宽高,还包含内边距和边框
块级元素和行内元素的特点和区别
- 块级元素
- 独占一行
- 可以设置宽高
- 可以设置内、外边距和边框
- 行内元素
- 一行显示多个
- 宽高由内容撑开
- 只能设置内边距、边框,设置外边距无效
CSS选择器权重值
- !important > 行内样式 > id选择器 > 类选择器 > 标签选择器 > 通配符选择器
H5的事件有哪些?
- input标签
- input,输入改变事件
- focus,获取焦点事件
- blur,失去焦点事件
- keydown,键盘按键按下事件
- keyup,键盘按键弹起事件
- mouseover,鼠标移入事件(mouseover支持冒泡,mouseenter不支持冒泡)
- mousemove,鼠标移动事件
- mouseout,鼠标移出事件
H5的input标签的type值有哪些?
- text,文本框
- password,密码框
- radio,单选框
- checkbox,复选框
- button,按钮
- submit,提交按钮
- date,日期选择
- color,颜色选择
JavaScript
JS数据类型
- 基础数据类型
- 字符串 String
- 数字 Number
- 布尔 Boolean
- undefined 未定义的值
- null 空值
- Symbol 标识,Symbol对象永远不相等,创建时传入的名称字符串,只是用来在debugger时用来区分,应用场景是给对象设置独一无二的属性,避免属性被覆盖和把原有属性覆盖
- BigInt 大整数,拥有比Number类型更大的数值范围
- 引用数据类型
- 对象 Object(包含:函数 Function、数组 Array)
回流和重绘
- 回流:元素的大小、布局发生改变时,浏览器需要进行回流进行调整布局
- 重绘:元素的颜色、字体等其他外观属性发生改变,浏览器需要进行重绘来更新外观
- 回流和重绘都有一定的开销,因此应该要避免不必要的回流和重绘,可以通过优化后的CSS选择器,来减少回流的次数。或者使用缓存来避免重复的重绘
- 可以使用
requestAnimationFrame()
函数来在浏览器的下一次重绘之前回调,实现特定的业务逻辑,例如可以在更新页面元素的外观时,将多个操作合并为一次重绘,减少重绘的次数
原型和原型链
- 每个构造函数都有一个
prototype
属性,指向一个原型对象,原型对象中,一般存放该构造函数的共享方法 - 每个对象上,都有一个
__proto__
属性,指向了构造器的原型对象 - 原型链,对象的成员查找提供的一种机制。当访问一个对象的某个成员时,优先查找对象本身,如果没有则查找该对象的的原型对象(也就是proto属性指向的原型对象),如果也没有找到,则查找原型对象的原型对象,依次类推,直到找到Object为止,也就是为null,就会报错
this指向的问题
- 在全局中调用函数,this指向window
- 对象内的this,this指向对象本身
- 在构造函数中的this,this指向该构造函数准备要构造出来的实例
- 在事件回调函数中,this为触发该事件的DOM元素
- 箭头函数中的this,箭头函数没有this,沿用它上一级的作用域的this
- 可以通过call、apply、bind,改变this的指向
forEach和map的区别
- 作用不同:forEach是遍历数组,map是映射数组
- 返回值不同:forEach没有返回值,map有返回值,返回的是映射后的新数组
- 处理流程不同:forEach是按顺序遍历每个元素,并执行回调函数。map是除了按顺序遍历元素,执行回调函数外,还会把回调函数的返回值存到一个新数组中,并返回
call、bind、apply的区别
- call、bind、apply都可以改变函数的this指向
- call和apply,都是改变this指向后,马上调用原函数,区别是函数的参数,call是一个接一个的传入,而apply是将参数放到数组中再传入函数
- bind是改变完this后,不会立即调用函数,而是返回一个新函数,我们在调用新函数,参数也是一个接一个的传入
浅拷贝和深拷贝
- 浅拷贝,拷贝基础数据类型是值拷贝,而拷贝引用数据类型时,拷贝的是对象的内存地址,堆内存中依旧只有一个对象,所以原对象和克隆对象的引用类型属性,指向的是同一个内存地址的对象,修改其中一方会互相影响
- 深拷贝,让引用类型数据的拷贝是完全的复制,在堆内存中是2个对象,修改其中一方不会互相影响
- 浅拷贝,基础数据类型和引用数据类型,都可以使用
...展开运算符
来浅拷贝。引用数据,对象可以使用Object.assign(),数组可以使用Array.prototype.concat() - 深拷贝,可以使用JSON序列化和反序列化来实现,但会丢失掉undefind、Function类型的属性,并且不能处理环形结构的数据
- 工作中,一般使用第三方库lodash,该库提供了浅拷贝、深拷贝的方法,并且能支持undefind、Function属性,环形结构的数据
防抖和节流
- 防抖和节流是常见性能优化手段,可以控制函数的执行频率
- 防抖是一定时间内,只执行最后一次事件,一般使用延时器实现,每次调用都清除掉上一次的延时器,所以只有当用户停止操作,一定时间后才会执行,例如DOM元素点击事件、输入框自动联想搜索
- 节流是单位事件内,控制事件以一定频率触发,一般使用延时器实现,控制事件在一个时间范围内,只触发一次,例如在处理窗口大小变化事件、页面滚动事件、音、视频的进度改变事件等时,需要频繁发送请求、读写localStorage
ES6新特性
- let和const,let拥有块级作用域,不存在变量提升,const是常量,赋值了就不能改变
- 箭头函数,简化函数声明,箭头函数没有this,沿用上一级作用域的this
- 模板字符串,比字符串拼接更简洁、更直观
- 解构赋值,数组解构、对象解构
- 剩余参数
- 函数形参默认值
- 类,引入类的改变,更加容易维护
ES6新增方法
- 数组
- find(),查找元素
- findIndex(),查找元素的下标
- includes,判断元素是否包含
- sort,排序
- 对象
- Object.keys(),获取对象的所有属性名
- Object.values(),获取对象的所有属性值
- Object.assign(),对象浅拷贝
点击穿透现象以及解决方法
- 点击穿透,指的是点击上层元素,下层元素也响应事件
- 防止点击穿透:监听点击事件,在回调函数中,阻止默认点击行为
- event.stopPropagation();
- event.preventDefault();
如何理解Promise
- 一套异步编程的解决方案
- 将异步操作的结果,封装为成功(fulfilled)、失败(rejected)和等待(pending),并且状态一经更改,就不可逆,如网络请求、读取文件
- 解决回调函数嵌套地狱问题,提供链式编程来解决回调地狱的问题
- Promise上有3个静态方法,Promise.all()、Promise.allSettled()、Promise.race()、Promise.any()
- Promise.all(),需要传入一个数组,存放多个Promise,组合多个Promise为一个Promise,全部Promise都成功,才算成功,但有一个Promise失败,就当全部失败(请求商品三级分类,获取到商品一级分类后,并发请求所有二级分类)
- Promise.allSettled(),也是传入多个Promise,组合为一个Promise,所有Promise都执行完毕,不管成功还是失败,才会进行回调(多个异步请求,都结束后,关闭Loading圈)
- Promise.race(),赛跑机制,也是传入多个Promise,组合为一个Promise,但只要第一个Promise成功,就为成功,其他Promise的结果就不管了(并发请求缓存和发出请求,谁先获取到,就用谁的)
- Promise.any(),同样将多个Promise包装为一个Promise,只要有一个Promise成功,就为成功,全部Promise失败时,才算失败
async和await的理解
- async和await是Promise的语法糖,async用于修饰方法,代表是异步方法,await是阻塞等待Promise的成功结果,如果失败会抛出异常,要使用try-catch进行捕获处理
- async和await,将异步代码写法转变回同步写法,相比Promise更加直观
sessionStorage和localStorage的区别
- sessionStorage和localStorage都是用来存储数据到本地,存储大小大概有5M左右,而Cookie只能存储4k的大小
- sessionStorage在浏览器关闭后,自动清除。而localStorage是永久持久化,除非用户手动删除
- localStorage的存储容量,一般比sessionStorage大,不同浏览器可能有差异
rem和em的区别
- rem和em都是字体大小的单位,但计算方式不同
- rem是相对于html根标签的font-size字体大小的比例计算
- em是相对于父元素的字体大小的计算
响应式布局
- 响应式布局,根据用户的设备,手机、平板、PC等不同尺寸的设备,进行自适应,都能有良好的显示效果
- 常见的响应式布局方式
- 使用流式布局,用百分比设置元素的宽高,让元素在不同尺寸的设备上自动调整大小
- 使用媒体查询,定义不同尺寸下元素的尺寸和样式,动态应用不同的样式
- 使用Flex布局,让元素在一个容器中按照一定的规则进行布局
视频控件
- 视频控件,通常使用HTML5的video标签
- 使用时,需要指定视频的地址、视频的宽高、是否自动播放
- 可以控制播放、暂停、快进、快退
什么是同源?
- 协议、域名(IP)、端口号,3者都完全相同,才为同源
- 3者有一部分不一样,都为不同源
- 不同源时,发送ajax请求,产生跨域,请求会被浏览器拦截
为什么会跨域?
- 是浏览器的一种同源安全手段
- 浏览器要求网页地址和请求地址,3部分都必须相同,否则就会产生跨域
- 协议相同,例如http、https
- 主机相同,域名、IP地址
- 端口号相同
- 以下3个标签,不受到跨域限制
- img标签
- link标签
- script标签
跨域怎么处理?
- 使用代理服务,在本地搭建一个代理服务器,如Nginx,它负责转发请求,解决跨域问题
- 使用JSONP,通过动态插入script标签的方式事件跨域访问,只支持GET请求,是一种很老的方式
- 通过服务端配置CROS,CROS是一种跨域资源共享方式,服务端配置HTTP头信息,让浏览器允许跨域
ES6中的var、let、const的区别
- var,在ES6之前使用,存在变量提升、没有块级作用域等问题
- let,用于替代var,没有变量提升,有块级作用域
- const,用于定义常量,赋值后就不能修改
怎么改变this指向
- 通过call、apply、bind来改变函数的this指向
- 如果想沿用上一级的this,可以使用箭头函数,或者在函数调用前先用一个变量that保存this
for of 和 for in 的区别
- for-of,遍历数组的值
- for-in,遍历对象的属性名
什么是同步和异步
- JavaScript是单线程语言,同一时间只能执行一个任务,如果同步执行一个耗时任务,会阻塞执行,导致界面卡顿
- 因此出现了异步编程,通过将耗时任务交给浏览器或操作系统去执行,任务结果通过回调函数或事件机制,回调通知或事件,通知主线程,不会阻塞执行
宏任务和微任务
- 宏任务和微任务,都用于处理耗时任务,有单独的任务队列来存放任务
- 宏任务,例如交互事件、定时器、延时器、script标签的解析
- 微任务,Promise.then()
- 优先级:当队列,存在宏任务和微任务时,优先执行完微任务,再执行宏任务
- 执行流程,JS解析器运行时,遇到同步代码,直接交给执行栈直接执行,如果遇到宏任务,则放入到宏任务队列,遇到微任务则放入到微任务队列。当执行完执行栈中的同步任务后,就会去微任务瞄一眼,如果有微任务则将微任务挪到执行栈,在执行栈中执行微任务,执行完所有的微任务,则再去宏任务队列瞄一眼。如有宏任务,则将宏任务挪到执行栈,执行宏任务。执行完一个宏任务后,又会去微任务瞄一眼,发现有微任务就执行。(总之,微任务的优先级比宏任务高,执行完一次宏任务都要去将微任务队列中,执行完所有的微任务,再执行下一个宏任务,这样设计,就让微任务有了插队的可能)这种不断往复的执行的流程,就是事件循环EventLooper
什么是闭包
- 闭包,具体表现形式:外层函数嵌套内层函数,内层函数使用外层函数的变量,外层函数返回内层函数
- 作用:保护私有数据,防止全局变量污染
- 问题:闭包会导致内存泄漏,需要手动将返回的内层函数的引用设置为null,置空引用,来防止内存泄漏
介绍一下作用域
- 全局作用域,在全局作用域下声明的变量和函数,在任何位置都能访问
- 局部作用域,在函数内或块级作用域中可访问,出了函数作用域或块级作用域就访问不到了
深拷贝,为什么能做到拷贝后互不影响
- 因为深拷贝是将引用数据类型的属性,进行完整拷贝,而不是值的拷贝,也就是不是拷贝对象内存地址,是在堆内存中真的创建了一个新的对象
说一下事件委托的应用场景
- 事件委托的原理是事件冒泡,如果没有事件冒泡,就无法实现事件委托
- 事件委托一般用于给动态生成的元素,绑定事件,例如列表或表格的列表项、表格项,绑定点击事件
- 给父元素绑定一个事件处理函数,不需要给所有子元素绑定,能节省内存,提高性能
什么是纯函数
- 纯函数,只依赖传入的形参,不依赖函数外部的变量,传入相同的形参,总能得到相同的结果
- 纯函数没有副作用
- 如果传入相同的形参,得到不同的结果,那么就不是纯函数
数组去重
- Array.from() + new Set()
- 拓展运算符 + 新数组 + new Set()
- for循环 + indexOf() + 新数组push()
- forEach循环 + indexOf() + 新数组push()
- forEash循环 + includes() + 新数组push()
什么是递归
- 函数调用自身,就叫递归
- 必须有终止条件,否则死循环会栈溢出
封装axios的步骤
- 创建一个request.js,引入axios
- 使用axios.create()创建axios的实例,配置基地址和超时时间
- 配置请求拦截器和响应拦截器,然后默认导出
- 一般在请求拦截器中,添加token到请求头
- 一般在响应拦截器中,判断服务端返回的状态码,例如判断登录状态失效,就跳转到登录页,或者解构固定的服务端响应解构数据,result.data.data
JS怎么判断数据的类型
- 使用typeof,只适用于基础数据类型
- 基础数据类型,判断不了null,判断null会返回object
- 判断NaN,判断的是number
- 引用数据类型,只能判断Function,数组和其他都返回object
- 使用instanceof,只能用于判断引用数据类型,不能判断基础数据类型
- 使用Object.prototype.toString.call(),该方式兼容性最好,既能判断基础数据类型,也能判断引用数据类型
图片懒加载的底层原理
- img标签的src属性,设置为空字符串,将真实的图片地址放在一个自定义属性,例如data-origin中
- 监听页面滚动事件,判断img标签是否可见,可见再把自定义属性中的值,设置到src属性中
Git
Git的常用命令
- git add,添加到暂存区
- git commit,提交代码到版本库
- git push ,推送代码到远程仓库
- git pull,拉取代码
- git clone,克隆代码
- git checkout,签出代码
- git checkout -b,签出或创建新分支
- git merge,合并分支
Webpack
Webpack的版本号
- 2018,webpack4,可零配置运行,打包(构建)速度提高80%
- 2020,webpack5,打包速度更快,开启文件缓存,明显提升打包速度
HTTP协议
HTTP有了解过吗?
- HTTP协议,基于TCP\IP,是基于请求、响应模型,一个无状态的协议
- HTTP协议,请求报文分为请求行、请求头、请求体。响应分为响应行(状态行)、响应头、响应体
- HTTP协议可传输超文本HTML,也可以传输XML、JSON,目前常用是JSON
- 常用请求方式:GET获取、POST提交、PUT更新、DELETE删除
- 常见状态码:200成功,304读取缓存、401没有登录、403权限不足、500服务器内部错误
HTTP不写端口时,默认是多少
- HTTP默认端口为80
- HTTP默认端口为443
HTTP有哪些请求方法
- GET 获取
- POST 提交
- PUT 更新
- DELETE 删除
HTTP常用状态码
- 200成功
- 304读取缓存
- 401没有登录
- 403权限不足
- 500服务器内部错误
HTTP的三次握手过程
HTTP的四次挥手过程
说一下在页面中输入URL到加载完成的过程
HTTP和HTTPS的区别
- 2者都是传输协议
- 安全性:HTTPS在传输数据的基础上,增加了加密,防止数据不被窃取和修改
- 证书,HTTP要求服务端安装SSL证书,验证服务端的身份,确保用户访问的是真实的网站
- 端口号:HTTP的默认端口号是80,HTTPS的默认端口是443
- HTTP协议的URL以http开头,HTTPS协议的URL以https开头
MVVM
说一下你对MVVM的理解
- M是Model模型,是应用程序中的数据和逻辑
- V是View视图,是用户看见的界面
- VM是视图模型,数据驱动视图的框架
- 优点:将视图和业务逻辑分离,以数据驱动视图变化,易维护和拓展
Vue
Vue的插槽
- 默认插槽
- 组件中只有一个插槽,父组件只能传递一个内容给子组件,其实默认插槽也是具名插槽,只是名字叫default
- 具名插槽
- 子组件可以有多个插槽,父组件可以传入多个内容给子组件,子组件需要指定插槽的名字
- 作用域插槽,在具名插槽的基础上,让子组件传出数据给父组件,例如表格项-按钮组件,点击时,需要将当前项的数据,传出给表格父组件使用,例如删除数据、显示表格项详情等
Vue的data,为什么一定是要一个函数
- 因为组件复用时,每个组件都应该有自己的状态,如果data不是一个函数返回的新对象,而是同一个对象,则每个组件都公用一个数据对象,就会导致组件的状态混乱,互相影响
Vue双向绑定的原理
- 双向绑定的原理是数据劫持和观察者模式来实现的,大概分为2步:
- 通过Object.defineProperty来劫持数据,收集依赖
- 当数据变更时,通知依赖去响应视图的变化
- 当Vue实例被创建时,会在内部遍历所有数据,并使用Object.defineProperty来对每个属性添加getter和setter。这样,当数据被改变时,Vue就可以捕获数据,并通知观察者,重新渲染页面
使用Vuex的完整步骤
- 导入vuex依赖
- 创建store.js文件,创建vuex实例,并通过Vue.use(),使用Vuex插件,再挂载到Vue实例上
- 创建module文件夹,配置模块的store.js
- 配置state、mutations、actions、getters
v-model的实现原理
- v-model,其实是一种语法糖,作用是将数据和表单元素进行双向绑定,数据改变时更新视图,表单元素内容更新时,更新数据
- 数据改变时,更新视图,其实是v-bind
- 视图内容改变时,更新数据,其实是v-on
- 例如input标签使用v-model,就是v-bind:value="",v-on:onclick="",也就是:value和@click
什么是单页面应用
- 单页面应用,也就是在HTML页面加载所有页面,通过JS切换视图达到切换页面的效果,而不需要像多页面应用需要重新发起请求由服务端进行渲染
- 单页面应用的性能高,切换页面时,一些通用的头部、尾部等通用样式,不需要重复发出请求来获取,减轻服务端的负载
- 单页面应用的SEO比较差,因为视图都是通过JS动态渲染的,搜索引擎爬虫无法运行JS脚本,只能抓取HTML里面的标签和文本来决定这个站点的价值。
- 单页面应用可以通过服务端渲染SSR来解决单页面应用SEO差的问题
如何解决单页面应用SEO难度大的问题
- 单页面应用可以通过服务端渲染SSR来解决单页面应用SEO差的问题
谈谈你对vue的理解
- Vue,一个渐近式的UI视图框架,理念是使用数据驱动视图,不需要开发者手动操作DOM,让开发者更加关注数据和具体业务
Vue中如何监听路由信息的改变
- 通过watch监听器路由对象的变化
- 使用路由提供的回调钩子函数
nextTick的理解
- 当我们修改响应式变量时,并不会马上就根据变量,重新渲染DOM,而是异步更新
- 因为响应式变量可能在短时间内,频繁修改,如果同步渲染DOM,模板比较复杂时,会导致卡顿,产生性能问题
- 由于Vue的DOM更新是异步的,我们直接在修改响应式变量后,就直接通过ref操作DOM元素,会发现获取不到,而Vue提供了nextTick函数,该函数会在异步更新DOM完毕后回调,我们在这个回调中再通过ref操作DOM就肯定能获取到DOM元素
Vue中对象添加新属性,界面不自动刷新,怎么解决
- 使用Vue实例的
$set
函数,this.$set(attr, index, val);,主动通知Vue劫持新的属性 - 使用Vue包装过的数组方法,例如
splice
,arr.splice(index, 1, value); - 使用
$fourceUpdate
强制刷新 - 使用中转变量,将新变量赋值原有响应式变量
v-for中的key
- key是Vue框架内部使用的,用于Vue框架复用视图时使用,不会对我们业务逻辑产生影响
- 如果不使用key,会导致Vue无法精确复用视图,例如ul中有多个li,当我们删除第一个li时,Vue会删除最后的一个li,然后将内容网上挪,如果第一个li中有一个背景色,就会发现我们虽然删除了数组的第一个元素,但Vue并不是相应的删除第一个li标签,就会导致视图复用出现问题
- 而设置了key,让每个li标签都有一个唯一标识,就能让Vue知道我们删除的数组数据对应哪个视图,就能删对li标签了
说一下Vue组件通讯
- 父、子组件传参
- 父传子:props传参
- 子传父:$emit(),发送自定义事件
- 子孙通信:provide和inject
- 跨组件通信:EventBus、Vuex
说几个Vue的指令
- v-text,相当于innerText
- v-html,相当于innerHTML
- v-if、v-else-if、v-else,相当于if、else if和else,条件渲染
- v-show,和v-if效果类似,都是显示隐藏某个元素,但v-if是直接将DOM元素直接从DOM树移除,v-show是通过切换元素的CSS-display属性来实现显示隐藏
- v-bind,动态绑定属性,可以简写为:号
- :class,动态绑定class,当某个条件为true时,添加指定的class类名给DOM元素,语法:
:class="{类名: 条件表达式}"
,或者::class=[类名1, 类名2, 类名3]
- :style,动态样式,当只想改变DOM元素的某个样式属性,而不想使用类名时使用,语法::style="{color: activeColor, backgroundColor: bgColor}"
- v-model,视图和数据双向绑定,是:value和@input的合写,要求子组件必须有一个名叫
value
的属性名,当子组件经过一些UI操作后,内容发生变化后,发出的自定义事件的名称必须叫input
- sync修饰符,v-model要求子组件只能有一个属性可以进行双向绑定,并且子组件的属性名和发出的事件名都是固定的,不能修改。当子组件有多个属性都可以提供双向绑定时,就可以使用sync修饰符,它可以自定义子组件的属性名和发出的事件名,语法:
v-bind:属性名.sync="值"
,发出的事件的格式:update:属性名="新值"
Vue的路由懒加载
- 路由懒加载,可以让组件在路由被访问时,才加载对应的组件,也就是异步组件,避免一进入页面,就直接加载所有的路由组件,解决单页面应用首屏加载慢的问题
Vuex的核心属性
- state,状态,也就是数据
- mutations,同步修改state的函数
- actions,异步修改state的函数(异步后调用mutation的函数来实现修改)
- getters,计算state为一个新的state,或者合并多个state,相当于计算属性
Vue的路由模式
- hash模式,使用H5的新特性,URL地址
#/
后路径变化时,浏览器不会发出请求,而是触发hashchange事件,但有#/
,URL地址比较难看 - history模式,浏览器一直都支持的模式,URL路径变化时,浏览器会发出请求,要求服务端返回新的页面,但单页面应用只有一个页面,URL的地址都没有对应的页面,只是用于VueRouter监听,切换对应的组件,从而显示不同的页面,该方式需要服务端进行配置,否则会出现404
Vue路由之间的跳转方式
- 有4种方式
- 声明式导航,也就是使用标签来跳转
- 使用
a
标签,设置href
属性为目标页面的路由路径,主要必须加上#/
- 使用
router-link
标签,设置to
属性为目标页面的路由路径,不需要加#/
,点击发起路由跳转
- 使用
- 编程式导航,也就是使用使用JS来跳转
- 使用
this.$router.push
,发起路由跳转 - 使用
this.$router.replace
- 使用
go(N)
,N为数字,正数则前进页面、负数则回退页面
Vue如何封装组件
- 创建一个
.vue
的单文件 - 通过
props
声明组件需要的参数 - 组件进行一些UI操作后,使用
$emit
发送自定义事件,通知父组件更新数据 - 父子组件通信,要遵守单向数据流,父组件传参给子组件,子组件使用事件通知父元素修改数据,不能子元素修改父元素的数据,要遵守数据谁持有,谁修改
v-show和v-if的区别
- v-show,使用元素的display属性,设置为none来实现隐藏元素
- v-if,条件渲染,将元素直接在DOM树中移除,来实现隐藏元素
- 如果需要频繁显示、隐藏,一般使用v-show,因为v-if会频繁创建、销毁元素,对性能有影响
- 需要身份校验后,根据角色才显示某些按钮,使用v-if,可以避免敏感数据的隐藏
Vue中v-for和v-if,为什么不能一起使用
- 在Vue2中,v-for的优先级高于v-if,所以当2个指令放在同一个标签时,就是每次循环都要先判断,再决定显示或隐藏,会消耗很大的性能
- 解决方案是多套一层div,将
v-if
放在外层,v-for
放在里层,直接进行判断,为true再决定是否开始循环 - Vue3解决了这个问题,将
v-if
优先级高于v-for
Vue的生命周期
- beforeCreate,创建Vue实例前回调
- created,创建Vue实例后回调,此时vue实例身上的data数据还没有处理成响应式变量,所以不能在这里操作data中的数据,一般在这个回调函数中,发出接口请求
- beforeMount,挂载前回调,此时页面还是模板内容,Vue实例的data中的数据,还没有填充到视图,此时还不能操作DOM元素,因为挂载数据的时候,是重新替换视图标签,如果在此时查找DOM元素,并不是最终的DOM树
- mounted,挂载后回调,此时data的数据已经渲染到视图,可以进行查找DOM元素,操作DOM元素等,或者使用Vue提供的refs获取DOM元素
- beforeUpdate,Vue实例的响应式变量准备更新前回调,此时去拿响应式变量的值,还是旧值
- updated,响应式变量更新后,此时获取data中的数据是新值,注意建议不要在该回调中更新响应式变量,容易更新后,又调用updated,产生递归,出现栈溢出
- beforeDestroy,准备销毁前回调,一般在该回调中清除定时器、延时器、取消Ajax请求等异步任务
- destroyed,销毁后回调,在该回调中,Vue会取消data中数据的响应式,解绑DOM元素的事件监听,所以不要在该回调中,再操作响应式变量,是不会更新DOM的
- 如果组件被
keep-alive
标签组件包裹,则该进入、离开该组件时,不会销毁组件,所以上面的生命周期不会重复调用,而是会调用activated
和deactivated
,activated
是组件被激活,也就是页面可见,而deactivated
是页面失活
Vue-Router的钩子函数
- beforeEach,前置守卫,可以进行私有页面的权限判断,例如token为空,拦截掉跳转,跳转到登录页,强制用户登录
Vue-Router的传参方式有哪些
- 查询参数跳转传参
- 动态路由跳转传参
Vue-Router的前置守卫
- 在路由跳转前,执行前置守卫,可以进行私有页面的权限校验,例如判断token是否为空,为空则拦截跳转,跳转到登录页
Vue计算属性与watch的区别
- computed计算属性,每次获取计算属性时,如果计算属性的依赖项没有发生变化,计算属性会使用之前的缓存值,而不是每次都重新计算,提升性能
- watch侦听器(监听器),监听属性值的变化,当变化时,执行handler回调,适用于数据变化时,更新DOM元素
- immediate属性,是否在第一次渲染的时候就执行回调函数
- deep属性,如果需要监听的变量是引用数据类型,要监听它内部的属性变化,就要将该属性设置为true
keep-alive,使用页面跳转后返回数据,原数据还在吗?
- 使用
keep-alive
标签组件,包裹router-view
路由视图组件,能让页面跳转,组件不被销毁,组件的data数据,自然就还在
使用Vuex时,怎么实现数据的持久化
- 在vuex的mutation函数中,调用本地存储进行数据持久化,并在state初始化时从本地存储中获取数据来初始化state
前置守卫的应用场景
- 私有页面的权限拦截,在前置守卫回调中,判断Token是否为空,不为空或白名单页面(注册、登录等),才放行,否则拦截,直接跳转到登录
优化
页面优化思路有哪些?
项目的打包优化
- 路由懒加载,让目标路由页面组件,在第一次加载时,才创建,避免单页面应用首屏加载慢的问题
Axios拦截器
- 请求拦截器
- 请求前,显示Loading的Toast提示,添加token到请求头
- 请求后,隐藏Toast提示
- 响应拦截器
- 响应成功,对数据进行数据剥离,例如减少一层data(axios会给数据加多了一层data)
- 响应失败,根据服务端的响应错误信息,弹出错误的Toast提示
Token失效的处理办法
- 在axios的响应拦截器中,判断HTTP状态码,如果为和服务端约定的状态码,如约定是401,则为token过期,前端删除本地存储的过期token,弹出Toast提示用户当前登录状态已过期,再跳转到登录页面