2024前端面试题汇总(记录自用)

最近准备面试了,连刷好多题,但是都在电脑上,放优快云上方便手机随时刷!!!

HTML面试题

 

01.如何让一个盒子在水平方向和垂直方向都居中

方法一:利用文本水平居中text-align: center和行高line-height进行实现,由于对齐方式的不同,需要给子盒子添加vertical-align: middle;

方法二:利用子绝父相和外边距margin实现,子盒子设置绝对定位的四个方向的位移都设为0,然后将外边距margin属性值设置为auto即可。

方法三:利用flex布局实现,父元素中添加display:flex,然后在父元素中添加主轴居中和侧轴居中即可。 

 

02.你知道哪些css3新特性和h5新特性

H5新特性:
1.语义化标签:header、footer,
2.音频视频 :audio、video 

Css3新特性:
背景和边框:border-radius:圆角box-shadow / text-hadow:阴影
2D/3D 转换:2D 转换(transform)
动画、过渡:动画(animation)

 

03.重绘和回流有什么区别

回流:当页面的布局和尺寸发生变化时,浏览器需要重新计算和调整页面元素的位置、大小以及布局关系,这个过程称为回流。
重绘:重绘是指当页面的样式发生改变时,浏览器会对元素的样式进行更新
两者关系:回流会导致重绘,但是重绘不一定会导致回流

 

JS面试题

 

