前面面试题

160 阅读7分钟

web storage和cookie的区别

sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。而localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。

web storage和cookie的区别

Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的。Cookie的大小是受限的,并且每次你请求一个新的页面的时候Cookie都会被发送过去,这样无形中浪费了带宽,另外cookie还需要指定作用域,不可以跨域调用。

除此之外,Web Storage拥有setItem,getItem,removeItem,clear等方法,不像cookie需要前端开发者自己封装setCookie,getCookie。但是Cookie也是不可以或缺的:Cookie的作用是与服务器进行交互,作为HTTP规范的一部分而存在 ,而Web Storage仅仅是为了在本地“存储”数据而生。

Vue等单页面应用及其优缺点?

优点:

1、提供了更加吸引人的用户体验:具有桌面应用的即时性、网站的可移植性和可访问性。

2、单页应用的内容的改变不需要重新加载整个页面,web应用更具响应性和更令人着迷。

3、单页应用没有页面之间的切换,就不会出现“白屏现象”,也不会出现假死并有“闪烁”现象

4、单页应用相对服务器压力小,服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍。

5、良好的前后端分离。后端不再负责模板渲染、输出页面工作,后端API通用化,即同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端。

缺点:

1、首次加载耗时比较多。

2、SEO问题,不利于百度,360等搜索引擎收录。

3、容易造成Css命名冲突。

4、前进、后退、地址栏、书签等,都需要程序进行管理,页面的复杂度很高,需要一定的技能水平和开发成本高。

什么是vue的计算属性?

在一个计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以。

vue计算属性的方法

v-on监听的多个方法?

v-on监听的多个方法

v-on可监听的多个方法

谈谈对keep-alive 的理解?

主要用于电商项目中节约渲染时间

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

一般结合路由和动态组件一起使用,用于缓存组件;

提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;

对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activated与deactivated):

首次进入组件时:beforeRouteEnter > beforeCreate > created> mounted > activated > ... ... > beforeRouteLeave > deactivated

再次进入组件时:beforeRouteEnter >activated > ... ... > beforeRouteLeave > deactivated
blog.csdn.net/weixin\_444…

Vue父子组件双向绑定的方法有哪些?

通过在父组件上自定义一个监听事件<myComponent @diy="handleDiy">,在子组件用this.emit(diy,data)来触发这个diy事件,其中data为子组件向父组件通信的数据,在父组件中监听diy个事件时,可以通过emit('diy',data)来触发这个diy事件,其中data为子组件向父组件通信的数据,在父组件中监听diy个事件时,可以通过event访问data这个值。

通过在父组件上用修饰符.sync绑定一个数据,在子组件用this.$emit('update:show',data)来改变父组件中show的值。

双向数据绑定的原理:

数据劫持 observe compile watcher

组件的插槽功能:

分为匿名插槽 具名插槽 作用域插槽

具名插槽

父组件中使用插槽时怎么访问子组件的数据

在实际项目中,我们要获取子组件的数据在父组件中渲染插槽的内容。怎么办?

首先要在子组件中给slot绑定一个子组件的data中的一个对象childrenData,

例:topParent是给父组件中调用的,以上称为插槽prop

然后在父组件中这样调用,

其中slotProps是代表插槽prop的集合,其命名可以随便取的。此时slotProps.toParent的值就是childrenData的值

iframe的优缺点

优点:

1.iframe能够把嵌入的网页原样展现出来;

2.模块分离,便于更改,如果有多个网页引用iframe,只需要修改iframe的内容,就可以实现调用的每一个页面内容的更改,方便快捷;

3.网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe来嵌套,增加代码的可重用;

4.如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决;

5.重载页面时不需要重载整个页面,只需要重载页面中的一个框架页;

6.方便制作导航栏。

缺点:

1.样式和脚本需要额外链入,调用外部页面,需要额外调用css,增加页面额外的请求次数,增加服务器的http请求;

2.代码复杂,在网页中使用框架结构最大的弊病是搜索引擎的“蜘蛛”程序无法解读这种页面,会影响搜索引擎优化,不利于网站排名;

3.框架结构有时会让人感到迷惑,滚动条除了会挤占有限的页面空间外会使iframe布局混乱,还会分散访问者的注意力,影响用户体验;

4.链接导航疑问。运用框架结构时,必须保证正确配置所有的导航链接,否则,会给访问者带来很大的麻烦。比如被链接的页面出现在导航框架内,这种情况下访问者便被陷住了,因为此时他没有其他地点可去;

5.产生多个页面,不易管理;

6.多数小型的移动设备(PDA 手机)无法完全显示框架,设备兼容性差。

v-if v-show的区别

v-if是动态的向DOM树内添加或者删除DOM元素;v-show是通过设置DOM元素的display样式属性控制显隐

v-if适合运营条件不大可能改变;v-show适合频繁切换

css 中控制元素显示隐藏的是 visibility: hidden visible默认

vue中created 中调用接口获取数据 操作dom元素在mounted里面 此时DOM已加载完成

多组件生命周期

初始加载阶段:父 beforeCreate -- 父created --父beforeMount --- 子beforeCreate -- 子created --子beforeMount--子mounted --父mounted

更新阶段:父beforeUpdate --子beforeUpdate -- 子updated -- 父updated

销毁阶段:父beforeDestory--子 beforeDestory -- 子destoryed -- 父destoryed

ajax的原理?

简单来说就是 老板 秘书 主任

老板让秘书去主任那边取一份文件,老板继续做他的其他事,秘书取回来后交给老板,老板对文件进行处理

页面通过ajax发送接口请求,页面继续在浏览器窗口进行渲染,后台接收到接口,返回接口想要的相应数据给浏览器,浏览器接收到数据,渲染到页面

