• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

前端面试百问

开发技术 开发技术 2天前 5次浏览

请问websocket如何兼容低浏览器的

websocket是H5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

对于低版本的浏览器我们可以使用下面几种方法对低版本浏览器进行兼容。

  1. 引入SockJS库,他是JavaScript的一个库,支持websocket的浏览器会优先使用原生的websorcket,如果不支持,则会使用引用的库文件。
  2. 引用socket.IO的库文件,这同样是基于时间的双向通信,如何不支持则会使用替代的方案。

Vue单页面的SEO解决方案

方法:用prerender-spa-plugin结合vue-meta-info来实现首页的seo

首先就是安装插件:

npm install prerender-spa-plugin vue-meta-info --save

然后在项目的目录下找到文件build/webpack.prod.conf.js,添加如下代码:

const PrerenderSpaPlugin = require('prerender-spa-plugin')

new PrerenderSpaPlugin(
  //将渲染的文件放到dist目录下
      path.join(__dirname, '../dist'),
      //需要预渲染的路由信息
      [ '/index','/about' ],
      {
      //在一定时间后再捕获页面信息,使得页面数据信息加载完成
        captureAfterTime: 50000,
        //忽略打包错误
        ignoreJSErrors: true,
        phantomOptions: '--web-security=false',
        maxAttempts: 10,
      },
    )

描述图片懒加载的方案,并对其原理进行简单的描述

图片懒加载是前端页面优化的一种方式,在页面中有很多图片的时候,图片加载就需要很多时间,很耗费服务器性能,不仅影响渲染速度还会浪费带宽,为了解决这个问题,提高用户体验,所以就出现了懒加载这种方式来减轻服务器的压力,优先加载可视区域的内容,其他部分等进入了可视区域再加载,从而提高性能。

思路:在图片没有进入可视区域时,先不给的src赋值,这样浏览器就不会发送请求了,等到图片进入可视区域再给src赋值。图片的真实地址需要存储在data-src中。图片没有进入可视区域,也就是说图片的offsetTop需要小于页面的可视高度,但想一想,当图片在页面的下方的时候呢,需要页面滚动了一段距离之后才能看到图片,所以这里需要满足img.scrollTop < 页面的可视区域高度+页面滚动的高度,这里是实现图片懒加载的关键,接下来看具体代码的实现

<img src="loading.gif" data-src="1.jpg" alt="">
<img src="loading.gif" data-src="2.jpg" alt="">
<img src="loading.gif" data-src="3.jpg" alt="">
<img src="loading.gif" data-src="4.jpg" alt="">
<img src="loading.gif" data-src="5.jpg" alt="">
<img src="loading.gif" data-src="6.jpg" alt="">
<img src="loading.gif" data-src="7.jpg" alt="">
<img src="loading.gif" data-src="8.jpg" alt="">
<img src="loading.gif" data-src="9.jpg" alt="">
<img src="loading.gif" data-src="10.jpg" alt="">
......

*{
	margin: 0;
	padding: 0;
}
img{
	vertical-align: top;
	width: 100%;
	height: auto;
}

let img = document.getElementsByTagName('img');
let n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
lazyload(); 
window.addEventListener('scroll',lazyload);
function lazyload(){  //监听页面滚动事件
	var seeHeight = window.innerHeight;  //可见区域高度
	var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	for(let i = n; i < img.length; i++){
		if(img[i].offsetTop < seeHeight + scrollTop){
			if(img[i].getAttribute("src") == 'loading.gif'){
				img[i].src = img[i].getAttribute("data-src");
			}
			n = i + 1;
		}
	}
}

请问浮动元素引起的问题和解决方法

浮动元素引起的问题:

  1. 由于浮动元素已脱离文档流,所以父元素无法被撑开,影响与父级元素同级的元素。
  2. 与浮动元素同级的非浮动元素(内联元素)会跟随其后,也是由于浮动元素脱离文档流,不占据文档流中额位置。
  3. 如果该浮动元素不是同级第一个浮动的元素,则它之前的元素也应该浮动,否则容易影响页面的结构显示。

