前端面试题(会持续更新,各位大佬指正)

1.关于反向代理:

     我们的项目里解决跨域的方式用的是反向代理,也是开发环境代理,主要是在vue.config.js里面进行配置.但是我在查询的过程中也了解到,这种一般的话比较常用于开发环境,如果线上环境比较复杂的话,可能会更加麻烦,就需要用到jsonp和cros,但是这两个在我们的项目里并没有用到,所以我也只是略做了一点点了解,比如script标签可以实现跨域访问这一点还蛮让我觉得有趣的.  

// 处理跨域
  devServer: {
    port: "8080",
    proxy: {
      "/api": {
        // target: 'http://ihrm-java.itheima.net',
        target: "http://192.168.21.102:3000",
        changeOrigin: true,
      },
    },
  },

2.表格导出:

登录与注册模块,主要是在注册的时候使用element-ui里的表单组件,实现密码的校验和二次校验,判断两次输入的密码是否一致,以及对密码进行限制,防止用户注册的时候输入一些安全性较弱的密码,在用户选择忘记密码的时候,跳转到找回密码页面,使用手机号验证进行密码的修改,登陆成功之后将token存储到本地,以便之后访问需要权限才能修改的订单页面.在退出登陆的时候清除token并跳转到登陆页面. 

3.关于echarts的使用:

 主要是在后台首页里的一个图表展示,用来展示近一个月订单的图表数据用来作为业务的分析使用.但是因为对数据的要求并不是很苛刻,所以也只是使用了较为简单的饼状图用来呈现,先下载echarts,然后在入口文件里引入,最后是在首页页面里初始化并配置一些相关的属性.xAxis,yAxis,series这三个参数,然后很重要的一点是,必须为图表设置宽高,否则也会有问题.

4.后台管理项目菜单权限怎么做:

1. 项⽬通过⼏个业务模块的配合⽣成对应的权限数据
2. 把后端返回的权限数据和前端本地的路由做对⽐,得到过滤之后的有资格显示的路由数组
3. 调⽤路由核⼼⽅法router.addRoutes把路由数组加⼊到路由系统中
4. 如果需要显示到左侧菜单⾥,可以配合vuex做,vuex存⼀份相同的数据,渲染左侧的菜单

5.按钮的权限怎么做

通过后端返回的权限标识和按钮⾃身的标识code做对⽐,如果有资格就显示,否则就隐藏
这⾥根据思路我们可以封装⼀个全局指令,通过指令可以实现复⽤,就可以在需要控制的按钮身上进⾏ 指令绑定,从⽽控制按钮的显示也隐藏

6.封装⼀些⾃⼰的业务组件 :

先把可复⽤的模板部分写好,然后把可变的需要⾃定义的部分⽤slot插槽占位 要是
插槽个数⽐较多的话,可以区分⼀下默认插槽和具名插槽,尤其是功能区域⽐较明显的部分,⽤具名会 更加清晰

7.前端做数据处理:

主要是在组件和后端返回的数据之间,或者组件产⽣的数据和需要提交给后端的数据之间,有可能会出 现结构对不上,这个时候可能会处理⼀下,举个例⼦,⽐如说我们常⽤的tree型组件要求必须是嵌套的 tree型数组,这个时候就需要处理⼀下,再⽐如提交的时候接⼝要求的是字符串,我们组件给到的数组, 也需要处理

8. 如何处理树形处理

树形数据处理,通常后端那边返回的格式是⼀个普通的平铺结构,不过⼀般都会有⼀个pid字段,可以根 据它找到⾃⼰的⽗节点,那办法就有了,或者使⽤递归的⽅式,性能好⼀点的话可以采⽤⾮递归的⽅式 都可以,主要还是⼀个寻找⽗节点的过程, 一般父级都有一个paid,子级id都等于父级的paid.

9.伪代码思路是什么:

1. 可以先遍历原数组以它的id作为key,本身作为value形成⼀个对象结构
2. 再次遍历原数组,通过每⼀项的pid字段取第⼀步形成的对象中尝试进⾏对象取值,如果取到了值就 把⾃⼰push到它chilren属性中,如果找不到代表它本身就是最外层的⽗节点,直接push到函数最终 产出的数组中