如何获取变量的数据类型?

varstr='abc'; ===> console.log(typeofstr);//string

如何处理跨域问题?

后台在响应头设置 resp.setHeader("Access-Control-Allow-Origin", "*"); //允许所有接口访问

ajax接口请求中 dataType:"jsonp",//数据类型为jsonp type 类型只能为get

nginx 做反向代理

框架自带的配置

什么叫同源协议策略?

协议、域名、端口都相同

css样式简写的几种方式

闭包的原理?

闭包:有权访问另一个函数作用域中的变量的函数;一般情况就是在一个函数中包含另一个函数。

从官方定义我们知道闭包是一个函数,只不过这个函数有[超能力],可以访问到另一个函数的作用域。

数据改变页面DOM元素没有更新

setTimeout this.set(obj,key,value)this.set(obj ,key , value) this.delete(obj,key);

Promise.all([p1, p2]) 一个失败全部失败 promise.race([p1, p2]) 只要有一个成功就成功

节流和防抖是用来干什么的?

节流,单位时间内只发送第一次请求

节抖用在登录页面的点击事件中 单位时间内只发送最后一次请求 用setTimeout实现的

vue指令的生命周期?

Vue.directive('focus', {

bind: function(el){}, //每当指令绑定到元素上,立即执行 bind 函数。注意:绑定时 DOM 树还未被构建。

inserted: function(el){}, //inserted 表示元素插入到 DOM 中的时候(已经插入),执行 inserted 函数

updated: function(el){} //当VNode更新时,会执行 updated})

ComponentUpdated:function(el){}//被绑定元素所在模板完成一次更新周期时调用。

unbind:function(el){} //只调用一次,指令与元素解绑时调用。

})

路由拦截:beforeEach afterEach

请求拦截 axios.interceptors.request.use

响应拦截:axios.interceptors.response.use

数组去重的几种方式?

1)Array.from(new Set(arr))

function unique (arr) {
  return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
 //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

**2)**利用for嵌套for,然后splice去重

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;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     //NaN和{}没有去重,两个null直接消失了

3) 利用indexOf去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
   // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]  //NaN、{}没有去重

4)利用sort()先排序后for循环去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
     var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]      //NaN、{}没有去重

5)利用includes

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]     //{}没有去重

6)利用filter去重

function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

7)利用递归去重

function unique(arr) {
        var array= arr;
        var len = array.length;

    array.sort(function(a,b){   //排序后更加方便去重
        return a - b;
    })

    function loop(index){
        if(index >= 1){
            if(array[index] === array[index-1]){
                array.splice(index,1);
            }
            loop(index - 1);    //递归loop,然后数组去重
        }
    }
    loop(len-1);
    return array;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

8)利用Map数据结构去重

function arrayNonRepeatfy(arr) {
  let map = new Map();
  let array = new Array();  // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    if(map .has(arr[i])) {  // 如果有该key值
      map .set(arr[i], true); 
    } else { 
      map .set(arr[i], false);   // 如果没有该key值
      array .push(arr[i]);
    }
  } 
  return array ;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

9)利用[new set(arr)]

[...new Set(arr)] 
//代码就是这么少----(其实,严格来说并不算是一种,相对于第一种方法来说只是简化了代码)

vue中路由跳转 history和hash区别?

  • hash模式是通过改变锚点(#)来更新页面URL,并不会触发页面重新加载,我们可以通过window.onhashchange监听到hash的改变,从而处理路由。
  • history模式是通过调用window.history对象上的一系列方法来实现页面的无刷新跳转。

hash

hash,原本用来结合锚点控制页面视窗的位置,具有以下特点:

  • 可以改变URL,但不会触发页面重新加载(hash的改变会记录在window.hisotry中)因此并不算是一次http请求,所以这种模式不利于SEO优化
  • 只能修改#后面的部分,因此只能跳转与当前URL同文档的URL
  • 只能通过字符串改变URL
  • 通过window.onhashchange监听hash的改变,借此实现无刷新跳转的功能。

history

根据 Mozilla Develop Network 的介绍,调用 history.pushState() 相比于直接修改 hash,存在以下优势

  • 新的URL可以是与当前URL同源的任意 URL,也可以与当前URL一样,但是这样会把重复的一次操作记录到栈中
  • 通过参数stateObject可以添加任意类型的数据到记录中
  • 可额外设置title属性供后续使用
  • 通过pushState、replaceState实现无刷新跳转的功能。

也就是文章开头讲到的场景,当应用通过vue-router跳转到某个页面后,因为此时是前端路由控制页面跳转,虽然url改变,但是页面只是内容改变,并没有重新请求,所以这套流程没有任何问题。但是,如果在当前的页面刷新一下,此时会重新发起请求,如果nginx没有匹配到当前url,就会出现404的页面。
那为什么hash模式不会出现这个问题呢?
上文已讲,hash虽然可以改变URL,但不会被包括在HTTP请求中。它被用来指导浏览器动作,并不影响服务器端,因此,改变hash并没有改变url,所以页面路径还是之前的路径,nginx不会拦截。 因此,切记在使用history模式时,需要服务端允许地址可访问,否则就会出现404的尴尬场景。

  • 在hash模式下,所有的页面跳转都是客户端进行操作,因此对于页面拦截更加灵活;但每次url的改变不属于一次http请求,所以不利于SEO优化。
  • 在history模式下,借助history.pushState实现页面的无刷新跳转;这种方式URL的改变属于http请求,因此会重新请求服务器,这也使得我们必须在服务端配置好地址,否则服务端会返回404,为确保不出问题,最好在项目中配置404页面