如何清楚浮动:

  1. 使用css样式中的clear:both
  2. 给父元素添加clearfix样式
.clearfix:after{
    content: ".";       
    display: block;  
    height: 0;  
    clear: both;  
    visibility: hidden;
} 

.clearfix{
    display: inline-block; 
} /* for IE/Mac */

跨域的原因与解决方式

跨域是指浏览器的不执行其他网站脚本的,由于浏览器的同源策略造成,是对JavaScript的一种安全限制.说白点理解,当你通过浏览器向其他服务器发送请求时,不是服务器不响应,而是服务器返回的结果被浏览器限制了.

跨域的解决方案

JSONP方式

缺点: get请求,前后端都要修改

优点:无兼容问题(js哪个浏览器不兼容 站出来)

思路:利用js保存全局变量来传递数据,因为js不受同源策略控制,script标签加载机制html

反向代理,ngixn

你在工作中如何优化项目代码,请举例说明.

深拷贝和浅拷贝的区别,请你实现深拷贝的几种方法?

深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。

浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。

深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。

实现深拷贝:

let obj={
    name:'zs',
    age:18
}
 
let deepCloneObj=JSON.parse(JSON.stringify(obj))
deepCloneObj.name=28;
console.log(obj.age);//18

优点:优点是方便快捷,性能相对比较好;

缺点:但是复杂的对象进行JSON转换有可能会丢失属性.

let obj={
    name:'zs',
    main:{
        a:1,
        b:2
    },
  fn:function(){},
  friends:[1,3,5,7,9]
}
 
function copy(obj){
    let newObj=null;
    if(typeof(obj)=='object'&&obj!==null){
        newObj=obj instanceof Array ? []:{};
        for(var i in obj){
            newObj[i]=copy(obj[i])
        }
    }else{
        newObj=obj
    }
    return newObj
}
var obj2 = copy(obj)
obj2.name = '修改成功'
obj2.main.a = 100
console.log(obj,obj2)

请你实现快速排序算法