10什么是虚拟dom :

1.保存在内存中的js对象
虚拟dom是相对于浏览器所渲染出来的真实dom而言的,再react,vue等技术出现之前,我们要改变 页面展示的内容只能通过遍历查询dom树的方式找到需要修改的dom然后修改样式行为或者结构,来达到更新UI的目的。
这种方式相当消耗计算资源,因为每次查询dom几乎都要遍历整颗dom树,如果建立一个与dom树对 应的虚拟dom对象,即一个保存dom节点信息,属性和内容的js对象,以对象嵌套的形式来表示dom 树及其层级结构,那么每次dom的更改就变成了对js对象的属性的增删改查,这样一来查找js对象的属 性变化要比查寻dom树更节省性能开销

11.浏览器加载缓慢时怎么办:

1.使用免费cdn加载第三方资源
2.合并压缩js.css,减少请求次数以及减少流量的消耗。
3.代码优化,将js脚本放在</body>标签前,如果js脚本放在html的head标签中,就会阻碍页面呈现。 在网络速度比较慢的情况下会导致“白屏”,直到脚本下载完毕才继续呈现页面,所以js脚本放在页面 底部可以让页面尽快呈现,把样式表放头部。
4.懒加载 对于一些图片,显示首屏的,后面scroll到的时候再加载。
5. 预加载 在浏览器空闲时请求将来可能会用到的页面内容(如图像,样式表和脚本)。这样当用户要 访问下一个页面时,页面中的内容大部分已经加载到缓存中了,因此可以大大改善访问速度。
6.按需加载
模块化开发,只需加载用到的资源。
7.压缩文本和图片
gzip技术可以有效减少页面加载的时间。

12.跨组件传值: 

1 // eventBus技术也叫事件主线技术
2 // 作用:实现非父子关系组件之间的通信,
3 // 本质:通过空的vue实例来做为二者之间通信的桥梁,这个空的vue实例只负责触发、监听和移除事件,也
就是它只负责管理这些事件,
4 // 一,在接收方,在该对象上监听(绑定)一个自定义事件,如bus.$on("自定义事件名",函数体),
5 // 二,在派发方,在该对象上触发同一个自定义事件,如bus.$emit("自定义事件名",传递的参数)
6 // 用法:1.准备一个eventbus,也就是一个空的vue实例
7 // 2.接受方监听,$on
8 // 3.派发放通知,$emit
9 // 4.解绑二者数据通信, $off

13.防抖和节流 

防抖和节流的作用都是防止函数的多次调用。区别是,节流是在固定的时间n秒内触发事件,每隔n秒 触发一次(在n秒内无论出发触少次,都会保证在一定时间内只会执行一次事件处理函数)
防抖当你 在n秒内频繁触发一个事件,只会执行最后一次触发的事件处理函数防抖:是指在事件被触发n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时。一般使用在
一些点击请求的事件上,避免因为用户在很短的时间内多次点击,从而向后端多次发送同一个请求。

 14.null和undefined

1 1. null有自己的数据类型Null,而不属于Object类型,其之所以会被判定为Object类型,是因为js数据类
型在底层都是以二进制的形式表示的,二进制的前三位为0的会被typeof判定为Object类型,而null的二进制
位恰好都是0,所以会被误判为Object,这也算是js的一个bug */
2 // 2. undefined和null的区别
3 // undefined表示一个变量(包括只声明但未定义的变量,对象中不存在的属性,未传实参的形参等)自然
的、最原始的状态值,而null表示一个变量被人为的设置为空对象,而不是原始状态,所以在实际使用中,不
要对一个变量显示的设置为undefined,这并没有什么意义,因为变量不赋值本身就是undefined,当需要释
放一个对象时,直接赋值为null即可
4 // 3. undefined == null
5 // 虽然undefined和null的语义不同,但他们都表示一个无效的值
6 // 但根据javascript高级程序设计一书中讲解判等操作符时提到,比较相等性之前不能将null和
undefined转换成其他任何值,
7 // 如:
8 Number(null) // 0
9 Number(undefined) // NaN
10
11
12 // 使用全等操作符时返回false,因为二者属于不同的数据类型
13 console.log(undefined === null); // false