01.冒泡排序

    // 编写方法,实现冒泡
    var arr = [29,45,51,68,72,97];
    //外层循环,控制趟数,每一次找到一个最大值
    for (var i = 0; i < arr.length - 1; i++) {
        // 内层循环,控制比较的次数,并且判断两个数的大小
        for (var j = 0; j < arr.length - 1 - i; j++) {
            // 白话解释:如果前面的数大,放到后面(当然是从小到大的冒泡排序)
            if (arr[j] > arr[j + 1]) {
                var temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
 
    }
    console.log(arr);//[2, 4, 5, 12, 31, 32, 45, 52, 78, 89]

 

02.数组去重

//定义一个有重复数据的数组
let arr = [1,2,1,'j',5,'1',true,2,5,'h',true];
//定义set对象,并用数组arr对其进行初始化
let set = new Set(arr);
//通过Array.from()方法将 set 转化为数组 并赋给新数组
let newArr = Array.from(set);
//打印结果
console.log(newArr);

方法二、
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];
 
function unique(arr) {
  for (var i = 0; i < arr.length; i++) {
    for (var j = i + 1; j < arr.length; j++) {
      if (arr[i] == arr[j]) {
        //如果第一个等同于第二个,splice方法删除第二个
        arr.splice(j, 1);
 
        j--;
      }
    }
  }
  return arr;
}
 
console.log(arr[4],typeof arr[4],arr[6],typeof arr[6]) // true string true boolean
console.log(arr[4]==1,arr[6]==1) // false true
 
// NaN 和 {} 没有去重,且两个 null 直接消失了
// 如果第一个等同于第二个,splice方法删除第二个,所以1跟'1'、true留1,false跟0留false
console.log(unique(arr)); // [1, 2, 'true', 15, false, undefined, NaN, NaN, 'NaN', 'a', {…}, {…}, {…}, {…}, {…}]

03.http状态码:

http是什么:HTTP(HyperText Transfer Protocol) :超文本传输协议。
常见状态码:
1xx:提示信息,表示目前是协议处理的中间状态,还需要后续的操作
2xx:成功,报文已经收到并被正确处理  【200,、204、206】
3xx:重定向,资源位置发生变动,需要客户端重新发送请求 【301、302、304】
4xx:客户端错误,请求报文有误,服务器无法处理    【400、403、404】
5xx:服务器错误,服务器在处理请求时内部发生了错误    【500、501 、502、503 】

04.fon...in和for...of

遍历数组用for of 获取到的是数组的元素,for of不能遍历对象会报错 
遍历对象用for in 获取到的是属性名可以通过 obj[key]的方式获取值 
for in遍历数组获取的是数组下标

 

05.this的指向

 1.独立函数的调用 指向全局对象window(独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用)
2.通过事件调用的函数,this就指向函数的直接调用者
3.call、apply、bind 可以改变this指向

06.检测数据类型

1.typeof 只能准确判断除 null 以外的基本数据类型
    typeof 5          // "number"
2.instanceof 可以用来判断引用数据类型,但是不能正确判断基本数据类型。
3.Object.toString.call()
    Object.prototype.toString.call(arr)

07.数组和字符串的相互转换

1.使用字符串的 split() 方法可以根据指定的分隔符把字符串切分为数组。
2.如果使用数组的 join() 方法,可以把数组元素连接为字符串。

08.filter map foreach区别

1.forEach:会改变原数组,只是增强的for循环,没有返回值
2.map:会返回一个新数组,返回的是把循环的每一项进行操作之后的值,不会改变原数组
3.filter:会返回一个新数组,返回的是符合条件的元素,不会改变原数组

09.set和map

区别:
1.存储的数据类型不同:
set:是一种存储唯一值的集合,它的成员值只能出现一次,不重复
map:是一种键值对的集合,它将键与值相关联。键是唯一的,每个键只能出现一次,但值可以重复。
2.存储方式不同:
set:使用类似于数组的方式存储值,保持插入顺序
map:使用键值对的方式存储数据,可以通过自定义的键来关联和访问值

10.延迟加载js有哪些方式

1.async

2.defer
例如:<script defer src=""></script>   
          <script anync src=""></script>

defer : 等html加载完成,才会执行js代码,顺次执行js脚本
async : 和html解析同步,不是顺次执行js代码,谁先加载完谁先执行(一般引入的js的文件多,并且有主次关系,不能用async)

11.JS数据类型相关的代码题

数据类型:基本类型,引用类型
基本类型:string,number,boolean,undefined,null,symbol,bigInt
引用类型:object

任何基本类型和字符串加时 其他类型都会隐式转为字符串在相加
除了字符串以外的基本类型和数字类型相加时,其他类型都会隐式转为数字类型在相加

console.log(typeof(NaN))   //number
console.log(typeof(undefined))  //undefined
console.log(typeof(null))  // object
console.log(true+1)  // 2    
console.log('name'+true)  //nametrue
console.log(undefined+1)  //NaN
 

12."=="不同类型比较的原理

string == number || boolean ....都会被隐式转换
通过valeOf转换(valueOf方法通常由JS在后台自动调用)

13.undefined和null区别

1.null会被隐式转换为0,不容易发现错误
2.undefined不会 ,undefined转换为NaN
3.null代表了空,undefined标识应该有值,但是实际并没有,声明未赋值
4.数据类型不一样 
5.先有null后有undefined,undefined就是为了弥补null的这个坑
但null == undefined为true

14.js引擎如何实现异步的?

js引擎是通过事件循环(Event Loop)实现异步的

15.微任务和宏任务

首先,js任务分为同步任务和异步任务。异步任务又分为微任务和宏任务,其中异步任务属于耗时的任务
属于宏任务的有:定时器,延时器,ajax
属于微任务的有:Promise,async/await
执行顺序:js在执行代码时,会先执行同步代码,遇到异步任务就会把异步任务放入到异步队列里去,当所有同步代码执行完毕后,再将微任务从队列中调入主线程执行,微任务执行完再执行宏任务,一直循环到所有的任务执行完毕(完成一次时间循环EventLoop)
一次事件循环只能处理一个宏任务,一次事件循环可以将所有的微任务处理完毕。
    

  //同异步坑点
  for(let i = 0;i<3;i++){
     setTimeout(()=>{
        console.log(i);   //因为let在循环中每次输出的i都是独立的,他们不公用  所以隔一秒输出0,1,2
     },1000*i)
  }

   for(var j = 0;j < 3; j ++){
      setTimeout(()=>{
          console.log(j);   //因为var全局可以,所以每次循环i都会变成新值,所以覆盖每隔一秒输出3
       },1000*j)
    }
//-----------------------------想用var打印012的解决方法------------------------------------//
       //利用闭包 1.14
        for(var j = 0;j < 3; j ++){
            (function (j){
                setTimeout(()=>{
                    console.log(j);  
                },1000*j)
            })(j)
        }
//-------------------------------------------------------------------------------------//
事件循环中的微任务,宏任务
微任务:promise.then   (注意 promise里的是同步代码  。then是异步微任务)
宏认为:定时器。。
要执行宏任务前提是清空所有的微任务
整个流程:  同步=>事件循环的微任务=>宏任务=>微任务。。。。。

16.js的垃圾回收机制

两种方式:
1.标记清楚:当变量进入执行环境的时候就会被标记为‘进入环境’,被标记‘进入环境的’的变量是不会被回收的,当变量离开环境时会被标记为‘离开环境’,被标记为离开的变量就会被内存释放

2.引用计数:当变量被复制或者引用时,这个值的引用计数为1,如果变量没有被引用的时候就会被减1.如果计数为0 那么这个变量就会被内存释放

 

17.JS作用域

作用域是指一个变量的作用的范围 
作用域分为 1.全局作用域 2.局部作用域
全局作用域:写在script标签里的js代码都是全局作用域 可以作用与整个网页
局部作用域:作用与函数内部的作用域就是局部作用域,也叫函数作用域,在函数外不可以访问到

题目测试:
(function (){
   var a = b = 10;
})()
console.log(a); //报错
console.log(b); //b=10  因为b没有被var申明  他是全局的变量

3.申明变量如果没写就默认是全局的作用域
4.js有变量提升的定义(变量悬挂申明)  也就是先把var 变量名放作用域最上面,但此时没值
//第5点最重要
5.打印寻找的优先级:变量申明(即赋值) > 函数申明 > 作用域函数的形参 > 变量提升

        function f(x){
            console.log(x); //100
            var x = 200;
            console.log(x) //200
        }
        f(a = 100)   //这里参数a=100  相当于var a   a=100  提升到了window里了
        console.log(a)   //100

18.JS考题

1.对象都是通过new创建出来的,地址是不相等的,所以对象之间不相等
比如 [1,2,3] === [1,2,3]  //false
2.对象的key 都是字符串类型 
        var a = {};
        var b = {key:"b"};
        var c = {key:"c"};
        a[b] = "b";    //在a对象上塞入{key:"b"}这个对象当做属性名  属性值为b
        //有在a对象上塞入{key:"c"}这个对象当做属性名,和上一个塞入的属性名重复了,因此发生覆盖,属性值改为    
        a[c] = "c";                            
        console.log("a[b]",a[b],a);//c
3.对象如何查找某个属性:
         先在本身找==》构造函数中找==》对象本身的原型上找==》构造函数的原型中找==》对象上一层的原型找    
如下:
function Fun(){
    this.a = "2"
}
Fun.prototype = "4";
let obj = new Fun();
obj.a = "1";
obj.__proto__.a = "3"
Object.prototype.a = "5"
console.log(obj.a)  //找的顺序 就是数字的顺序

19.JS判断是否为数组的几个方法

1.Array.isArray(需要判断的数组)

var arr = [1,2]
console.log(Array.isArray(arr))  //true

2.需要判断的数组 instanceof Array

var arr = [1,2]
console.log(arr instanceof Array)  //true

3.通过原型 Object.prototype.toString.call(需要判断的数组)

var arr = [1,2]
console.log(Object.prototype.toString.call(arr))  //返回的是[Object Array]就说明是数组

4.Array.prototype.isPrototypeOf(需要判断的数组)

var arr = [1,2]
console.log(Array.prototype.isPrototypeOf(arr))  //true

20.slice是干嘛的,splice是否会改变原数组

1.slice是截取数组的
不会改变原数组,会创造新数组,参数为开始到结束的下标,结束的下标不取  参数是负数,就从后往前数
var arr = ["a","b","c","d","e"]
var arr2 = arr.slice(1,3)   //参数为开始到结束的下标,结束的下标不取  参数是负数,就从后往前数
console.log(arr2)  // ["b","c"]

2.splice 的功能:插入,删除,替换 
会改变原数组,返回新数组是删除的元素  
第一个参数是开始位置的下标,第二个参数是删除的个数,第三个参数是被删除的下标插入的新东西
var array = ["1","2","3","4","5"]
var array2 = array.splice(1,1)
console.log(array) //["1","3","4","5"]
console.log(array2) //["2"]

21.找出多维数组的最大值

二维数组 输入:[[4,5,1,3],[13,27,18,26],[32,35,37,39],[1,875,1000,1001]]
    要求输出:[5,27,39,1001]

let arr = [[4,5,1,3],[13,27,18,26],[32,35,37,39],[1,875,1000,1001]]
function fnArr(arr){
    let newArr = [];
    arr.forEach((item,index)=>{
        newArr.push(Math.max(...item))
    })
    return newArr;
}
console.log(fnArr(arr))

22.给字符串新增方法实现功能

 String.prototype.addPrefix  = function (value){
            console.log(this)  // 这里的this是String    即{'world'}
            return value+this
      }
console.log(str25.addPrefix('hello'))

 

23.找出字符串中出现最多次数的字符以及次数

 //26 找出字符串中出现最多次数的字符以及次数
        var str26 =  "aaabbbbddddddddddcccccceee"
        //思路1  用map函数  遍历字符串用for of    遍历对象用for in
        //map.get(key) 得到对应key的value值   ;  map.set(key,value); //设置对应key的值为value
        let map = new Map();
        for (let i of str26) {
            if(map.get(i) == undefined){  //说明第一次没有
                map.set(i,1);   //给他设置value值为1
            }else{
                map.set(i,map.get(i)+1)   //  说明已经有了  //给他设置value值+1
            }
        }
        console.log(map,map.size)
        //统计次数
        let arr26 = [...map]
        console.log(arr26)
        let max26 = 0;
        for(let item in arr26){
            if(arr26[item][1]>max26){
                max26=arr26[item][1]
            }
        }
        console.log(max26)


        //思路2
        let obj26 = {};
        for (let j of str26){
            if(obj26[j]){
                obj26[j] ++
            }else {
                obj26[j] = 1
            }
        }
        console.log(obj26)
        //出现的次数
        var max = 0;
        for(let key in obj26){
            if(max<obj26[key]){
                max = obj26[key]
            }
        }
        console.log(max)

24.new操作符具体做了什么!!!

1.创建了空的对象    
2.将空对象的原型,指向于构造函数的原型    即对象的__proto__和构造函数的prototype一样的
3.改变this指向,正常是window  创建对象后this指向该对象  
4.对构造函数返回值判断,如果返回基本类型,则忽略返回值。如果是引用类型,就返回这个引用类型的对象

举例:
        //Foo是构造函数
         function Foo(){
            this.name = "zx";
            return {}
        }
        console.log(new Foo()) //{}
// ---------如果返回基本类型--------------//
       function Foo(){
            this.name = "zx";
            return 111
        }
        console.log(new Foo()) //Foo {name: 'zx'}

25.闭包

1.闭包是什么?
答:闭包是子函数本身+到父函数作用域的链接,父函数的变量不会被垃圾回收
函数在执行完之后,会被js回收机制给回收掉。

2.闭包可以解决的问题(优点)
  2.1 内部函数可以访问外部函数的局部变量,
  2.2 闭包可以解决的问题   
  //下面的代码因为被父函数包着,所以i没有被垃圾回收,这样就可以打印对应的下标(这里涉及到var坑点和同异步1.5)
  //html页面得有几个ul li
  var lis = document.getElementByTagName('li')
  for(var i = 0 ; i<lis.length;i++){
      (function(i){
          lis[i].onclick = function(){
              alert(i)
          }
      })(i)
  }


3.闭包的缺点
  变量会驻留在内存中,造成内存溢出,占用内存
  解决方案:在父函数最后一行,把子函数调用的变量 都给他赋值为null

26.防抖和节流

应用场景:
防抖和节流都是防止某一时间频繁触发,但是原理却不一样。
防抖是将多次执行变为只执行一次,节流是将多次执行变为每隔一段时间执行。
防抖(debounce):
search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
//给输入框绑定事件
           // let input = document.querySelector("input");
            // input.onkeydown = fangdou(function () {
            //     console.log(this.value)
            // },500);
            // //利用闭包写个防抖函数
            // function fangdou(fn,timer) {
            //     let t = null;
            //     return function () {
            //         if (t !== null){
            //             clearTimeout(t)
            //         }
            //         t = setTimeout(()=>{
            //             fn.call(this)
            //         },timer)
            //     }
            // }
节流(throttle):
鼠标不断点击触发,mousedown(单位时间内只触发一次)
 window.onscroll = jieliu(function () {
            console.log("我是李帅帅")
        }, 500)

        function jieliu(fn, timer) {
            let flag = true;
            return function () {
                if (flag) {
                    setTimeout(() => {
                        fn.call(this);
                        flag = true;
                    }, timer)
                }
                flag = false;
            }
        }

27.原型链

1.原型解决的问题
答:对象共享属性,共享方法

2.谁有原型?
构造函数拥有:prototype
new的对象拥有:__proto__

3.对象查找属性的顺序:
对象本身的属性==》构造函数中的属性查找(this.)==》对象的原型(也是构造函数中的原型)==》当前原型的原型

4.原型链
  4.1 原型链就是把原型串联起来
  4.2 原型的最顶端是null
  

28.JS继承有哪些方式

方式一:ES6继承

         class Parent{
            constructor(){
                this.age = 18;
            }
        }
        class Child extends Parent{
            constructor(){
                super();
                this.name = 'ironMan';
            }
        }
        let c1 = new Child()
        console.log(c1.name,c1.age)

方式二:原型继承

         function Parent(){
            this.age = 20;
        }
        function Child(){
            this.name = 'ironMan';
        }
        Child.prototype = new Parent() //把Parent放入Child的原型中
        let c1 = new Child()
        console.log(c1.name,c1.age)

28方式三:改变this指向

          function Parent(){
            this.age = 20;
        }
        function Child(){
            Parent.call(this)  //这里把Parent的this的指向改变成Child
            this.name = '张鑫';
        }

        let c1 = new Child()
        console.log(c1.name,c1.age)

29.说一下call,apply,bind的区别

1.共同点
  可以改变this的指向 ,语法都是  函数.call()  函数.apply()  函数.bind()

2.不同点
  区别1:call,apply会立即执行,bind不会立即执行,它会返回一个函数
  区别2:参数不同,call和bind直接放入对应参数,apply是一个数组
    
3.使用场景
    3.1 apply使用场景
        //找出数组里的最大值
        var arr30 = [1,2,3,4,5,777];
        //方法1 解构运算
        console.log(Math.max(...arr30)); //Math.max()参数不能放数组,可以解构数组再放进去
        //方法2 apply的参数机制
        console.log(Math.max.apply(window,arr30))
    3.2 bind使用场景(需要点击才能启动,而不是立即执行的场景 所以bind场景更多元)
        //绑定点击事件时 改变this指向
         <button type="button" id="btn">点下查看id</button>
        <span id="spanDom">222</span>
        let  btn = document.getElementById("btn");
        let spanDom = document.getElementById("spanDom");
        btn.onclick = function (){
            console.log(this.id); //不加bind显示btn  加了显示spanDom
        }.bind(spanDom)


//-------------------------------代码如下---------------------------------------------//
        var str = "这是window内的str";
       var obj =  {str:"这是obj对象内的str"}
       function fun(name,age){
           this.name = name
           this.age = age
           console.log(this,this.str)
       }
       // fun.call(obj,'ironMan',20);     //call会立即执行 原函数需要的参数放入即可
       // fun.apply( obj,['ironMan',20] ) //apply会立即执行,第二个参数为数组,原函数需要的参数放入数组中
       // fun.bind( obj ,'ironMan',20)()  //bind不会立即执行它会返回一个函数  原函数需要的参数放入即可
       fun()

30.sort背后的原理

sort是很根据字符编码的顺序进行排序的,数字<小写字母
之前的版本:插入排序和快排,现在是冒泡排序

31.JS深拷贝与浅拷贝

深拷贝不会的值修改不会影响原数据,浅拷贝会影响
基本数据类型的赋值都是深拷贝
深拷贝方法:JSON.parse(JSON.stringly(obj)),递归
浅拷贝方法:...扩展运算符,Object.assign(),Array.from()


      //32 封装函数  利用递归的思想实现深拷贝
        let obj32 = {
                a:"1",
                b:"2",
                c:[1,2,3,4]
            }
        function copyObj(obj){
            let newObj32
            //判断里面的属性是否为数组  不然全改成对象了
            if( Array.isArray(obj) ){
                 newObj32 = [];
            }else {
                 newObj32 = {};
            }
            for(let key in obj){
                if(typeof obj[key] == "object"){
                    newObj32[key] = copyObj(obj[key])
                }else {
                    newObj32[key] =obj[key]
                }
            }
            return newObj32
        }
        let nowObj32 = copyObj(obj32)
        nowObj32.a = "3"
        console.log(obj32,nowObj32)

32.localStorage,sessionStorage,cookie的区别

注意他们都可以在f12的Application查看
相同点:
1、都是客户端存储技术
2、都是键值对的形式存储数据
3、都是同源策略受限,只能访问同一域名下的存储数据
同源策略:协议、域名、端口号都相同
不同点:
1、localStorage:是持久化存储                             存储大小:>=5M
2、sessionStorage:是会话存储,即浏览器关闭之后就没了       存储大小:最多5M
3、cookie:可以设置有效时间                              存储大小:一般为几KB
localStorage、sessionStorage仅在客户端存储,不会随http请求发送到服务器
cookie会随每个http请求发送到服务器,可能影响性能
用途:
sessionStorage:主要用于长期保存在本地的数据,例如用户偏好设置、用户登录信息等
sessionStorage:主要用于在同一浏览器标签页的会话期间保存数据,当用户关闭标签页时数据会被清除。
cookie:主要用于在客户端和服务器之间传递信息,例如用户身份认证、跟踪用户行为等。

JQuery面试题

1.jquery选择器


1.基本选择器:$("#id") $(".class")
2.属性选择器:["type=input"] ["name=xxx"]
3.表单选择器::input
2.jquery绑定事件:
3.$("#id").click(function(){
    //表达式
})

2.如何将一个 HTML 元素添加到 DOM 树中:


append() //$("p").append("<b>你好</b>")向每个匹配的元素内部追加内容 
appendTo() //$("<b>你好</b>").appendTo("p")将所有匹配的元素追加到指定的元素中
prepend() //$("p").prepend("<b>你好</b>") 向每个匹配的元素内部前置添加内容
after() //$("p").after("<b>你好</b>")在每个匹配元素之后插入内容
before() //$("p").before("<b>你好</b>")在每个匹配的元素之前插入内容

3.$(this) 和 this 关键字在 jQuery 中有何不同?:


$(this) 返回一个 jQuery 对象,你可以对它调用多个 jQuery 方法,比如用 text() 获取文本,用val() 获取值等等。而 this 代表当前元素

4.你如何使用jQuery设置一个属性值? :


 attr()方法 attr(name, value), 这里name是属性的名称,value是属性的新值。

5.jQuery中 detach() 和 remove() 方法的区别是什么?:


 共同点是都可以移除DOM元素, 不同是detach() 会保持对过去被解除元素的跟踪, 因此它可以被取消解除, 而 remove() 方法则会保持过去被移除对象的引用

6.你如何利用jQuery来向一个元素中添加和移除CSS类? :


addClass() 和 removeClass() 这两个 jQuery 方法
 //$("#myElement").addClass("myClass");

7.jQuery.get() 和 jQuery.ajax() 方法之间的区别是什么?:


    ajax() 方法更强大,更具可配置性, 让你可以指定等待多久,以及如何处理错误。get() 方法是一个只获取一些数据的专门化方法。

8.jQuery有哪些好处:


轻量级、兼容性好、DOM 操作的封装

9.jquery怎么移除标签onclick属性:


获得a标签的onclick属性:$(“a”).attr(“onclick”)
删除onclick属性: $(“a”).removeAttr(“onclick”)
设置onclick属性: $(“a”).attr(“onclick”,“test();”)

10.请使用jQuery将页面上的所有元素边框设置为2px宽的虚线?:


  $("*").css("border", "2px dotted red"); 

11.如何用jQuery禁用浏览器的前进后退按钮?:


 window.history.forward(1);
 window.history.forward(-1);

12.如何使用 jQuery 遍历一个数组,并将每个元素添加到一个 <ul> 列表中?:


    $.each()
    $.each(array, function(index, value) {
      $("#list").append("<li>" + value + "</li>");
    });

13.如何使用 jQuery 发送一个 AJAX POST 请求,并在成功时执行回调函数?:


$.ajax({
  url: "your-url",
  type: "POST",
  data: yourData,
  success: function(response) {
    // 在请求成功时执行的代码
  },
  error: function(xhr, status, error) {
    // 在请求失败时执行的代码
  }
});

ES6面试题

 

01.ES6新特性

1,新增声明变量 let  const  
//var支持变量提升 let不支持  let只能声明一次 var可以重新声明 let声明的变量可以改变,值和类型都可以改变,没有限制。 const 声明的值不能改变

2,模板字符串
3,...拓展运算符
4,箭头函数
//箭头函数this是静态的。
//箭头函数内的this指向上层对象;始终指向函数声明时所在作用域下的this的值,无法被call改变
5,symbol
//ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值
6,Set  Map
7.class 类
8,结构赋值

02.var,let,const的区别

共同点:他们都可以申明变量
不同点
1.var具有变量提升机制,let和const没有变量提升机制
2.var可以多次申明同一个变量,let和const不可以多次申明同一变量
3.var和let是申明变量的,const是申明常量的
4.var和let申明的变量可以再次被赋值,但const不可以再次赋值

03.var,let,const的作用域考题

          //考题一
        console.log(str)
        var str = "你好"; //var存在变量提升 所以是undefined

        console.log(num) //报错 ,const同理
        let num = 10;   

        //考题二
        function demo(){
            let n = 2;
            if(true){
                let n = 1
            }
            console.log(n)  //2 拿不到if里面的作用域
        }
        demo()

        //考题三
        const obj = {
            a:1
        }
        //obj = 123;//直接报错
        obj.a = 11111;//里面的属性是可以改的
        console.log(obj)

04.合并对象

 let obj331 = Object.assign(a33,b33)  //后面的相同属性覆盖前面的相同属性
        console.log(obj331)
        //方法2  ES6扩展运算符
        let obj332 = {...a33,...b33}   //后面的相同属性覆盖前面的相同属性
        console.log(obj332)

05.将对象转为数组或者JSON数组

     //把对象变成数组和变成JSON数组
        let obj = {'未完成':5, '已完成':8, '待确认':4, '已取消':6};
        
     1.   //变成数组
         var arr1 = []
         for (let i in obj) {
             console.log(i)
             arr1.push(obj[i]);
         }
         console.log(arr1);

2.Object.values(obj)


        //变成JSON数组
        var arr2 = []
        for (let i in obj) {
            let o = {};
            o[i] = obj[i];
            arr.push(o)
        }
        console.log(arr2);

06.箭头函数与普通函数的区别

1.箭头函数的this只在箭头函数定义的时候决定好了,不可以通过call,apply,bind修改,
//------------注:其实箭头函数没有this,他里面的this是外层第一个普通函数的this-------------------//
2.箭头函数不能new(不能当做构造函数)
3.箭头函数是没有prototype原型的
4.箭头函数没有arguments对象(关于arguments https://www.cnblogs.com/zhangruiqi/p/7861210.html)

07.Promise的几种状态

1.三种状态:
pending(进行中);
fulfilled(已成功);
rejected(已失败);
2.Promise是什么:
解决回调地狱问题,但一直.then 也不利于维护,现在最简洁的写法是async,await不需要.then了
3.关于promise的一些编程以及async和await

08.find和filter的区别

答:返回值不同
filter:返回一个新数组,原数组不变
find:返回具体的内容,只返回符合条件的第一个数据,原数组不变    

09.前端开发中如何解决跨域

什么是跨域:跨域问题指的是在浏览器中,通过 JavaScript四 发起的 HTTP 请求受到了同源策略的限制。同源策略要求两个页面具有相同的协议、域名和端口号,否则就被视为跨域请求。

解决方法:
1.JSONP:利用script标签可跨域的特点,在跨域脚本中可以直接回调当前脚本的函数。
jsonp缺点:只能实现get一种请求
2.CORS:服务器设置HTTP响应头中Access-Control-Allow-Origin值,解除跨域限制。
但是这两个跨域方案都存在一个致命的缺陷,严重依赖后端的协助。

前端独立完成跨域: 
1、配置代理服务器,也称正向代理,是指一个位于客户端和目标服务器(target server)之间的服务器,为了从目标服务器取得内容,客户端向代理发送一个请求并指定目标(目标服务器),然后代理向目标服务器转交请求并将获得的内容返回给客户端。
在webpack.config.js中配置:
devServer:{
    proxy:[{
         target: 'http://www.demo2.com:8080',代理跨域目标接口
    }]
}
数据流程:
数据请求过程:浏览器-》代理服务器-》目标服务器
数据返回过程:目标服务器-》代理服务器-》浏览器

2、WebSocket协议跨域
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。 原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

10.深拷贝使用JSON转的缺点

弊端:
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;

VUE中axios相关问题

01.axios是什么

axios 是一个轻量的 HTTP客户端

  • Axios 是一个基于 promise异步 ajax 请求库,就是可以发送get、post请求,负责与后端交互

 

02.特性

  • 支持 Promise 所有API

  • 拦截请求和响应(就是前端发送请求前,可以设置自动拦截请求,相当于给请求加条件);

  • 转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据;

  • 安全性更高,客户端支持防御 XSRF跨站请求伪造**)-> 就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略;

03.请求方式

axios({        
  url:'xxx',    // 设置请求的地址
  method:"GET", // 设置请求方法
  params:{      // get请求使用params进行参数凭借,如果是post请求用data
    type: '',
    page: 1
  }
}).then(res => {  
  // res为后端返回的数据
  console.log(res);   
})

04.并发请求axios.all([])

//把axios请求封装到函数中,通过axios.all的方式请求,参数是数组,里面是封装的函数,通过,then按顺序返回
function getUserAccount() {
    return axios.get('/user/12345');
}

function getUserPermissions() {
    return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
    .then(axios.spread(function (res1, res2) { 
    // res1第一个请求的返回的内容,res2第二个请求返回的内容
    // 两个请求都执行完成才会执行
}));

2.如果是将所有的异步请求但要返回所有值

则用自己的方法放到一个数组中,然后使用promise All ( [ ] ) 返回的就是所有的结果

        /*
        * Function applyProportion是一个异步的请求
        * 需求:同时拿到三年的数据(params:[2018, 2019, 2020]);
        */
        const initData = async (params) => {
            //promiseArr中为三个Promise异步请求
            const promiseArr = params.map(item => {
                return applyProportion({ year: item });
            });
            try {
                // 同时发送三个请求,结果res为一个Array [res1, res2, res3],根据自己的需求用res
                const res = await Promise.all(promiseArr); 
            } catch (error) {
                console.log(error)
            }
        }

05.为什么要封装

可以更方便的管理和修改axios请求域名,假设是一个庞大的项目,只是修改域名就会花费很多的时间和精力,如果对axios进行封装,便能省去了很多的步骤

比如:

axios('http://localhost:3000/data', {
  // 配置代码
  method: 'GET',
  timeout: 1000,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'xxx',
  },
  transformRequest: [function (data, headers) {
    return data;
  }],
  // 其他请求配置...
})
.then((data) => {
  // todo: 真正业务逻辑代码
  console.log(data);
}, (err) => {
  // 错误处理代码  
  if (err.response.status === 401) {
  // handle authorization error
  }
  if (err.response.status === 403) {
  // handle server forbidden error
  }
  // 其他错误处理.....
  console.log(err);
});

如果每个页面都发送类似的请求,都要写一堆的配置与错误处理,就显得过于繁琐了

这时候我们就需要对axios进行二次封装,让使用更为便利

 

06.如何封装?

封装的同时,你需要和 后端协商好一些约定,请求头,状态码,请求超时时间.......

设置接口请求前缀:根据开发、测试、生产环境的不同,前缀需要加以区分

请求头 : 来实现一些具体的业务,必须携带一些参数才可以请求(例如:会员业务)

状态码: 根据接口返回的不同status , 来执行不同的业务,这块需要和后端约定好

请求方法:根据getpost等方法进行一个再次封装,使用起来更为方便

请求拦截器: 根据请求的请求头设定,来决定哪些请求可以访问

响应拦截器: 这块就是根据 后端`返回来的状态码判定执行不同业务

07.设置接口请求前缀

利用node环境变量来作判断,用来区分开发、测试、生产环境

if (process.env.NODE_ENV === 'development') {  //本地开发
  axios.defaults.baseURL = 'http://dev.xxx.com'
} else if (process.env.NODE_ENV === 'production') { //线上
  axios.defaults.baseURL = 'http://prod.xxx.com'
}

在本地调试的时候,还需要在vue.config.js文件中配置devServer实现代理转发,从而实现跨域

devServer: {
    proxy: {
      '/proxyApi': {
        target: 'http://dev.xxx.com',
        changeOrigin: true,
        pathRewrite: {
          '/proxyApi': ''
        }
      }
    }
  }

08.设置请求头与超时时间

大部分情况下,请求头都是固定的,只有少部分情况下,会需要一些特殊的请求头,这里将普适性的请求头作为基础配置。当需要特殊请求头时,将特殊请求头作为参数传入,覆盖基础配置

const service = axios.create({
    ...
    timeout: 30000,  // 请求 30s 超时
      headers: {
        get: {
          'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
          // 在开发中,一般还需要单点登录或者其他功能的通用请求头,可以一并配置进来
        },
        post: {
          'Content-Type': 'application/json;charset=utf-8'
          // 在开发中,一般还需要单点登录或者其他功能的通用请求头,可以一并配置进来
        }
  },
})

09.封装请求方法

先引入封装好的方法,在要调用的接口重新封装成一个方法暴露出去

// get 请求
export function httpGet({
  url,
  params = {}
}) {
  return new Promise((resolve, reject) => {
    axios.get(url, {
      params
    }).then((res) => {
      resolve(res.data)
    }).catch(err => {
      reject(err)
    })
  })
}

// post
// post请求
export function httpPost({
  url,
  data = {},
  params = {}
}) {
  return new Promise((resolve, reject) => {
    axios({
      url,
      method: 'post',
      transformRequest: [function (data) {
        let ret = ''
        for (let it in data) {
          ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
        }
        return ret
      }],
      // 发送的数据
      data,
      // url参数
      params

    }).then(res => {
      resolve(res.data)
    })
  })
}

把封装的方法放在一个api.js文件中

import { httpGet, httpPost } from './http'
export const getorglist = (params = {}) => httpGet({ url: 'apps/api/org/list', params })

页面中就能直接调用

// .vue
import { getorglist } from '@/assets/js/api'

getorglist({ id: 200 }).then(res => {
  console.log(res)
})

这样可以把api统一管理起来,以后维护修改只需要在api.js文件操作即可

10.请求拦截器请

请求拦截器可以在每个请求里加上token,做了统一处理后维护起来也方便

// 请求拦截器
axios.interceptors.request.use(
  config => {
    // 每次发送请求之前判断是否存在token
    // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况,此处token一般是用户完成登录后储存到localstorage里的
    token && (config.headers.Authorization = token)
    return config
  },
  error => {
    return Promise.error(error)
  })

11.响应拦截器

 

 

响应拦截器可以在接收到响应后先做一层操作,如根据状态码判断登录状态、授权

// 响应拦截器
axios.interceptors.response.use(response => {
  // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
  // 否则的话抛出错误
  if (response.status === 200) {
    if (response.data.code === 511) {
      // 未授权调取授权接口
    } else if (response.data.code === 510) {
      // 未登录跳转登录页
    } else {
      return Promise.resolve(response)
    }
  } else {
    return Promise.reject(response)
  }
}, error => {
  // 我们可以在这里对异常状态作统一处理
  if (error.response.status) {
    // 处理请求失败的情况
    // 对不同返回码对相应处理
    return Promise.reject(error.response)
  }
})

VUE面试题

01.VUE和REACT的一些区别

1.相同点
都有组件化思想
都支持服务器端渲染
都有虚拟dom
数据驱动视图
都有支持native的方案:Vue的weex、React的React native
都有自己的构建工具:Vue的vue-cli、React的Create React App


2.区别
数据流向的不同。react从诞生开始就推崇单向数据流,而Vue是双向数据流
数据变化的实现原理不同。react使用的是不可变数据,而Vue使用的是可变的数据
组件化通信的不同。react中我们通过使用回调函数来进行通信的,而Vue中子组件向父组件传递消息有两种方式:事件和回调函数

02.谈谈你对MVVM的理解

MVVM是一种架构模式(设计模式),它是`Model`,`View`,`ViewModel`的首字母简写

1. Model:数据层,提供数据
2. View:视图层,渲染页面,响应页面的交互行为
3. ViewModel:视图模型层,它是数据层和视图层交互数据的桥梁(连接数据层和视图层的桥梁)
MVVM的核心:
实现了数据的双向绑定,数据驱动视图,当数据发生改变时,会自动更新视图,不需要再操作DOM节点,当视图中的数据发生改变时,会自动更新数据

03.vue数据双向绑定原理

  原理:Vue中采用了数据劫持的方式,通过Object.defineProperty()函数来监听数据变化,并在数据变化时触发对应的更新函数。
vue组件中data里面的数据,在初始渲染之前都会被Object.defineProperty转化成响应式数据,然后这些数据的访问和修改就能够被监听了,每当数据被访问的时候就会触发getter,接着会通知watcher,watcher这是会做一次依赖的收集或者更新,然后watcher会负责通知渲染界面(先转化为虚拟DOM,在映射为真正的DOM)。每当数据被设置的时候会触发setter,接着setter也会通知watcher,watcher在去通知渲染界面
Object.defineProperty()与Proxy的区别:前者只能监听属性的读取和修改,后者可以监听数组的变化等更多场景,且性能更高。

04.vue的性能优化有哪些

1.避免v-if和v-for在同一元素上使用
2.使用 keep-alive 缓存组件
3.保证 key 值的唯一性
4.按需加载路由和组件
5.延迟加载图片

05.vue使用了哪些设计模式

1.观察者模式:Vue 使用观察者模式来实现数据的响应式,当数据发生变化时,相关的视图会自动更新。
2.发布订阅模式:Vue 的事件系统基于发布-订阅模式,允许组件之间进行解耦,通过订阅和发布事件来通信。
3.组件模式:Vue 的核心思想之一是组件化开发,将页面拆分成独立的组件,每个组件负责自己的一部分功能,便于复用和维护。
 

06.Vue中如何检测数组的变化

可以使用Vue.set(target, key, value) 或 vm.$set(target, key, value) 来监测数组变化
可以更新视图的操作数组的方法:push()、pop()、shift()、unshift()、splice()、sort() 和 reverse()。这些方法都是基于数组原型链上的方法,Vue通过重写这些方法来监测数组变化。

07.vue项目优化

1.v-if 和 v-show 区分使用场景
2.computed 和 watch 区分使用场景
3.v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
4.长列表性能优化
5.事件的销毁
6.图片资源懒加载
7.路由懒加载
8.第三方插件的按需引入
9.优化无限列表性能
10.服务端渲染 SSR or 预渲染
11.骨架屏
12.数据懒加载

08.webpack层面的优化

1.Webpack 对图片进行压缩
2.减少 ES6 转为 ES5 的冗余代码
3.提取公共代码
4.模板预编译
5.提取组件的 CSS
6.优化 SourceMap
7.构建结果输出分析
8.Vue 项目的编译优化


基础的 Web 技术的优化
1.开启 gzip 压缩
2.浏览器缓存
3.CDN 的使用
4.使用 Chrome Performance 查找性能瓶颈

09.v-if和v-for哪个优先级更高?

实践中不应该把v-for和v-if放一起
v-for优先级高于v-if
如果一起使用,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费
如果要一起用,可以用个盒子加v-if 把v-for包裹起来

10.Vue要做权限管理该怎么做?控制到按钮级别的权限怎么做?

前端权限控制可以分为四个方面:
1.接口权限
2.按钮权限
3.菜单权限
4.路由权限

接口权限:
1.接口权限目前一般采用jwt的形式来验证,没有通过的话一般返回401,跳转到登录页面重新进行登录
2.登录完拿到token,将token存起来,通过axios请求拦截器进行拦截,每次请求的时候头部携带token

路由权限控制:
1.初始化即挂载全部路由,并且在路由上标记相应的权限信息,每次路由跳转前做校验
菜单权限:
菜单与路由分离,菜单由后端返回

按钮权限:
按钮权限也可以用v-if判断
但是如果页面过多,每个页面页面都要获取用户权限role和路由表里的meta.btnPermissions,然后再做判断
 

11.从0到1自己构架一个vue项目,说说有哪些步骤、哪些重要插件、目录结构你会怎么组织

构建项目,创建项目基本结构
引入必要的插件:
代码规范:prettier,eslint
提交规范:husky,lint-staged
其他常用:svg-loader,vueuse,nprogress
常见目录结构

12.$attrs是为了解决什么问题出现的?

主要作用是为了实现批量传递数据。

13.Vue的普通Slot以及作用域Slot的区别

插槽分三种:默认插槽,具名插槽,作用域插槽
vue中的插槽,指的是子组件中提供给父组件使用的一个占位符;
用标签表示,父组件可以在这个占位符中填充任何模板代码,比如HTML、组件等,填充的内容会替换掉子组件的标签(替换占位符)。
普通插槽:
普通插槽是渲染后做替换的工作。父组件渲染完毕后,替换子组件的内容。
作用域插槽:
作用域插槽可以拿到子组件里面的属性。在子组件中传入属性然后渲染。

14.如何优化SPA应用的首屏加载速度慢的问题?

1.将公用的JS库通过script标签外部引入,减小 app.bundel 的大小,让浏览器并行下载资源文件,提高下载速度;
2.在配置 路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundel 的体积,在调用某个组件时再加载对应的js文件;
3.加一个首屏loading图,提升用户体验;

15.什么是diff算法?

Diff算法是一种对比算法,主要是对比旧的虚拟DOM和新的虚拟DOM,找出发生更改的节点,并只更新这些接地那,而不更新未发生变化的节点,从而准确的更新DOM,减少操作真实DOM的次数,提高性能。

16.生命周期

1.正常情况下:Vue生命周期总共可以分为8个阶段:创建前后, 载入前后,更新前后,销毁前销毁后
beforeCreate    组件实例被创建之初
created            组件实例已经完全创建      
beforeMount        组件挂载之前
mounted            组件挂载到实例上去之后
beforeUpdate    组件数据发生变化,更新之前
updated            组件数据更新之后
beforeDestroy    组件实例销毁之前
destroyed        组件实例销毁之后

2.在哪个阶段有$el(绑定节点),$data(数据)
created 有data
beforeMount 有el

3.如果加了keep-alive后  每次重新进入组件只会加入1个生命周期(需要改变的数据要放到activated钩子函数中)
activated        keep-alive 缓存的组件激活时

4.父子组件的加载流程
加载渲染过程
->父beforeCreate -> 父created -> 父beforeMount ->子beforeCreate -> 子created 
-> 子beforeMount -> 子mounted-> 父mounted

子组件更新过程
->父beforeUpdate-> 子beforeUpdate -> 子updated-> 父updated

父组件更新过程
父beforeUpdate -> 父updated

销毁过程
-> 父beforeDestroy-> 子beforeDestroy -> 子destroyed-> 父destroyed

5.其他钩子
deactivated        keep-alive 缓存的组件停用时调用
errorCaptured    捕获一个来自子孙组件的错误时被调用

17.谈谈对keep-alive的了解

1.是什么:
vue系统自带的一个组件,是来缓存组件。可以提升性能
就是用来缓存组件,提升项目性能。具体实现如下:首页进入到详情页,如果在首页每次点击都是相同的,那么详情页就没必要请求,直接缓存即可,如果不是同一个,那么就需要判断在请求

18.v-show和v-if的区别

1.共同点:都可以控制元素的显示与隐藏
2.不同点:
v-show:是通过css display:none 来隐藏元素,适合频繁切换显示与隐藏的元素
v-if:是直接在DOM中删除元素,适合不频繁切换的组件,因为频繁的添加和删除DOM会影响性能

19.如何让CSS只在当前组件起作用

1.正常情况下:将当前组件的<style>修改为<style scoped>即可

2.如果引入scss  
     1.安装scss   npm i sass-loader node-sass --save
     2.<style lang='scss' scoped>即可

3.scss样式穿透
    父元素类名/deep/子元素类名

20.v-model的使用和绑定事件

1.v-model是什么?
        v-model是Vue框架的一种内置的API指令,本质是一种语法糖写法。
        v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定
2.原理:
        v-bind绑定value属性的值;
        v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;

3.事件绑定:
        用@事件名
        但在移动端的click事件有300ms的延迟。解决问题可以引入fastClick
          1.npm i fastclick
          2.在main.js引入   import FastClick from 'fastclick'
          3.FastClick.attach(document.body)

21.Vue-loader

1.可以将vue组件解析成js模块

22.NextTick是做什么的

1.定义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
2.场景:需要在视图更新之后,基于新的视图操作。比如:按钮改变数据的同时获得标签的数据(如果不加这个NextTick,点击的第一次,标签数据内容还是旧的数据)

23.跨域代理!!!多用

原因:浏览器传数据有跨域协议(协议http,地址localhost,端口号5000)三样东西要相同,ajax传数据时,服务器响应到数据了,返回值时被浏览器给卡住了

解决方法:
(1)cors   后端设置,可以设置响应头,这样就可以获取数据,缺点这样导致任何人都可以获得数据,不安全
(2)JSONP  只解决get请求,用处不大
前两个标签都需要后端配合


(3)配置代理服务器
     代理服务器的协议地址路径和本浏览器位置相同,但他是服务器,没有浏览器的跨域协议,相当于中介
      在vue中在vue.config.js配置
    module.exports = {
        pages:{
            index:{
                //入口
                entry:'src/main.js'
            }
        },
        lintOnSave:false,//语法检查
        //开启代理服务器  proxy的属性值写目标服务器地址(方式1)
        devServer: {
            proxy: 'http://localhost:3000'
        }
    }
注意:写完之后重启脚手架,之后的接口都写本地端口号,优先寻找8080本地资源,没有再去3000服务器寻找,无法配置多个代理

(4)!!!!!方式二的写法 在请求接口时需要把简写后的代理放到代理服务器端口号之后
        module.exports = {
            pages:{
                index:{
                    //入口
                    entry:'src/main.js'
                }
            },
            lintOnSave:false,//语法检查
            //开启代理服务器  proxy的属性值写目标服务器地址(方式2)
            devServer: {
                proxy: {
                    //把http://localhost:3000的路径简写为/smileZX放在代理服务器端口号之后即可
                    '/smileZX1': {
                        target: 'http://localhost:3000',
                        pathRewrite:{"^/smileZX1":''},   //正则把 /smileZX1 给她转为空字符串 方便后端找不到路径
                        ws: true,                       //用于支持websocket
                        changeOrigin: true              //请求来源是否撒谎,默认撒谎同服务器
                    },
                    '/smileZX2': {
                        target: 'http://localhost:3001',
                        pathRewrite:{"^/smileZX2":''},
                        ws: true,
                        changeOrigin: true
                    }
                }
            }
        }

24.ref是什么

是用来获取DOM的 在标签内设置ref属性  调取时直接this.$refs.对应的属性名  就可以获得DOM对象

25.Vue组件通信

(1)父传子组件(自定义属性):
    父组件在子组件标签通过自定义属性,子组件用props对象去接 
    
(2)子传父组件(自定义事件):
    子组件用$emit传给父组件,父组件需要在子组件标签绑定自定义事件    

(3)兄弟组件(全局事件总线):
    通过中转bus传值,预先在公共目录设置好bus.js 
    A兄弟传值:
     import bus from '/common/bus'
     bus.$emit("事件名称",需要传的值)
    B兄弟收值:
     import bus from '/common/bus'
     bus.$on("事件名称",(res)=>{把res赋值就可以了})

26.computed,watch有什么区别

1.computed:计算属性,computed会利用缓存机制,只有数据修改的时候才会变化,不支持异步
2.watch:监听属性 监听data中的数据也可以监听路由 可以放两个参数 第一个是新的值,第二个是旧的值,可以发送异步请求

27.单页面应用的优缺点

单页面应用:SPA
    单页面的应用就是一个html页面
    优点:
        (1)用户体验好,内容的改变不需要重新加载,对服务器压力小
        (2)前后端分离
        (3)在组件切换的时候 可以做到一些好看的动画
    缺点:
        (1)不利于seo(搜索引擎优化)
        (2)导航不可用,需要自己实现前进,后退
        (3)初次加载耗时多
        (4)页面复杂度提高
        
多页面应用:MPA
    多页面就是多个html页面
    

28.路由跳转方式

1.  router-link 标签跳转
2.  this.$router.push事件跳转
vue-router 中有hash模式和history模式。
hash
即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。
比如这个 URL:http://www.baidu.com/#/home,hash 的值为 #/home。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

history
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法(需要特定浏览器支持),用来完成 URL 跳转而无须重新加载页面,不过这种模式还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,就需要配置404页面。

区别
通过history api,我们丢掉了丑陋的#,但是它也有个问题:不怕前进,不怕后退,就怕刷新,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的。
在hash模式下,前端路由修改的是#中的信息,而浏览器请求时不会将 # 后面的数据发送到后台,所以没有问题。但是在history下,你修改path时,当刷新时,如果服务器中没有相应的响应或者资源,则会刷新出来404页面。
 

29.router和route的区别

1.$router为vueRouter的实例,相当于一个全局路由对象,里面含有很多的属性和子对象,用户可以在任何地方使用,在编程式导航中,可以通过$router进行路由跳转(push、replace)
2.$route代表当前正在跳转的路由对象,是一个局部对象,每个路由都有一个$route对象,this.$route后面跟name、path、params、query,可以通过$route可以获取对应的name,path,params,query等路由信息

30.vue-router有几种导航钩子

1.全局导航钩子:router.beforeEach(to,from,next)作用:跳转前进行判断拦截、组件内的钩子、单独路由独享组件
2、路由独享钩子可以在路由配置上直接定义 beforeEnter
3、组件内的导航钩子有三种:
beforeRouteEnter 在进入当前组件对应的路由前调用
beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用
beforeRouteLeave 在离开当前组件对应的路由前调用=
    第一种:beforeEach((to,from,next)=>{
            if(即将进入的路由to.path不在需要守卫的数组中){next()}
            else{
                if(本地缓存是否登陆过){
                    next()
                }else{
                    next("/login");
                }
            })
    
    //afterEach是路由跳转后执行的。     
    第二种:afterEach((to,from)=>{
                
            })

(2)路由配置的导航钩子 beforeEnter可以对单独路由导航使用
    beforeEnter((to,from,next)=>{})
    //使用场景
    {
        path:"/center",
        component:center,    
        beforeEnter((to,from,next)=>{
            next();
            if(本地缓存有){
                next();
            }else{
                next('/login')
            }
        })    
    }    
(3)组件内钩子函数
beforeRouteEnter(to, from, next):进入路由之前执行的函数,写在组件里可根据路由进行页面判断或传值。
beforeRouteUpdate(to,from,next)  
  比如:一个组件有二级导航的时候,点击二级导航的时候导航路径更新了,会触发路由钩子函数beforeRouteUpdate。
beforeRouteLeave(to, from, next):离开路由之前执行的函数,可用于页面的反向传值,页面跳转。

31.vue路由的两种模式

1.hash模式
特点:地址栏有 #不美观但是兼容性要好一些,刷新页面不会发送请求
2.history模式
特点:地址栏没有#更美观,刷新页面会发送请求,会导致页面出现找不到的情况

32.Vuex

1.Vuex可以理解为全局仓库
2.vuex的几种属性:State,Getter,Mutation,Action,Module
原理:
        import Vue from 'vue'
        import Vuex from 'vuex'
        Vue.use(Vuex);
        export default new Vuex.Store({
          state: {
              //用于存放数据
          },
          mutations: {
              //同步代码用于修改state数据的  里面的函数放两个参数(state,要修改的值)
          },
          actions: {
              //async异步函数用于启动mutations同步函数的  里面两个参数(context,params)
              //params是启动该函数传过来的参数
              //启动mutations同步函数用context.commit("同步的函数名",要修改的值)
          },
          modules: {
              //放置需要引入的子vuex
            cinema,user,cinemaRoom,film
          }
        })

常用的git命令

1.说几个git常用命令?
答:我工作中常用的有git add ,git status,git commit –m,git push,git pull等

git config
用法:git config –global user.name “[name]”
用法:git config –global user.email “[email address]”
git init
用法:git init [repository name]
该命令可用于创建一个新的代码库
git clone
用法:git clone [url]
该命令可用于通过指定的URL获取一个代码库。
git add 添加文件到仓库
git status 查看仓库当前的状态,显示有变更的文件。
git diff 比较文件的不同,即暂存区和工作区的差异。
git commit 提交暂存区到本地仓库。
git reset 回退版本。
git push
用法:git push [variable name] master
该命令可以将主分支上提交的变更发送到远程代码库。

说一下多人操作同一个文件,如果出现冲突该如何解决?

答:当遇到多人协作修改同一个文件时出现冲突,我先将远程文件先git pull下来,手动修改冲突代码后,再git add ,git commit,git push再上传到远程仓库。如果pull也pull不下来提示冲突的话,可以先通过git stash暂存下来,然后再pull拉取,然后git stash pop,取出原来写的,手动修改,然后提交

webpack

 

1、webpack的作用是什么,谈谈你对它的理解?
现在的前端网页功能丰富,特别是SPA(single page web application 单页应用)技术流行后,JavaScript的复杂度增加和需要一大堆依赖包,还需要解决Scss,Less……新增样式的扩展写法的编译工作。
所以现代化的前端已经完全依赖于webpack的辅助了。
现在最流行的三个前端框架,可以说和webpack已经紧密相连,框架官方都推出了和自身框架依赖的webpack构建工具。
react.js+WebPack
vue.js+WebPack
AngluarJS+WebPack
2、webpack的工作原理?
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。在3.0出现后,Webpack还肩负起了优化项目的责任。
3、webpack打包原理
把一切都视为模块:不管是 css、JS、Image 还是 html 都可以互相引用,通过定义 entry.js,对所有依赖的文件进行跟踪,将各个模块通过 loader 和 plugins 处理,然后打包在一起。
按需加载:打包过程中 Webpack 通过 Code Splitting 功能将文件分为多个 chunks,还可以将重复的部分单独提取出来作为 commonChunk,从而实现按需加载。把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载
4、webpack的核心概念
Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。告诉webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
output :出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
5、Webpack的基本功能有哪些?
代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
6、gulp/grunt 与 webpack的区别是什么?
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。grunt和gulp是基于任务和流(Task、Stream)的。
类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。webpack是基于入口的。
webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
7、webpack是解决什么问题而生的?
如果像以前开发时一个html文件可能会引用十几个js文件,而且顺序还不能乱,因为它们存在依赖关系,同时对于ES6+等新的语法,less, sass等CSS预处理都不能很好的解决……,此时就需要一个处理这些问题的工具。

http请求过程

1.浏览器发起请求
2.解析域名得到ip进行TCP连接
3.浏览器发送HTTP请求和头信息发送
4.服务器对浏览器进行应答,响应头信息和浏览器所需的内容
5.关闭TCP连接或保持
6.浏览器得到数据数据进行操作。

从用户在浏览器中输入url,到浏览器渲染出界面,中间发生了什么?

1.DNS域名解析
2.发起http或者https请求
3.服务端处理请求
4.浏览器接受请求响应,并渲染界面

前端如何实现身份验证

前端现在一般用token实现身份验证,  登录过后后端会给到前端返回一个token,然后我们把这个token存到本地存储,然后后续在进到主页之后,基本所有的接口发送时,我们都要把这个token添加到header里面,

这时,后端接受到请求之后,会先去解析token,如果能够正常的解析,则表单当前登录人没有问题,可以正常返回数据,否则返回401

浏览器渲染页面的流程

1.解析HTML页面,并构建DOM
2.获取外部资源
3.解析CSS样式,并构建CSSOM
4.执行JavaScript代码
5.结合DOM和CSSOM构成渲染树
6.布局和绘制

最容易碰到的问题:

适配问题 rem flex 响应式

ie9下v-for 在option渲染数据,字符不显示的问题 通过虚拟DOm 从新编写select的样式

接口安全的问题 :接口发送 和 请求 统一加密,通过key 进行校验

未完待续。。。。。。。。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值