function quickSort(arr) {
   /*
    * 创建len保存数组的长度,每次获取数组的长度都要实时查询不利于性能;
    * index作为保存取到的中间值;
    * pivot保存比较参照物;
    * left、right作为子数组的容器;
    */
    var len = arr.length,
        index,
        pivot,
        left=[],
        right=[];
    // 如果数组只有一位,就直接返回数组,递归的终止条件;
    if (len <= 1) return arr;

    //获取中间值的索引,使用Math.floor向下取整;
    index = Math.floor(len / 2);

    // 使用splice截取中间值,第一个参数为截取的索引,第二个参数为截取的长度;
    // 如果此处使用pivot=arr[index]; 那么将会出现无限递归的错误;
    // splice影响原数组,原数组长度减一;
    pivot = arr.splice(index, 1);
    len -= 1;

    // 小于arr[pivot]的存到left数组里,大于arr[pivot]的存到right数组;
    for (var i = 0; i < len; i++) {
        if (pivot > arr[i]) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    // 不断把分割的左右子数组传入quickSort,直到分割的只有一位直接返回子数组本身,递归终止;

    // 把每次分割的数组一层一层的用concat连接起来;
    // 每一层left里的元素都小于对应的pivot,right里的元素都大于对应的pivot;
    return quickSort(left).concat(pivot, quickSort(right));
}

如何判断JS变量的数据类型

  1. typeof
1 // 特殊的基本数据类型判断
2 typeof null // 'object'
3 // 特殊的引入数据类型判断
4 typeof function () {} // 'function'
5 typeof new Function() // 'function'

  1. instanceof
1 function Car () {} // 定义一个构造函数
2 console.log(new Car() instanceof Car)
3 console.log({} instanceof Object) // true
4 console.log([] instanceof Array) // true
5 console.log(new String('') instanceof String) // true
6 console.log('' instanceof String) // false
Object.prototype.toString.call(null) // [object Null]
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(1) // [object Number]
Object.prototype.toString.call(new Number(1)) // [object Number]
Object.prototype.toString.call('a') // [object String]
Object.prototype.toString.call(new String('a')) // [object String]

Object.prototype.toString.call({}) // [object Object]
Object.prototype.toString.call([]) // [object Array]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call(/^d$/g) // [object RegExp]
Object.prototype.toString.call(() => {}) // [object Function]
Object.prototype.toString.call(new Function()) // [object Function]

function Car () {} // 定义一个构造函数
Object.prototype.toString.call(new Car()) // [object Object]

rem要如何配置

rem是只相对于根元素htm的font-size,即只需要设置根元素的font-size,@media可以针对不同的屏幕尺寸设置不同的样式

例如:

/*dpi*/
/* for 1080+ px width screen */
/* for 1080 px width screen */
/* for 800 px width screen */
/* for 800 px width screen */
@media only screen and (min-width: 751px) { html, body { font-size: 31.25px; } }
/* for 800 px width screen */
@media only screen and (max-width: 750px) { html, body { font-size: 31.25px; } }
/* for 720 px width screen */
@media only screen and (max-width: 720px) { html, body { font-size: 30px; } }
/* for 640 px width screen */
@media only screen and (max-width: 640px) { html, body { font-size: 27px; } }
/* for 540 px width screen */
@media only screen and (max-width: 540px) { html, body { font-size: 22.5px; } }
/* for 480 px width screen */
@media only screen and (max-width: 480px) { html, body { font-size: 20px; } }
/* for 450 px width screen */
@media only screen and (max-width: 450px) { html, body { font-size: 18.9px; } }
/* for 414 px width screen */
@media only screen and (max-width: 414px) { html, body { font-size: 17.25px; } }
/* for 375 px width screen */
@media only screen and (max-width: 375px) { html, body { font-size: 15.625px; } }
/* for 320 px width screen */
@media only screen and (max-width: 320px) { html, body { font-size: 13.5px; } }

vue中key的作用

key值大多情况下使用在循环语句中,从本质来讲主要作用大概有以下两点:

  1. 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes,相当于唯一标识ID。
  2. Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染, 因此使用key值可以提高渲染效率,同理,改变某一元素的key值会使该元素重新被渲染。

至少写出三种减少页面加载时间的方法

  1. 重复的HTTP请求数量应尽量减少
  • 减少调用其他页面,文件的数量
  • 精灵图
  1. 压缩JavaScript css代码
  2. 在文件头部配置css样式的定义
  3. 在文件末尾放javascript脚本
  4. css,javascript改为外部调用
  5. 使用CDN网络加速
  6. 服务器启用Gzip压缩功能
  7. ajax采用缓存调用
  8. 缩减iframe的使用,如无必要尽量别使用
  9. 优化图片文件

路由导航(路由)守卫

全局导航守卫

  • 全局前置守卫router.beforeEach(fn)
  1. n中有三个参数
    • to:表示要去哪里
    • from:表示从哪里来
    • next:表示通不通过
  • 关于next的使用

    • next() 等价于 next( true ) 表示可以从当前路由跳转到目标路由
    • next( false ) 表示不通过, 表示从当前路由跳转不到目标路由
    • next(’/login’) 等价于 next({path:’/login’}) 跳转指定的路由
    • next(’/login’) 等价于 next({path:’/login’,params,query})
    • next( fn ) 数据预载
    
    // 全局前置路由守卫
    router.beforeEach( (to,from,next) =>{
        const name = localStorage.getItem('name');//查看本地存储上是否有name对象
        if( name || to.path === '/login'){//短路逻辑,有就可以继续执行,没有就跳转到登录页面
            next()
        }else{
         next( '/login' )//跳转登录页面
        }
    })
    
    
    // 全局后置路由守卫 router.afterEach(fn),表示离开当前路由时执行
    router.afterEach( (to,from,next)=> {
        if(to.path === '/user'){//当to的path值等于这个时
            alert('确定进入user吗')
        }
    })
    
  • 全局的后置守卫 afterEach(fn)

路由独享守卫

  • 写在路由表中的守卫钩子
  • 针对的是和当前路由相关的,那么其他与之不相关的路由,它是无法监听它的变化情况的
{
path: '/user',
component: User,
beforeEnter: ( to,from,next ) =>{
    // console.log(to)
    const name = localStorage.getItem('name');//判断本地存储有没有name对象
    if( name){//存在就可以继续执行
        next()
    }else{
        setTimeout(()=>{
                alert('你还没有登录,点击跳转登录')//不存在就弹窗告诉没有登录,点击登录
                next('/login')
            },0)
       }
    }
},

手写axios的二次封装

ajax是基于哪个对象实现的?如何中断Ajax请求?

DOM阻塞

DOM的阻塞会导致页面的加载时间延迟,阻塞的资源有html,css(包括web font),javascript

不是单页面能使用vuex么

看你个人需要了。单页应用为什么要用的?还不是他的逻辑太复杂了,混乱的不得了,不找个工具管理一下各种坑人。如果一个单页应用里面什么js都没有,就是html,那你引入vuex有什么用吗?那么好了,并不是说单页应用什么的,是你遇到了什么问题,用什么东西去解决好了附上vuex里面其实也写了,为了管理你的共享的东西,共享的状态。

swagger的了解

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

1、是一款让你更好的书写API文档的规范且完整框架。

2、提供描述、生产、消费和可视化RESTful Web Service

3、是由庞大工具集合支撑的形式化规范。这个集合涵盖了从终端用户接口、底层代码库到商业API管理的方方面面。

数据分页

定义数据集

获取定位

测性能工具

面向对象的理解

在现实生活中任何物体都可以归为一类事物,而每一个个体都是一个事物的实例,面向对象的编程是以对象为中心.

其中面向对象的有三大特征,分别是封装,继承多态

其中的多态表现为:方法的重载和方法的重写;

方法重载:重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数.

方法重写:重写(也叫覆盖)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。即函数名和参数都一样,只是函数的实现体不一样.

H5有哪些屏幕触碰事件

touchstart:触摸开始的时候触发

touchmove:手指在屏幕上滑动的时候触发

touchend:触摸结束的时候触发

而每个触摸事件都包括了三个触摸列表,每个列表里包含了对应的一系列触摸点(用来实现多点触控):

touches:当前位于屏幕上的所有手指的列表。

targetTouches:位于当前DOM元素上手指的列表。

changedTouches:涉及当前事件手指的列表。

每个触摸点由包含了如下触摸信息(常用):

identifier:一个数值,唯一标识触摸会话(touch session)中的当前手指。一般为从0开始的流水号(android4.1,uc)

target:DOM元素,是动作所针对的目标。

pageX/pageX/clientX/clientY/screenX/screenY:一个数值,动作在屏幕上发生的位置(page包含滚动距离,client不包含滚动距离,screen则以屏幕为基准)。 

radiusX/radiusY/rotationAngle:画出大约相当于手指形状的椭圆形,分别为椭圆形的两个半径和旋转角度。初步测试浏览器不支持,好在功能不常用,欢迎大家反馈。

Label通常怎么用,用什么场景

label标签为input元素定义标注(标记),它不会向用户呈现任何特殊效果,和span标签类似。但label标签和span标签最大的区别就是它为鼠标用户改进了可用性,可以关联特定的表单控件。

主要的应用场景:

label标签常用于与checkboxradio关联,以实现点击文字也能选中/取消checkboxradio

css写一个三角形

<div></div>
<style>
div {
	width: 0;
	height: 0;
	border-top: 50px solid transparent; /*这行去掉也行*/
	border-bottom: 50px solid yellow;
	border-right: 50px solid transparent;
	border-left: 50px solid transparent;
}	
</style>

或者

<div></div>
<style>
div {
	height:0px;
	width:0px;
	border-top:solid 100px red;
	border-left:solid 100px green;
	
}	
</style>

移步>>>>>>


喜欢 (0)