15. 深拷贝和浅拷贝

1.基本数据类型赋值时赋的是数据,所以并不存在深浅拷贝的问题。
2.引用数据类型赋值(即拷贝时)时,赋的值的在栈空间的引用地址,指向的堆空间中存储的引用类型 的值,这时很明显,既然拷贝前后的两个对象是同一个地址,指向的是同一块内存区域,那么其中一 个变化另一个也会跟随变化,这就是浅拷贝,只拷贝了引用数据类型的第一层数据。
3.浅拷贝和深拷贝是针对复杂数据类型而言的,浅拷贝只复制对象的第一层,深拷贝是对象中的每一层 数据都要拷贝。
4.深拷贝的对象是在内存中重新开辟了空间,与源对象的存储空间并不是同一个,所以他们是互不影响,各自对立的。

16. 浅拷贝的方法:

方法一:使用for...in
var newObj = {}
for (var k in source) {
if(source.hasOwnProperty(k)){
newObj[k] = source[k]
}
}




方法二:object.assign()

17. 深拷贝的方法

方法一:JSON.parse(JSON.stringfy(obj))
该方法实现深拷贝有缺陷:
a: 如果json里面有时间对象,则序列化结果是:时间对象=>字符串的形式
b:如果对象中有函数,undefined,则序列化的结果会把function,undefined丢失
c:如果对象中有NaN,infinity和-infinity,则序列化的结果会变成null
d:如果对象中有对象是由构造函数生成的,则序列化的结果会丢弃对象的constructor等等
方法二:递归

 18.typeof和instanceof都可以用来判断变量的数据类型

1.typeof会返回一个变量的数据类型(返回的是字符串类型的值),一般用其判断基本数据的类型
instanceof返回的是一个布尔值,一般用其判断引用数据的类型,原理是检测构造函数的原型是否存
在在被检测对象的原型链上。
2.typeof的局限:对于数组,null、function、object等特殊对象使用typeof一律返回object字符串,
所以想要使用typeof区分数组,null、function是不行的
判断一个变量是否为数组类型可以使用下面的方法:
a: constructor属性:
var arr = [1,2,3]
arr.constructor === Array
b: instanceof
arr instanceof Array
c: Array.isArray(arr)
d: Object.prototype.toString.call(arr)
⭐补充:Object.prototype.toString.call()的作用是精确判断对象的类型:
Object.prototype.toString.call()其实是利用Object.prototype.toString()方法可以返回“[object
对象构造函数名]”的原理来确定数据类型的,并且js中所有的实例都是Object的实例,可以使用
call/apply调用Object.prototype原型对象的toString()方法
在js中一切皆对象,任何都不例外,它可以对任何类型的数据类型进行精确判断,例如:
console.log(Object.prototype.toString.call('jerry')) // [object String]
console.log(Object.prototype.toString.call(12)) // [object Number]
console.log(Object.prototype.toString.call(true)) // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call({name:'jerry'})) // [object Object]
console.log(Object.prototype.toString.call(function(){})) // [object Function]

19.v-if和v-for不能在同一元素上使用 :

1.vue中不建议将v-if和v-for使用在统一元素上,当vue处理指令时,v-for的优先级要比v-if高,所以
vue会先循环所有数据然后在进行v-if的判断,影响性能和速度,因为每次判断v-if之前都需要将所有数
据进行一次v-for的循环,例如:
<ul>
<li v-for = "item in dataList" :key= item.id v-if="isShow"></li>
</ul>
如上例中,如果在dataList数组中有1000条数据,其中中有10条是isShow状态的,我们只希望显示这
10条,但是在实际渲染时,每一次渲染,这1000条数据都会被遍历一遍。
2.解决:
第一步:使用空标签template(页面渲染时 不生成dom节点),在这一层进行v-if判断,然后在内部进
行v-for的循环,这样在渲染时就会先进性判断再进行循环,如下:
<template v-if="isShow">
<ul>
<li :key= item.id v-for = "item in dataList" ></li>
</ul>
</template>
第二步:使用计算属性computed先将那些不需要显示的项过滤掉,再进行循环渲染
computed:{
dataList(){
return this.list.filter(item =>{
item.isShow = true
})
}

 20.v-for的更新性能为什么高

1.在原生js中如果页面依赖的数据arr数组的元素发生了变化,我们如何更新页面 呢?
重新操作数组,比如说循环遍历将数据重新渲染一次、这种做法会引起页面的重排和重绘,性能很
低。v-for在数组变化时不会将标签全部清除重排重绘,而是尽可能的复用原有标签,只更新变化了的
部分。即当v-for依赖的数据更新时,会在内存中循环出一套新的虚拟dom,再与就的虚拟dom进行对
比,找出变化的部分,尽可能的尝试复用标签就地更新
2.新旧虚拟dom的比较是通过diff算法进行的比较,那diff算法是如何比较新旧虚拟dom的呢?
新旧dom树进行同级比较,如果根元素变化了,则删除重建整颗dom树,如果根元素未变只是属性发
生改变,则当前dom复用,只改变属性
3.v-for加key属性,提高循环更新的性能
key为唯一的字符串或数字类型
新旧虚拟dom进行比较时,如果没有key,会尽可能的标签复用,按顺序更新变化的内容或dom,如
果有key则会根据key来进行diff对比,

 21.computed计算属性:

1.个变量的值要依赖另外一些变量计算得来
2.计算属性的函数第一次执行后返回的值会被缓存,在计算属性的依赖项不变的情况下当页面中多次调
用同一个计算属性时,会从缓存中直接获取,如果依赖项变化了,会重新执行计算属性的函数,再缓
存起来备用。
而声明在methods中的方法,调用多少次执行多少次。
3.计算属性与表单数据双向绑定要使用完整写法:
computed: {
set(val){
log(val)
}
get(){
return '无名氏'
}
}

 22.为什么vue中的data必须是函数:

是为了保证组件的独立性和可复用性,data是一个函数。组件实例化的时候这个函数会返回一个对 象,计算机会为这个对象分配一个内存地址,实例化几次,就会分配几个独立的内存,他们的地址都 不相同,所以每个组件中的数据都不会相互干扰,改变其中任何一个都不会影响其他组件

 23.路由守卫

路由跳转前中后的钩子函数,分为全局路由守卫,组件路由守卫,路由独享守卫
1.全局路由守卫:router.beforeEach(to,from,next()) // 全局前置守 router.afterEach(to,from) // 注意全局后置守卫没有next()参数
2.组件路由守卫:写在每个单独的vue文件中的路由守卫,与methods节点同级 beforeRouteEnter(to,from,next){} // 注意在进入之前,组件实例还未渲染,所以在此路由守卫中无 法通过this获取组 件实例,自能通过vm来访问组件实例 next(vm=> {})
beforeRouteUpdate(to,from,next){}
beforeRouteLeave(to,from,next){}
3.路由独享守卫 在路由配置页面单独给路由配置一个守卫

 24:forEach和map的区别:

1.forEach()没有返回值,但可以通过索引来操作数组中的元素,并能够改变原数组
例:var arr = [1,2,3,4,5]
var res = arr.forEach(function(item,index,arr){
arr[index] = item*10
})
log(res) // undefined
log(arr) // [10,20,30,40,50]
2.map()有返回值,可以return出来,但返回的是一个新数组,并不能影响原数组
var arr = [1,2,3,4,5]
var res = arr.map(function(item,index,arr){
return item*10
})
log(res) // [10,20,30,40,50] 返回一个新数组
log(arr) // [1,2,3,4,5] 原数组不变

 25:git如何解决合并冲突:

当我们开发完成一个分支,并合并到master主分支后需要push到远程仓库时遇到了冲突,如下
1.我们执行远程推送:git push
2.如果别人在自己之前提交了对同一个文件的修改或其他情况,git会提示push失败,需要先pull远程
代码:git pull origin/master(拉去远程仓库的master分支最新代码并合并与本地的master合并)
a: 如果能自动合并,git就会提示 auto merge成功,这时可以直接git push origin master
b: 如果不能自动merge,并报出合并冲突的提示:merge conflict,需要手动解决冲突,通过git
status可以查看冲突的文件,在冲突文件中,git使用
<<<<<<<HEAD 文件1的冲突内容======= 文件2的冲突内容 >>>>>>>来标记不同分支的内容
c.找出冲突内容并手动进行修改后,再进行提交git add. git commit -m '解决了合并冲突'
3.总结:当git无法自动合并分支时,就必须先解决冲突,再进行提合并

 26:数组中不会触发视图更新的两种方法:

1。直接使用索引设置元素,如:
this.array[index] = newVlaue
2. 直接修改数组的长度,例:this.array.length = newLength
3.解决办法:
a : this.$set(this.array,index,newVlaue)或者this.array.splice(index,1,newVlaue)
b: this.array.splice(newLength)

 27:插槽:

  1.1 概念:子组件提供给父组件的占位符。用slot元素来表示 父组件可以在这个占位符里面填充各模板的代码。简而言之,就是子组件留个位置。父组件可可以使用指定的内容来填充。


 1.2 单个插槽,子组件通过<slot></slot>来占位,父组件引入组件,比如:<com></com>在里面直接使用。

1.3具名插槽: 

   格式是:v-solt:插槽的名字  简写的话是     #插槽的名字

子组件

 <template>
 <slot name= "header"  :text="1"  :text2="2"></slot> 
 <slot></slot>
 <slot name = "footer"></slot>
 </template>


 <com>

<template v-slot:header>
    <div>
      插入头部
    </div>
</template>


<template v-slot:footer>
    <div>
      插入尾部
    </div>
</template>  


</com>

1.4作用域插槽

   可以传递数据 父组件中可以接受子组件传递的参数。

   格式为: v-solt:插槽名 = ”props“

<template #header="props">
    <div> text1:{{props.text1}} </div>
    <div> text2:{{props.text2}} </div>
</template>


 1.5动态插槽名

   格式为:v-slot=[插槽名称变量]

   <template #name="props">
    <div> text1:{{props.text1}} </div>
    <div> text2:{{props.text2}} </div>
</template>

28:ES6新增特性

1. let 防止循环遍历变为全局变量 块级作用域、变量不可提升、值可修改

2. const 声明常量 值不可修改 块级作用域 变量不可提升

3. 数组对象结构赋值

4. 剩余参数

5. 扩展运算符,合并数组,伪数组可遍历对象转真数组

6. 数组构造函数方法, Array.from(array, item => item * 2) 可将伪数组变为真数组

数组实例方法: find() 查找第一个符合条件的成员,找到返回,找不到返回 undefinedfind

findIndex() 找到返回索引,找不到返回 -1 includes() 返回布尔值

8. 字符串的新增方法

[ ] 模板字符串 :解析变量,换行,调用函数

[ ] statrtwith() 和 endWith() 返回布尔值 repeat () 将源字符串重复多少次 ‘c’.repeat(2)

set 数据结构, 类似于数组,但成员是唯一的,不可重复,可用于数组去重, const set1 = new

Set(['a','a','b','b']) const arr = [...set1]

9. symbol基本数据类型,表示唯一的标识符,可用来声明对象的唯一属性

29 let、const 以及 var 的区别是什么?


1.let 和 const 定义的变量不会出现变量提升,而 var 定义的变量会提升。

2.let 和 const 是JS中的块级作用域

3.let 和 const 不允许重复声明(会抛出错误)

4.let 和 const 定义的变量在定义语句之前,如果使用会抛出错误(形成了暂时性死区),而 var 不会。

5.const 声明一个只读的常量。一旦声明,常量的值就不能改变(如果声明是一个对象,那么不能改变的是对象的引用地址)

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值