一、一阶段面试题
1. img中的alt和title的区别
alt: 图片未加载时候的提示文字,title是鼠标划过提示文字
2. rem和em的区别
设置rem的的单位元素,大小都参考根元素(html)的字体大小 ;em的字体大小参考的父元素的字体大小,其他属性参考的是当前元素的字体大小
3. BFC 是什么
BFC称为块级格式化上下文,是页面中独立渲染的区域
触发条件:
一 根元素(html) 二 float属性不为none 三 position为absolute或fixed 四 display为inline-block, table-cell, table-caption, flex, inline-flex 五 overflow不为visible
特性:
一、内部的Box会在垂直方向,一个接一个地放置。 二、Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠(按照最大margin值设置) 三、每个元素的margin box的左边, 与包含块border box的左边相接触 四、BFC的区域不会与float box重叠。 五、BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。 六、计算BFC的高度时,浮动元素也参与计算
4. @media all (min-width:640px) and (max-width:1024px){ body{min-height:768px} }的含义
媒体查询,当前分辨率在640-1024之间, body的最小高度为768
5. 你做的页面在哪些浏览器测试过,内核是啥
- 谷歌:webkit(新版后变成了 blink) 2. 火狐 :Gecko 3. ie:Trident 4. 欧鹏 新版本后是blink
6. 网站文件和资源优化的方法
- 结构和样式分离 2. 优化重复代码
7. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">content值里面的意思
width = device-width:视口宽度和设备宽度设置为一致
initial-scale: 初始的缩放比例(默认设置为1.0)
minimum-scale:允许用户缩放到的最小比例(默认设置为1.0)
maximum-scale:允许用户缩放到的最大比例(默认设置为1.0)
user-scalable:用户是否可以手动缩放(默认设置为no,因为我们不希望用户放大缩小页面)
8. css实现元素水平垂直居中
元素{posiiton:absolute;left:50%;top:50%;margin-left:-当前盒子的宽度的一半;marign-top:-当前元素高度的一半}
元素{posiiton:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}
父元素{display:flex;justifiy-content:center;align-items:center}
9.盒子阴影和文本阴影
盒子阴影 box-shadow:水平偏移 垂直偏移 模糊程度 阴影颜色; 文本阴影 text-shadow:水平偏移 垂直偏移 模糊程度 阴影颜色;
10.为啥出现浮动,和清除浮动的方法
(1)块级元素需要横向排列需要用到浮动
(2)清除浮动的方法 ① 父元素overflow:hidden②浮动元素后添加div且设置clear:both;③元素::after{content:"";clear:both;display:block;}
11. src和href的区别
src是图片引入路径对应的属性,href是a标签的跳转路径,或者是link的引入css外部样式表对应的属性
12. css选择器的种类
- id选择器 class选择器 后代选择器 群组选择器 伪类选择器 标签选择器等
13.margin的重叠
给上面的盒子添加下边距,给下面的盒子添加了上边距,边距会产生重叠,以最大值显示,左右会相加
14.display:none与visibility:hidden的区别是什么
两个都是隐藏元素,前者隐藏之后不占位
14. link和@import引入外部样式表的区别
1.link可以引入css还有其他文件类型 @import只能是css 2. link引入的css和结构同时加载,@import是先结构,后样式 3.link几乎所有浏览器都支持 @import只有ie6及以上支持
15. Doctype 的作用?严格模式与混杂模式的区别?
<!DOCTYPE>用于告知浏览器该以何种模式来渲染文档
严格模式下:页面排版及 JS 解析是以该浏览器支持的最高标准来执行
混杂模式:不严格按照标准执行,主要用来兼容旧的浏览器,向后兼容
16. css继承的属性有哪些
文本:letter-spacing、line-height、color、font-family、font-size、font-style、font-weight、text-decoration、text-transform text-indent text-align list-style、list-style-type、list-style-position、list-style-image
17. b标签和strong标签,i标签和em标签的区别?
都可以加粗,strong表强调,都可以倾斜,em表强调
18. 请写出至少你知道的HTML5标签
article audio canvas figcaption figure footer header hgroup mark nav section time video
19.语义化的理解
1.html语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;
2.在没有样式CCS情况下也以一种文档格式显示,并且是容易阅读的。
3.搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于 SEO。
4.使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
20. Html和xhtml有什么区别
XHTML元素必须正确的被嵌套,元素必须关闭,标签必须小写,必须有根元素
21. 移动端布局的常用方法
- 百分比布局 2.响应式布局 3.rem布局
22. 内联块元素之间形成的空白间隙的原因及解决方法
- 代码之间的回车所导致 解决 1. 浮动 2. 设置父元素的字体大小为0 3. 代码卸载同一行
23. 介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的?
标准盒模型和怪异(低版本ie)盒模型,怪异盒模型的内容区会将padding和border的值包进去
24. 渐进增强和优雅降级之间的不同吗?
渐进增强针对低版本浏览器进行构建页面,保证最基本的 功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。 优雅降级 graceful degradation:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
25. 网页制作会用到的图片格式有哪些
png-8,png-24,jpeg,gif,svg。 Webp。
WebP 格式,谷歌(google)开发的一种旨在加快图片加载速度的图 片格式。图片压缩体积大约只有 JPEG 的 2/3,并能节省大量的服务器带宽资源和数据空 间。Facebook Ebay 等知名网站已经开始测试并使用 WebP 格式。 在质量相同的情况下,WebP 格式图像的体积要比 JPEG 格式图像小 40%
26. rgba()和 opacity 的透明效果有什么不同
rgba()和 opacity 都能实现透明效果,但最大的不同是 opacity 作用于元素,以及元素内 的所有内容的透明度, 而 rgba()只作用于元素的颜色或其背景色。(设置 rgba 透明的元素的子元素不会继承透 明效果!)
27. 为什么要初始化样式?(重置样式表)
由于浏览器兼容的问题,不同的浏览器对标签的默认样式值不同,若不初始化会造成不同 浏览器之间的显示差异 但是初始化 CSS 会对搜索引擎优化造成小影响
二、阶段面试题
### 1.深拷贝浅拷贝
浅拷贝,就是简单的值传递。a值传给b,b值变动时,a值会被影响,深拷贝则反之,达到了真正意义上的拷贝。b值被改动,不影响a值,可以通过 JSON.parse(JSON.stringify(object)) 来解决。
//浅拷贝
var a=[1,2,3,4,5];
var b=a;
b[0]=11;
console.log(a); //[11,2,3,4,5]
console.log(b); //[11,2,3,4,5]
//深拷贝
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
2.JavaScript有几种类型的值
-
栈:原始数据类型(
Undefined
,Null
,Boolean
,Numbe
r、String
) -
堆:引用数据类型(对象、数组和函数)
-
两种类型的区别是:存储位置不同;
-
原始数据类型直接存储在栈(
stack
)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储; -
引用数据类型存储在堆(
heap
)中的对象,占据空间大、大小不固定,如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其 -
在栈中的地址,取得地址后从堆中获得实体
-
3.ajax步骤
// 1. 创建连接 var xhr = null; xhr = new XMLHttpRequest() // 2. 连接服务器 xhr.open('get', url, true) // 3. 发送请求 xhr.send(null); // 4. 接受请求 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if(xhr.status == 200){ success(xhr.responseText); } else { // fail fail && fail(xhr.status); } } }
4.document.ready和document.load的区别
都是页面加载完成事件,
document.ready
是当DOM文档树加载完成后执行一个函数 (不包含图片,css等)所以会比load较快执行在原生的jS中不包括ready()这个方法,Jquery才有,$().ready(function)
,load是当页面所有资源全部加载完成后(包括DOM文档树,css文件,js文件,图片资源等),执行一个函数,load方法就是onload事件。
5.写代码,一分钟后页面重新加载
<!doctype html>
<head> <meta charset="utf-8"> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link href="css/mui.css" rel="stylesheet" /> </head> <body>
</body>
<script> document.body.οnlοad=function(){ setInterval(()=>{ window.location.href=window.location.href; },1000*60); } </script>
5.数组去重
方法一
- 双层循环,外层循环元素,内层循环时比较值
- 如果有相同的值则跳过,不相同则
push
进数组
Array.prototype.distinct = function(){
var arr = this,
result = [],
i,
j,
len = arr.length;
for(i = 0; i < len; i++){
for(j = i + 1; j < len; j++){
if(arr[i] === arr[j]){
j = ++i;
}
}
result.push(arr[i]);
}
return result;
}
var arra = [1,2,3,4,4,1,1,2,1,1,1];
arra.distinct(); //返回[3,4,2,1]
方法二:利用splice直接在原数组进行操作
- 双层循环,外层循环元素,内层循环时比较值
- 值相同时,则删去这个值
- 注意点:删除元素之后,需要将数组的长度也减1.
Array.prototype.distinct = function (){
var arr = this,
i,
j,
len = arr.length;
for(i = 0; i < len; i++){
for(j = i + 1; j < len; j++){
if(arr[i] == arr[j]){
arr.splice(j,1);
len--;
j--;
}
}
}
return arr;
};
var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];
var b = a.distinct();
console.log(b.toString()); //1,2,3,4,5,6,56
- 优点:简单易懂
- 缺点:占用内存高,速度慢
方法三:利用对象的属性不能相同的特点进行去重
Array.prototype.distinct = function (){
var arr = this,
i,
obj = {},
result = [],
len = arr.length;
for(i = 0; i< arr.length; i++){
if(!obj[arr[i]]){ //如果能查找到,证明数组元素重复了
obj[arr[i]] = 1;
result.push(arr[i]);
}
}
return result;
};
var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];
var b = a.distinct();
console.log(b.toString()); //1,2,3,4,5,6,56
方法四:数组递归去重
- 运用递归的思想
- 先排序,然后从最后开始比较,遇到相同,则删除
Array.prototype.distinct = function (){
var arr = this,
len = arr.length;
arr.sort(function(a,b){ //对数组进行排序才能方便比较
return a - b;
})
function loop(index){
if(index >= 1){
if(arr[index] === arr[index-1]){
arr.splice(index,1);
}
loop(index - 1); //递归loop函数进行去重
}
}
loop(len-1);
return arr;
};
var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,56,45,56];
var b = a.distinct();
console.log(b.toString()); //1,2,3,4,5,6,45,56
方法五:利用indexOf以及forEach
Array.prototype.distinct = function (){
var arr = this,
result = [],
len = arr.length;
arr.forEach(function(v, i ,arr){ //这里利用map,filter方法也可以实现
var bool = arr.indexOf(v,i+1); //从传入参数的下一个索引值开始寻找是否存在重复
if(bool === -1){
result.push(v);
}
})
return result;
};
var a = [1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,2,3,3,2,2,1,23,1,23,2,3,2,3,2,3];
var b = a.distinct();
console.log(b.toString()); //1,23,2,3
方法六:利用ES6的set
Set
数据结构,它类似于数组,其成员的值都是唯一的。- 利用
Array.from
将Set
结构转换成数组
function dedupe(array){
return Array.from(new Set(array));
}
dedupe([1,1,2,3]) //[1,2,3]
拓展运算符(
...
)内部使用for...of
循环let arr = [1,2,3,3]; let resultarr = [...new Set(arr)]; console.log(resultarr); //[1,2,3]
6.json转字符串方法
var json = {name: "zhangsan", age: 23, email: "chentging@aliyun.com"}; var jsonStr = JSON.stringify(json); console.log(jsonStr); //输出:"{"name":"zhangsan","age":23,"email":"chentging@aliyun.com"}"
7.数字保留两位数
使用toFixed()方法
<input type="number" value="" class="inputVal"> <button class="inputs" style="width: 50px;height: 20px;">
script中的代码:
<script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> <script> $(function (){ // var num = 19.567.toFixed(2);//四舍五入,结果为19.57 $('.inputVal').keyup(function() { var coverNume = $('.inputVal').val(); console.log(coverNume); var sonm = Number(coverNume).toFixed(2);//Number 把字符串转化为数字 console.log(sonm); }); }); </script>
8字符串去空
//去除字符串两边的空白
String.prototype.trim=function(){
return this.replace(/(^\s*)|(\s*$)/g, "");
}
//只去除字符串左边空白
String.prototype.ltrim=function(){
return this.replace(/(^\s*)/g,"");
}
//只去除字符串右边空白
String.prototype.rtrim=function(){
return this.replace(/(\s*$)/g,"");
}
9冒泡或(选择)排序算法
//冒泡排序 var arr = [3, 1, 4, 6, 5, 7, 2];
function bubbleSort(arr) { for (var i = 0; i < arr.length - 1; i++) { for(var j = 0; j < arr.length - i - 1; j++) { if(arr[j + 1] < arr[j]) { var temp; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; } console.log(bubbleSort(arr));
//选择排序 function selsetSort(arr){ var len = arr.length; var index; for(var i=0;i<len-1;i++){ index=i; for(var j=i+1;j<len;j++){ if(arr[index]>arr[j]){//寻找最小值 index=j;//保存最小值的索引 } } if(index!=i){ var temp =arr[i]; arr[i]=arr[index]; arr[index]=temp; } } return arr; }
10.输入数字和期望的长度,转化为对应长度字符串,不足期望长度用0补足
function exp(num,length){
var numstr = num.toString();
var m = numstr.length;
if(numstr.length >= length){
return numstr;
}
for(var i = 0;i<length-m;i++){
numstr="0" + numstr;
}
return numstr;
}
11.前端已知(具有属性id, name)对象数组objList,现需要更新数组中id=103 的对象,使其name="new fourth"
let objLit=[{id: 100',name:first },{id: 101',name'second },{id:' 102 ,name:third'),{id:'103',name:'fourth'},{id:'105',name:'fifth'}];
for(var item of objLit){
if(item.id == '103'){
item.name = 'new fourth';
}
}
12.HTML5存储类型有哪些,有何区别?
Cookie、localstorage、sessionstorage
- cookie`是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)
- cookie数据始终在同源的http请求中携带(即使不需要),也会在浏览器和服务器间来回传递
sessionStorage
和localStorage
不会自动把数据发给服务器,仅在本地保存- 存储大小:
cookie
数据大小不能超过4ksessionStorage
和localStorage
虽然也有存储大小的限制,但比cookie
大得多,可以达到5M或更大
- 有期时间:
localStorage
存储持久数据,浏览器关闭后数据不丢失除非主动删除数据sessionStorage
数据在当前浏览器窗口关闭后自动删除cookie
设置的cookie
过期时间之前一直有效,即使窗口或浏览器关闭
13.请写出以下代码的输出
var a = []
a[1] = 2
console.log(a.length) //输出:2
14.请写出以下代码的输出
var a = {a:1,b:2}
var b = a
b.b = 3
console.log(a) //输出:{a: 1, b: 3}
console.log(b) //输出:{a: 1, b: 3}
15.请写出以下代码的输出
for(var i =0;i<5;i++){
setTimeout(function(){
console.log(new Date,i)
},1000)
}
console.log(new Date,i) //输出:5
16.求1000以内质数和
var prime = function(len){
var i,j;
var arr = [];
for(i = 1; i < len; i++){
for(j=2; j < i; j++){
if(i%j === 0) {
break;
}
}
if(i <= j && i !=1){
arr.push(i);
}
}
return arr;
};
console.log(prime(100));
17.数组元素去重(限制最多一次循环)
var arr=[2,8,5,0,5,2,6,7,2]; function unique1(arr){ var hash=[]; for (var i = 0; i < arr.length; i++) { if(hash.indexOf(arr[i])==-1){ hash.push(arr[i]); } } return hash; }
18.Js面向对象的几种方式
1.对象的字面量 var obj = {}
2.创建实例对象 var obj = new Object();
3.构造函数模式 function fn(){} , new fn();
4.工厂模式:用一个函数,通过传递参数返回对象。function fn(params){var obj =new Object();obj.params = params; return obj;},fn(params);
5.原型模式:function clock(hour){} fn.prototype.hour = 0; new clock();
首先,每个函数都有一个prototype(原型)属性,这个指针指向的就是clock.prototype对象。而这个原型对象在默认的时候有一个属性constructor,指向clock,这个属性可读可写。而当我们在实例化一个对象的时候,实例newClock除了具有构造函数定义的属性和方法外(注意,只是构造函数中的),还有一个指向构造函数的原型的指针,ECMAScript管他叫[[prototype]],这样实例化对象的时候,原型对象的方法并没有在某个具体的实例中,因为原型没有被实例。
19.说说TCP传输的三次握手四次挥手策略
为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送 后的情况置之不理,它一定会向对方确认是否成功送达。握手过程中使用了TCP的标志:SYN和ACK。
发送端首先发送一个带SYN标志的数据包给对方。接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。
最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束。
若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。
断开一个TCP连接则需要“四次挥手”:
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
20.用js代码简单的介绍下自己
<script> function Person(name,jingli,jineng) { this.name=name; this.jingli=jingli; this.jineng=jineng; } Person.prototype.show=function(){ console.log("我是"+this.name+";我有如下经历:"+this.jingli+";我会如下技能:"+this.jineng); } var myself=new Person("小田","小田工作室创办人,凤翔网络推广顾问","熟悉前端基本技能,熟悉网络营销思想有实战经验,掌握项目经理技能,可以编写文档,也可以使用axure进行原型设计,掌握自动化测试和性能测试技能") myself.show(); #### #### 21.Ajax同步和异步的区别,如何解决跨域问题 同步的概念应该是来自于OS中关于同步的概念:不同进程为协同完成某项工作而在先后次序上调整(通过阻塞,唤醒等方式).同步强调的是顺序性.谁先谁后.异步则不存在这种顺序性. 同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作。 异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容。 jsonp、 iframe、window.name、window.postMessage、服务器上设置代理页面 #### 22.列举几种后端通讯的方法及其使用的场景,关于跨域的理解。 1.后端程序可以通过session来进行通讯,session有过期时间,主要用于验证码的验证,登录过期等的应用。 2.数据库,数据库支持多种语言的操作,那么通过数据库就可以通讯。 关于跨域: 跨域请求存在的原因:由于浏览器的同源策略,即属于不同域的页面之间不能相互访问各自的页面内容。 **跨域的场景**: 1.域名不同 www.yangwei.com 和www.wuyu.com 即为不同的域名) 2.二级域名相同,子域名不同(www.wuhan.yangwei.com www.shenzheng.yangwei.com 为子域不同) 3.端口不同,协议不同 ( http://www.yangwei.com 和https://www.yangwei.com属于跨域www.yangwei.con:8888和www.yangwei.con:8080) 跨域的方式:(内容较多,需掌握CORS和jsonp,其他内容也要了解) **1.****前端的方式**: possMessage,window.name,document.domain,image.src(得不到数据返回),jsonP(script.src后台不配合得不到数据返回),style.href(得不到数据返回) 一.image.src,script.src,style.href 不受同源策略的影响可以加载其他域的资源,可以用这个特性,向服务器发送数据。最常用的就是使用image.src 向服务器发送前端的错误信息。image.src 和style.href 是无法获取服务器的数据返回的,script.src 服务器端配合可以得到数据返回。 二possMessage,window.name,document.domain 是两个窗口直接相互传递数据。 (1)possMessage 是HTML5中新增的,使用限制是 必须获得窗口的window 引用。IE8+支持,firefox,chrome,safair,opera支持 (2)window.name ,在一个页面中打开另一个页面时,window.name 是共享的,所以可以通过window.name 来传递数据,window.name的限制大小是2M,这个所有浏览器都支持,且没有什么限制。 3) document.domain 将两个页面的document.domain 设置成相同,document.domain 只能设置成父级域名,既可以访问,使用限制:这顶级域名必须相同 2.纯后端方式: CORS,服务器代理 CORS 是w3c标准的方式,通过在web服务器端设置:响应头Access一Cntrol一Alow一Origin 来指定哪些域可以访问本域的数据,ie8&9(XDomainRequest),10+,chrom4 ,firefox3.5,safair4,opera12支持这种方式。 服务器代理,同源策略只存在浏览器端,通过服务器转发请求可以达到跨域请求的目的,劣势:增加服务器的负担,且访问速度慢。 3.前后端结合:JsonP script.src 不受同源策略的限制,所以可以动态的创建script标签,将要请求数据的域写在src 中参数中附带回调的方法,服务器端返回回调函数的字符串,并带参数。 如 script.src="http://www.yangwei.com/?id=001&callback=getInfoCallback",服务器端返回 getInfoCallBack("name:yangwei;age:18") 这段代码会直接执行,在前面定义好getInfoCallBack函数,既可以获得数据并解析。 这种是最常见的方式。 4.webSocket(了解性拓展) [**服务端推送****websocket****和****sse****场景及应用**](https://www.iflym.com/index.php/java-network/201607040001.html) 应用场景 都可以进行服务端推送,并且都是使用长连接来进行.但两者的实现又有一点不同,sse仍使用http协议,并且使用相同的链接发送正常的http协议报文.而websocket是使用http协议进行握手,然后再使用同一个链接进行websocket协议的通信. **websocket****可以进行双向的通信,**即服务端可以往客户端发信息,客户端也可以向服务端发信息.而sse是单向的,只能由服务端往客户端发. **websocket****自带连接的保持**,即通过ping/pong协议保证连接可以始终维持,sse没有这个保证,不过可以参考ping/pong协议,自己周期性地发送信息来同样地进行处理.比如,5秒往客户端发一个特别的信息(通过type/name进行区分).其次,因为是基于浏览器的使用,sse有一个特性,就是浏览器发现一个连接断掉了,就会自动地进行重联,即重新发送请求.这样,服务端也不用担心连接被断开,不过需要处理新的请求必须和上一次请求的内容相连续,以及新的推送注册. 因为都是使用http协议进行起始处理,因此在签权上都可以使用到http协议本身的一些东西,比如header/cookie签权.在相应的握手阶段,通过读取cookie(session)来保证相应的请求必须是经过授权的,也可以用于定位使用人.甚至可以通过这些信息保证单个用户只能有一个请求,避免重复请求 由于都是基于浏览器使用,因此建议的数据传输都是文本型.虽然websocket支持二进制frame传输,不过一些都不建议使用.sse只能传输文本 不管是websocket还是sse,在用于通信时,都建议只用于进行数据的推送,而不是进行完整的应用处理.这里可以理解为,常规的业务处理仍然交给后端的服务来处理.这样,即可以使用之前的业务开发的优势,又可以使用推送的优势.而不是走向另一个级端,即所有的信息都想通过推送来传递. 开发方式 websocket开发首选netty,因为netty对协议的封装已经做到了完全的支持.通过 HttpServerCodec作为握手协议,WebSocketServerProtocolHandler作为协议处理,然后再加一个自己的handler,就完成了相应的业务处理.同时在性能上,netty在一个ws的请求建立起来之后,会自动地去除httpServerCodec相关的handler,这样保证后续的处理都是按照ws的协议来进行. sse开发首选jersey,jersey一media一sse提供了相应的sse支持,并且通过与rest相集成,开发一个sse就跟普通的业务开发相同. ws和sse在文本支持上都只支持utf一8编码,因此在处理上需要注册编码方式.同时在使用sse时,如果后端第一次进行响应时,相应的编码不对.chrome会直接报错,包括utf8都会报错(这是之前后端开发的一个问题),可以修正或者增加相应的拦截器,保证后端content一type响应中的charset=UTF一8. ws和sse都可以通过nginx进行代理转发.ws的处理只需要设置http版本,以及重新转发前端的Upgrade和Connection头即可.而sse,也可以通过禁用buffer来处理.参考 http://stackoverflow.com/questions/27898622/server一sent一events一stopped一work一after一enabling一ssl一on一proxy 特定实现 为保证在开发时推送类的和业务类的系统不会耦合在一起,或者同一个应用内有两种处理模式的功能存在.建议直接在系统层就开发2个不同的系统,一个专门用于推送,另一个用于相应的业务处理.然后业务处理后的数据,需要再交由推送处理,则可以在后端进行通过消息系统进行中转,如kafka(持久保证)或redis(内存订阅)等 因为二者在ie上的支持都很有限,因此不建议在ie上进行尝试 使用sse还是websocket,取决于是否需要前台交互,还取决于对后端的支持技术的了解程序.比如,了解jersey多一点,还是netty多一点.由于最近netty进行微服务化底层通信支持越来越流行,个人更倾向于使用websocket.但如果仅仅是一个简单的推送功能,又不希望修改代码,那也可以使用jersey(毕竟之前的系统就是在上面进行开发的) 需要后端有的时候需要进行定向发送或者是群发,这种需求ws和sse的实现中都有相应的处理.如ChannelGroup和SseBroadcaster,这样在后端获取到一个消息,需要进行路由时就可以从这里面拿相应的channel信息.不过,前提是对各个channel上进行了特定的消息绑定,这样就好区分具体的路由信息.具体路由策略可以在建立时绑定session,后续通过session来路由. #### 23.说说你对作用域链的理解 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。 #### 24.什么是ajax和json,它们的优缺点 ajax的全称:Asynchronous Javascript And XML。 异步传输+js+xml。实现无刷新状态更新页面和异步提交 所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果它自己会根据设定进行后续操作,与此同时,页面是不会发生整页刷新的,提高了用户体验。 Ajax实现过程: (1)创建XMLHttpRequest对象,也就是创建一个异步调用对象 (2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息 (3)设置响应HTTP请求状态变化的函数 (4)发送HTTP请求 (5)获取异步调用返回的数据 (6)使用JavaScript和DOM实现局部刷新 优点: 不需要插件支持 用户体验极佳 提升Web程序性能 减轻服务器和宽带的负担 缺点: 前进后退按钮被破坏 搜索引擎的支持不够 开发调试工具缺乏 JSON(JavaScript Object Notation)和XML一样也是一种简单文本格式。是一种比较流行的标准格式,是数据的载体,相对于XML,JSON更加易读、更便于肉眼检查。在语法的层面上,JSON与其他格式的区别是在于分隔数据的字符,JSON中的分隔符限于单引号、小括号、中括号、大括号、冒号和逗号。 优点: 作为一种数据传输格式,JSON 与 XML 很相似,但是它更加灵巧。 JSON不需要从服务器端发送含有特定内容类型的首部信息。 缺点: 语法过于严谨 代码不易读 eval 函数存在风险 #### 25. http状态码有那些,分别代表什么意思 简单版: 100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息 200 OK 正常返回信息 201 Created 请求成功并且服务器创建了新的资源 202 Accepted 服务器已接受请求,但尚未处理 301 Moved Permanently 请求的网页已永久移动到新位置。 302 Found 临时性重定向。 303 See Other 临时性重定向,且总是使用 GET 请求新的 URI。 304 Not Modified 自从上次请求后,请求的网页未修改过。 400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。 401 Unauthorized 请求未授权。 403 Forbidden 禁止访问。 404 Not Found 找不到如何与 URI 相匹配的资源。 500 Internal Server Error 最常见的服务器端错误。 503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。 完整版 1**(信息类):表示接收到请求并且继续处理 100——客户必须继续发出请求 101——客户要求服务器根据请求转换HTTP协议版本 2**(响应成功):表示动作被成功接收、理解和接受 200——表明该请求被成功地完成,所请求的资源发送回客户端 201——提示知道新文件的URL 202——接受和处理、但处理未完成 203——返回信息不确定或不完整 204——请求收到,但返回信息为空 205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件 206——服务器已经完成了部分用户的GET请求 3**(重定向类):为了完成指定的动作,必须接受进一步处理 300——请求的资源可在多处得到 301——本网页被永久性转移到另一个URL 302——请求的网页被转移到一个新的地址,但客户访问仍继续通过原始URL地址,重定向,新的URL会在response中的Location中返回,浏览器将会使用新的URL发出新的Request。 303——建议客户访问其他URL或访问方式 304——自从上次请求后,请求的网页未修改过,服务器返回此响应时,不会返回网页内容,代表上次的文档已经被缓存了,还可以继续使用 305——请求的资源必须从服务器指定的地址得到 306——前一版本HTTP中使用的代码,现行版本中不再使用 307——申明请求的资源临时性删除 4**(客户端错误类):请求包含错误语法或不能正确执行 400——客户端请求有语法错误,不能被服务器所理解 401——请求未经授权,这个状态代码必须和WWW一Authenticate报头域一起使用 HTTP 401.1 一 未授权:登录失败 HTTP 401.2 一 未授权:服务器配置问题导致登录失败 HTTP 401.3 一 ACL 禁止访问资源 HTTP 401.4 一 未授权:授权被筛选器拒绝 HTTP 401.5 一 未授权:ISAPI 或 CGI 授权失败 402——保留有效ChargeTo头响应 403——禁止访问,服务器收到请求,但是拒绝提供服务 HTTP 403.1 禁止访问:禁止可执行访问 HTTP 403.2 一 禁止访问:禁止读访问 HTTP 403.3 一 禁止访问:禁止写访问 HTTP 403.4 一 禁止访问:要求 SSL HTTP 403.5 一 禁止访问:要求 SSL 128 HTTP 403.6 一 禁止访问:IP 地址被拒绝 HTTP 403.7 一 禁止访问:要求客户证书 HTTP 403.8 一 禁止访问:禁止站点访问 HTTP 403.9 一 禁止访问:连接的用户过多 HTTP 403.10 一 禁止访问:配置无效 HTTP 403.11 一 禁止访问:密码更改 HTTP 403.12 一 禁止访问:映射器拒绝访问 HTTP 403.13 一 禁止访问:客户证书已被吊销 HTTP 403.15 一 禁止访问:客户访问许可过多 HTTP 403.16 一 禁止访问:客户证书不可信或者无效 HTTP 403.17 一 禁止访问:客户证书已经到期或者尚未生效 404——一个404错误表明可连接服务器,但服务器无法取得所请求的网页,请求资源不存在。eg:输入了错误的URL 405——用户在Request一Line字段定义的方法不允许 406——根据用户发送的Accept拖,请求资源不可访问 407——类似401,用户必须首先在代理服务器上得到授权 408——客户端没有在用户指定的饿时间内完成请求 409——对当前资源状态,请求不能完成 410——服务器上不再有此资源且无进一步的参考地址 411——服务器拒绝用户定义的Content一Length属性请求 412——一个或多个请求头字段在当前请求中错误 413——请求的资源大于服务器允许的大小 414——请求的资源URL长于服务器允许的长度 415——请求资源不支持请求项目格式 416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If一Range请求头字段 417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求长。 5**(服务端错误类):服务器不能正确执行一个正确的请求 HTTP 500 一 服务器遇到错误,无法完成请求 HTTP 500.100 一 内部服务器错误 一 ASP 错误 HTTP 500一11 服务器关闭 HTTP 500一12 应用程序重新启动 HTTP 500一13 一 服务器太忙 HTTP 500一14 一 应用程序无效 HTTP 500一15 一 不允许请求 global.asa Error 501 一 未实现 HTTP 502 一 网关错误 HTTP 503:由于超载或停机维护,服务器目前无法使用,一段时间后可能恢复正常 #### 26.HTTP的请求方法 HTTP(Hypertext Transfer Protocol)的八种请求方法: | **方法** | **概述** | | ------------- | ------------------------------------------------------------ | | **❤****GET** | 请求页面的详细信息,并返回实体主体。 | | **❤****POST** | 向指定资源提交数据进行数据请求(例如提交表单,或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 | | **PUT** | 从客户端向服务器传送的数据取代指定的文档内容。 | | DELETE | 请服务器删除指定的页面。 | | HEAD | 类似与Get请求,只不过返回的响应中没有具体的内容,用于获取报头 | | CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 | | OPTIONS | 允许客户端查看服务器的性能。 | | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 | #### 27. 什么是闭包(closure)为什么要用它 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。 闭包的特性: 1.函数内再嵌套函数 2.内部函数可以引用外层的参数和变量 3.参数和变量不会被垃圾回收机制回收 例如://li节点的onclick事件都能正确的弹出当前被点击的li索引 **
- ** **
- index = 0
- ** **
- index = 1
- ** **
- index = 2
- ** **
- index = 3
- ** **
** **<script type="text/javascript">** **var nodes = document.getElementsByTagName("li");** **for(i = 0;i<nodes.length;i+= 1){** **nodes[i].onclick = (function(i){** **return function() {** **console.log(i);** **} //****不用闭包的话,值每次都是4** **})(i);** **}** **</script>**
执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在
使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源
因为say667()的内部函数的执行需要依赖say667()中的变量
这是对闭包作用的非常直白的描述
function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; } var sayAlert = say667(); **sayAlert()//**执行结果应该弹出的667
你知道哪些针对jQuery的优化方法
基于Class的选择性的性能相对于Id选择器开销很大,因为需遍历所有DOM元素。
频繁操作的DOM,先缓存起来再操作。用Jquery的链式调用更好。
比如:var str=$("a").attr("href");
for (var i = size; i < arr.length; i++) {}
for 循环每一次循环都查找了数组 (arr) 的.length 属性,在开始循环的时候设置一个变量来存储这个数字,可以让循环跑得更快:
for (var i = size, length = arr.length; i < length; i++) {}
28.用原型链继承的方式写一个类和子类
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.study=function(){
return "学习"
}
/var p1 =new Person("张三",20);/
/p1.study();/
function Student(class_,name,age){
this.class_=class_;
this.name=name;
this.age=age;
}
Student.prototype=new Person();
var s1 =new Student("二班","李大人",16);
console.log(s1.name,s1.age,s1.class_,s1.study());
29.用原型链继承的方式写一个类和子类
function Person(name,age){ this.name=name; this.age=age; }
Person.prototype.study=function(){ **return "**学习" }
**/*var p1 =new Person("**张三",20);*/ /*p1.study();*/
function Student(class_,name,age){ this.class_=class_; this.name=name;this.age=age; } Student.prototype=new Person();
**var s1 =new Student("**二班","李大人",16);
console.log(s1.name,s1.age,s1.class_,s1.study());
30、 编写一个方法求一个字符串的字节长度,假设:一个英文字符占用一个字节,一个中文字符占用两个字节
function num(str) { var num1 = str.length; var num2 = 0; for (var i = 0; i < str.length; i++) { if (str.charCodeAt(i) >= 10000) { num2++; } } console.log(num1 + num2) }
31、 简单概括浏览器事件模型,如何获得资源dom节点
在各种浏览器中存在三种事件模型:原始事件模型( original event model),DOM2事件模型,IE事件模型.其中原始的事件模型被所有浏览器所支持,而DOM2中所定义的事件模型目前被除了IE以外的所有主流浏览器支持。
浏览器事件模型分为三个阶段
1、捕获阶段
2、目标阶段
3、冒泡阶段
Dom节点获取方法:
1.通过id属性获取 document.getElementById()
2.通过name属性获取 document.getElementsByName()
3.通过标签名获取 document.getElementsByTagName()
4.通过class属性获取 document.getElementsByClassName()
5.原生js中的querySelector 和 querySelectorAll方法也同样可以获取到相应的dom节点,相似于jquery,但比jq更快
33、 写一段ajax提交的js代码
var xhr =xhr(); function xhr(){ if(window.XMLHttpRequest){ return window. XMLHttpRequest(); }else if(window.ActiveXObject){ try { return new ActiveXObject("Microsoft.XMLHTTP"); }catch (e) { try { return new ActiveXObject("Msxml2.XMLHTTP"); }catch (ex) { } } } }xhr.open("get","url","true"); xhr.onreadystatechange=function(){ if (xhr.readyState4 && (xhr.status200||xhr.status==304)){document.getElementById("myDiv").innerHTML=xhr.responseText; } } xhr.send(); }
34、 判断字符串是否是这样组成的,第一个必须是字母,后面可以是字母和数字、下划线,总长度为5一20(请使用正则表达式)
function if_fit(str){ var reg=/[1]{1}\w{5,20}/g; var result=str.search(reg); return result; }
35、 截取字符串abcdefg的efg
var str="abcdefg"; console.log(str.slice(4));
36、 将字符串helloChina反转输出
var str = "helloChina"; 方法1**:console.log( str.split("").reverse().join("") );'); 方法2:for (var x = str.length一1; x >=0; x一一) {** document.write(str.charAt(x)); } 方法3**:var a=str.split("");** var rs = new Array; while(a.length){ rs.push(a.pop()); }alert(rs.join(""));
37. 简述ECMASCRIPT6的新特性
1.增加块作用域
2.增加let const
3.解构赋值
4.函数参数扩展 (函数参数可以使用默认值、不定参数以及拓展参数)
5.增加class类的支持
6.增加箭头函数
7.增加模块和模块加载(ES6中开始支持原生模块化啦)
8.math, number, string, array, object增加新的API
38. Apply和call方法的异同
相同点:两个方法产生的作用是完全一样的,第一个参数都是对象;
不同点:
call()方法参数将依次传递给借用的方法作参数,即fn.call(thisobj, arg1,arg2,arg3...argn),有n个参数
apply()方法第一个参数是对象,第二个参数是数组fn.apply(thisobj,arg),此处的arg是一个数组,只有两个参数
39. 在javascript中什么是伪数组,如何将伪数组转化为标准数组
这里把符合以下条件的对象称为伪数组:
1,具有length属性
2,按索引方式存储数据
3,不具有数组的push,pop等方法
伪数组(类数组):无法直接调用数组方法或期望length属性有什么特殊的行为,不具有数组的push,pop等方法,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的argument参数,还有像调用document.getElementsByTagName, document.childNodes之类的,它们返回的NodeList对象都属于伪数组。
可以使用以下函数将伪数组转化为真正的Array对象(兼容问题处理)。
**function makeArray(c) {** **try{** **return Array.prototype.slice.call(c);** **}catch(e){** **var ret = [],i, len = c.length;** **for(i = 0; i < len; i++) {** **ret[i] = (c[i]);** **}** **return ret;** **}** **}**
40. 用程序找出数组中出现次数超过一半的数字
思路:
1、 一个数字在数组中出现次数超过了一半,则排序后,位于数组中间的数字一定就是该出现次数超过了长度一半的数字(可以用反证法证明),也即是说,这个数字就是统计学上的中位数。最容易想到的办法是用快速排序对数组排序号后,直接取出中间的那个数字,这样的时间复杂度为O(nlogn),空间复杂度为O(1)。
2 、事实上可以不用对数组进行排序,或者说仅部分排序,受快速排序的partition函数的启发,我们可以利用反复调用partition函数来求的该数字。我们现在数组中随机选取一个数字,而后通过Partition函数返回该数字在数组中的索引index,如果index刚好等于n/2,则这个数字便是数组的中位数,也即是要求的数,如果index大于n/2,则中位数肯定在index的左边,在左边继续寻找即可,反之在右边寻找。这样可以只在index的一边寻找,而不用两边都排序,减少了一半排序时间。这种情况的平均时间复杂度大致为:T(n) = n+n/2+n/4+n/8+....+1,很明显当n很大时,T(n)趋近于2n,也就是说平均情况下时间复杂度为O(n),但是这种情况下,最坏的时间复杂度依然为O(nn),最坏情况下,index总是位于数组的最左或最右边,这样时间复杂度为T(n) = n+n一1+n一2+n一3+....+1 = n(n一1)/2,显然,时间复杂度为O(nn),空间复杂度为O(1)。
请设计一套方案,用于确保页面中js加载完全,对于优化某网页的加载速度,有什么独到见解
js方法:
<script type="text/javascript"> window.οnlοad=function(){ var userName="xiaoming"; alert(userName); } </script>
jquery方法:
<script type="text/javascript"> $(document).ready(function(){ var userName="xiaoming"; alert(userName); }); </script> 或者简写: $(function(){ var userName="xiaoming"; alert(userName); });
如何确定一个js是否加载完全或者页面中的所有js加载完全,具体办法如下:
**function loadScript****( url, callback****) {** **var script = document.createElement("script");** **script.type = "text/javascript";** **if (script.readyState) {** **script.onreadystatechange = function() {** **if (script.readyState == "loaded" || script.readyState == "complete") {** **script.onreadystatechange = null;** **callback();** **}** **}** **} else {** **script.onload = function() {** **callback();** **}** **}** **script.src = url;** **document.getElementsByName("head")[0].appendChild(script);** **}**
如何让脚本的执行顺序按照你设定的顺序执行,使用嵌套的方式:
loadScript("file1.js", function() { loadScript("file2.js", function() {loadScript("file3.js", function() { alert("All files are loaded"); }); }); });
网页加载速度优化:
1、减少请求
最大的性能漏洞就是一个页面需要发起几十个网络请求来获取诸如样式表、脚本或者图片这样的资源,这个在相对低带宽和高延迟的移动设备连接上来说影响更严重。
CDNs(内容分发网络)把资源放在离用户地理位置更近的地方对解决这个问题能起到很大作用,但是比起获取请求,大量的请求对页面加载时间的影响更为严重,而且最近的发现表明,CDNs对移动端用户的性能影响越来越低。
2、整合资源
对开发者来说,将Javascript代码和CSS样式放到公共的文件中供多个页面共享是一种标准的优化方法,这个方法能很简单的维护代码,并且提高客户端缓存的使用效率。
在Javascript文件中,要确保在一个页面中相同的脚本不会被加载多次,当大团队或者多个团队合作开发的时候,这种冗余的脚本就很容易出现,你可能会对它的发生频率并不低感到非常吃惊。
Sprites是css中处理图片的一项技术,Sprites就是将多张图片整合到一个线性的网状的大图片中,页面就可以将这个大图片一次性获取回来并且做为css的背景图,然后使用css的背景定位属性展示页面需要的图片部分,这种技术将多个请求整合成一个,能显著地改善性能。
平稳地改进但是需要对资源有控制权限,根据开发者的网站不同权限,一些资源并不需要被整合起来(例如,一些由CMS生成的资源),还有,对于一些外部域引用的资源,强行整合可能会导致问题,马海祥提醒大家需要注意的是,整合资源对手机浏览器来说是一把双刃剑,整合资源确实会在首次访问减少请求,但是大的资源文件可能会导致缓存失效,所以,需要小心地使用各种技术整合资源,以达到优化本地存储的目的。
3、使用浏览器缓存和本地缓存
现在所有的浏览器都会使用本地资源去缓存住那些被Cache一Control或者Expires头标记的资源,这些头能标记资源需要缓存的时间,另外,ETag(实体标签)和Last一Modified头来标识当资源过期后是否需要重新请求,浏览器为了减少不必要的服务器请求,尽可能地从本地缓存中获取资源,并且将那些已经过期的、或者当缓存空间减小的时候将那些很久不用的资源进行清理,浏览器缓存通常包括图片,CSS,Javascript代码,这些缓存能合理地提高网站的性能(比如为了支持后退和前进的按钮,使用一个单独的缓存来保存整个渲染的页面)。
移动浏览器缓存,通常是比桌面PC小的多,这就导致了缓存的数据会很经常被清理,HTML5的缓存基于浏览器缓存提供了一个很好的替换方案,Javascript的localStorage已经在所有主流的桌面和移动端浏览器上都实现了,使用脚本代码能简便地支持HTML5的localStorage操作,可以读写键值数据,每个域名大概有5MB的容量,虽然不同的移动浏览器上读写速度相差很大,但是localStorage大容量的缓存使得它很适合作为客户端的缓存,从localStorage获取资源明显快于从服务器上获取资源,而且在大多数移动设备上也比依靠缓存头或者浏览器的本地缓存更灵活可靠,这是移动浏览器比桌面PC更有优势的一个地方,在桌面PC上,本地缓存仍然优先使用标准的浏览器缓存,导致桌面PC本地缓存的性能落后于移动浏览器。
在此,马海祥要提醒各位一下:虽然localStorage的机制易于实现,但是它的一些控制机制却是非常复杂的,你需要考虑到缓存带给你的所有问题,比如缓存失效(什么时候需要删除缓存?),缓存丢失(当你希望数据在缓存中的时候它并不在怎么办?),还有当缓存满的时候你怎么办?
4、首次使用的时候在HTML中嵌入资源
HTML的标准是使用链接来加载外部资源,这使得更容易在服务器上(或者在CDN上)操作更新这些资源,而不是在每个页面上修改更新这些资源,根据上文讨论的,这种模式也使得浏览器能从本地缓存而不是服务器上获取资源。
但是对还没有缓存到浏览器localStorage的资源来说,这种模式对网站的性能有负面的影响,一般来说,一个页面需要几十个单独的请求来获取资源从而渲染页面。
所以说,从性能的角度来说,如果一个资源没有很高的被缓存的几率的话,最好把它嵌入到页面的HTML中(叫inlining),而不是使用链接外部,脚本和样式是支持内嵌到HTML中的,但是图片和其他的二进制资源其实也是可以通过内嵌包含base64编码的文本来嵌入到HTML中的。
内嵌的缺点是页面的大小会变得非常大,所以对于Web应用来说,关键的是能够跟踪分析这个资源什么时候需要从服务端获取,什么时候已经缓存到客户端了。
另外,在第一次请求资源后必须能够使用代码在客户端缓存资源,因此,在移动设备上,使用HTML5 localStorage能很好地做到内嵌。
由于不知道用户是否已经访问过这个页面了,所以需要网站有机制能生成不同版本的页面。
5、使用HTML5服务端发送事件
Web应用已经使用了各种从服务器上轮询资源的方法来持续地更新页面,HTML5的EventSource对象和Server一Sent事件能通过浏览器端的JavaScript代码打开一个服务端连接客户端的单向通道,服务端可以使用这个写通道来发送数据,这样能节省了HTTP创建多个轮询请求的消耗。
这种方式比HTML的WebSocket更高效,WebSocket的使用场景是,当有许多客户端和服务端的交互的时候(比如消息或者游戏),在全双工连接上建立一个双向通道。
这个技术是基于具体的技术实现的,如果你的网站当前是使用其他的Ajax或者Comet技术来轮询的,转变成Server一Sent事件需要重构网站的Javascript代码。
6、消除重定向
当用户在一个移动设备上访问桌面PC网站的时候,Web网站应用通常读取HTTP的user一agent头来判断这个用户是否是来自移动设备的,然后应用会发送带有空HTTP body和重定向HTTP地址头的HTTP 301(或者302)请求,把用户重定向到网站的移动版本上去,但是这个额外的客户端和服务端的交互通常在移动网络上会消耗几百毫秒,因此,在原先的请求上传递移动的web页会比传递一个重定向的信息并让客户端再请求移动页面更快。
对于那些想要在移动设备上看桌面PC网站的用户来说,你可以在移动web页面上提供一个链接入口,这样也能同时表示你的网站是并不提倡这种行为的。
虽然这个技术在理论上是简单的,但是实际上并不易于实施,由于有些m.sites是宿主在其他地方的,所以许多网站会选择重定向到一个不同的服务器上,有的网站则是会在重定向请求的时候种植上Cookie告诉Web应用这个用户是在使用移动设备,这种方法可能对web应用来说更容易控制。
7、减少资源负载
关于移动端页面的大小问题,渲染小页面更快,获取小资源也更快,减小每个请求的大小通常不如减少页面请求个数那么显著地提高性能。
但是有些技术在性能方面,特别是在需要对带宽和处理器性能精打细算的移动设备环境下,仍然是能带来很大利益的。
8、压缩文本和图像
诸如gzip这样的压缩技术,依靠增加服务端压缩和浏览器解压的步骤,来减少资源的负载,但是,一般来说,这些操作都是被高度优化过了,而且测试表明,压缩对网站还是起到优化性能的作用的,那些基于文本的响应,包括HTML,XML,JSON(Javascript Object Notation),Javascript,和CSS可以减少大约70%的大小。
浏览器在Accept一Encoding请求头中申明它的解压缩技术,并且当它们接收到服务端返回的Content一Encoding响应头标示的时候,就会按照这个响应头自动做解压操作。
马海祥觉得这种方法的优点就是易于实现,如果设置正确的话,现在所有的Web服务器都支持压缩响应,但是,也有一些桌面PC的安全工具会将请求头中的Accept一Encoding头去掉,这样即使浏览器支持解压缩,用户也无法获取到压缩后的响应。
9、代码简化
简化通常是使用在脚本和样式文件中,删除一些不必要的字符,比如空格,换行符,或者注释等,不需要暴露给外部的命名就可以被缩短为一个或者两个字符,比如变量名,合适的简化资源通常在客户端不需要做任何其他的处理,并且平均减少20%的资源大小,内嵌在HTML中的脚本和样式文件也是可以精简的,有很多很好的库来做精简化的操作,这些库一般也同时会提供合并多个文件这样减少请求数的服务(具体可查看马海祥博客《手机网站制作的常用方法及优化技巧》的相关介绍)。
简化带来的好处并不局限于减少带宽和延迟,对于那些移动设备上缓存无法保存的过大资源来说,也是很有改善的,Gzip在这个方面并没有任何帮助,因为资源是在被解压后才被缓存起来的。
Google的Closure Compiler已经难以置信地完成了理解和简化Javascript的工作,但是CSS的简化则没有那么容易,因为对不同浏览器来说有不同的CSS技术能迷惑CSS简化工具,然后让CSS简化后无法正常工作,马海祥提醒大家必须要注意的是,已经有这样的案例了,即使只是删除了不必要的字符,简化工作也有可能破坏页面,所以当你应用简化技术之后,请做一下完整的功能测试工作。
10、调整图片大小
图片通常是占用了Web页面加载的大部分网络资源,也占用了页面缓存的主要空间,小屏幕的移动设备提供了通过调整图片大小来加速传输和渲染图片资源的机会,如果用户只是在小的移动浏览器窗口中看图片的话,高分辨率的图片就会浪费带宽、处理时间和缓存空间。
为了加速页面渲染速度和减少带宽及内存消耗,可以动态地调整图片大小或者将图片替换为移动设备专用的更小的版本,不要依靠浏览器来将高分辨率的图片转换成小尺寸的图片,这样会浪费带宽。
另外一个方法是先尽快加载一个低分辨率的图片来渲染页面,在onload或者用户已经开始和页面交互以后将这些低分辨率的图片替换成为高分辨率的图片。
特别应用在高度动态化的网站是有优势的。
11、使用HTML5和CSS 3.0来简化页面
HTML5包括了一些新的结构元素,例如header,nav,article和footer,使用这些语义化的元素比传统的使用div和span标签能使得页面更简单和更容易解析,一个简单的页面更小加载更快,并且简单的DOM(Document Object Model)代表着更快的JavaScript执行效率,新的标签能很快地应用在包括移动端的新浏览器版本上,并且HTML5设计让那些不支持它的浏览器能平稳过渡使用新标签。
HTML5的一些表单元素提供了许多新属性来完成原本需要javascript来完成的功能,例如,新的placeholder属性用于显示在用户输入进入输入框之前显示的介绍性文字,autofocus属性用于标示哪个输入框应当被自动定位。
也有一些新的输入框元素能不用依靠Javascript就可以完成一些通用的需求,这些新的输入框类型包括像e一mail,URL,数字,范围,日期和时间这样需要复杂的用户交互和输入验证的元素,在移动浏览器上,当需要输入文本的时候,弹出的键盘通常是由特定的输入框类型来做选择的,不支持指定的输入类型的浏览器就会只显示一个文本框。
另外,只要浏览器支持内建的层次,圆角,阴影,动画,过渡和其他的图片效果,CSS 3.0就能帮助你创建轻便简易的页面了,而这些图片效果原先是需要加载图片才能完成的,这样,这些新特性就能加速页面渲染了。
人工地做这些改动是非常复杂和耗时的,如果你使用CMS,它可以帮你生成许多你不需要控制的HTML和CSS(具体可查看马海祥博客《制作移动端手机网站过程中的SEO优化方法技巧》的相关介绍)。
12、延迟渲染”BELOW一THE一FOLD”内容
可以确定的是如果我们将不可见区域的内容延迟加载,那么页面就会更快地展现在用户面前,这个区域叫做“below the fold”,为了减少页面加载后需要重新访问的内容,可以将图片替换为正确的高宽所标记的标签。
一些好的Javascript库可以用来处理这些below一the一fold 延迟加载的图像。
13、延迟读取和执行的脚本
在一些移动设备上,解析Javascript代码的速度能达到100毫秒每千字节,许多脚本的库直到页面被渲染以后都是不需要的加载的,下载和解析这些脚本可以很安全地被推迟到onload事件之后来做。
例如,一些需要用户交互的行为,比如托和拽,都不大可能在用户看到页面之前被调用,相同的逻辑也可以应用在脚本执行上面,尽量将脚本的执行延迟到onload事件之后,而不是在初始化页面中重要的可被用户看到的内容的时候执行。
这些延迟的脚本可能是你自己写的,更重要的是,也有可能是第三方的,对广告、社交媒体部件、或者分析的差劲的脚本优化会导致阻塞页面的渲染,会增加珍贵的加载时间,当然,你需要小心地评估诸如jquery这样为移动网站设计的大型脚本框架,特别当你仅仅只是使用这些框架中的一些对象的时候更要小心评估。
许多第三方的框架现在提供延迟加载的异步版本的API,开发者只需要将原先的逻辑转化到这个异步版本,一些JavaScript要做延迟加载会有些复杂,因为在onload之后执行这些脚本需要注意很多注意事项(例如,你有个脚本需要绑定到onload事件上,你需要做什么?如果你将脚本延迟到onload事件之后,就一定就会失去很多执行的时机)。
14、使用Ajax来增强进程
Ajax(Asynchronous JavaScript and XML)是一项使用XHR(XMLHttpRequest)对象来从Web服务器上获取数据的技术,它并不需要更新正在运行的页面,Ajax能更新页面上的某个部分而不需要重新构建整个页面,它通常用来提交用户的交互相应,但是也可以用来先加载页面的框架部分,然后当用户准备好浏览网页的时候再填充详细的内容。
尽管是这个名字,但是XMLHttpRequest并不强制要求你只能使用XML,你可以通过调用overrideMineType方法来制定“application/json”类型来使用json替换XML,使用JSON.parse会比使用原生的eval()函数快了几乎两倍,并且更为安全。
同时,切记Ajax的返回响应也会得益于那些应用在普通的返回响应的优化技术上面,确保对你的Ajax返回响应使用了缓存头,简化,gzip压缩,资源合并等技术。
由于这个技术是根据具体应用不同而不同的,所以很难量化,或许由于跨域问题,你需要使用XHR2,这个技术能使用外部域的资源,从而能进行跨域的XHR请求。
15、根据网络状况进行适配处理
由于使用更多带宽会使用更多移动网络的费用,所以只有能检测网络的类型才能使用针对特定网络的优化技术。
例如,预加载未来使用到的请求是非常聪明的做法,但是如果用户的带宽很稀有,并且加载的有些资源是永远不会用到的话,这个技术就是不合理的了。
在Android 2.2+,navigator.connection.type属性的返回值能让你区分Wifi和2G/3G/4G网络,在Blackberry上,blackberry.network也能提供相似的信息,另外,服务端通过检测请求中的User一Agent头或者其他的嵌入到请求中的信息能让你的应用检测到网络状况。
检测网络信息的API最近已经有所变化了,接口现在不是直接定义Wi一Fi,3G等网络状况,而是给出了带宽信息和诸如“非常慢,慢,快和非常快”这样的建议,有个属性能给出估计的MB/s值和一个“meterd”的Boolean值来表示它的可信度,但是对浏览器来说,很难根据这个来判断环境,判断当前网络环境然后适配仍然是一种最好的方法(具体可查看马海祥博客《百度移动搜索开放适配服务的3种方法》的相关介绍),但是这种方法正在被考虑被替换。
16、对多线程来说尽量使用HTML5的WEB WORKER特性
HTML5中的Web Worker是使用多个线程并发执行Javascript程序,另外,这种特别的多线程实现能减少困惑开发者多年的,在其他平台上遇到的问题,例如,当一个线程需要改变一个正在被其他线程使用的资源该如何处理,在Web Worker中,子线程不能修改主用户界面(UI)线程使用的资源。
对提高移动站点的性能来说,Web Worker中的代码很适合用来预处理用户完成进一步操作所需要的资源的,特别是在用户的带宽资源不紧缺的情况下,在低处理器性能的移动设备上,过多的预加载可能会干扰当前页面的UI响应,使用多线程代码,让Web Worker对象(并且尽可能使用localStorage来缓存数据)在另外一个线程中操作预加载资源,这样就能不影响当前的UI表现了。
要特别说明的是,Web Worker只在Android 2.0以上的版本实现,而且iphone上的ios5之前的版本也不支持,在桌面PC上,总是落后的IE只在IE 10才支持Web Worker。
虽然这项技术并不是非常难实现,但是对Web Workers来说,有一些限制需要强制遵守,Web Workers不能进入到页面的DOM,也不能改变页面上的任何东西,Web Worker很适合那种需要后台计算和处理的工作。
17、将CLICK事件替换成TOUCH事件
在触摸屏设备上,当一个用户触碰屏幕的时候,onclick事件并没有立即触发,设备会使用大约半秒(大多数设备差不多都是300毫秒)来让用户确定是手势操作还是点击操作,这个延迟会很明显地影响用户期望的响应性能,要使用touchend事件来替换才能解决,当用户触碰屏幕的时候,这个事件会立即触发。
为了要确保不会产生用户不期望的行为,你应该也要使用touchstart和touchmove事件,例如,除非同时有个touchstart事件在button上,否则不要判断touchend事件在button上就意味着点击行为,因为用户有可能从其他地方触碰开始,然后拖拽到button上触碰结束的,你也可以在touchstart事件之后使用touchmove事件来避免将touchend事件误判为点击,当然前提是需要假设拖拽的手势并不是预期产生点击行为。
另外,你也需要去处理onclick事件来让浏览器改变button的外观从而标识为已点击的状态,同时你也需要处理那些不支持touch事件的浏览器,为了避免代码在touchend和onclick代码中重复执行,你需要在确保用户触碰事件已经在touchend执行了之后,在click事件中调用preventDefault和stopPropagation方法。
这种技术需要更多工作才能在一个页面中增加和维护链接,touch事件的代码必须考虑其他手势,因为替换click的还有可能是缩放或者敲击动作。
18、支持SPDY协议
应用层HTTP和HTTPS协议导致的一些性能瓶颈,使得不论是桌面还是移动端的网站都非常难受,在2009年,谷歌开始研发一种叫做SPDY(谐意是“speedy”)的协议来替换已有的协议,这种协议宣称能突破这些限制,这个协议的目标是让多种浏览器和多种Web服务都能支持,所以这个协议是开源的,但是初步地,只有Google的Chrome浏览器(在版本10及之后的)和google的站点支持,一旦一个Web服务支持SPDY,那么它上面的所有站点都可以和支持这个协议的浏览器使用SPDY进行交互,将SPDY应用在25个top100的Internet网站上,Google收集到的数据是网站的速度会改善27%到60%不等。
SPDY自动使用gzip压缩所有内容,和HTTP不同的是,它连header的数据也使用gzip压缩,SPDY使用多线程技术让多个请求流或者响应流能共用一个TCP连接,另外SPDY允许请求设置优先级,比如,页面中心的视频会比边框的广告拥有更高的优先级。
或许SPDY中最变革性的发明就是流是双向的,并且可以由客户端或者服务端发起,这样能使得信息能推送到客户端,而不用由客户端发起第一次请求,例如,当一个用户第一次浏览一个站点,还没有任何站点的缓存,这个时候服务端就可以在响应中推送所有的请求资源,而不用等候每个资源被再次独立请求了,作为替换协议,服务端可以发送暗示给客户端,提示页面需要哪些资源,同时也允许由客户端来初始化请求。即使是使用后一种这样的方式也比让客户端解析页面然后自己发现有哪些资源需要被请求来得快。
虽然SPDY并没有对移动端有什么特别的设置,但是移动端有限的带宽就使得如果支持SPDY的话,SPDY在减少移动网站的延迟是非常有用的。
依据网站和服务的环境来进行平稳操作或进一步考虑,Google有一个SPDY模块支持Apache2.2 – mod_spdy – 这个模块是免费的;但是mod_spy有线程上的问题,并且和mod_php协作并不是很好,所以要求你使用这个技术的时候要确保你的网站的正常运行。
41. 请实现鼠标点击任意标签,alert该标签的名称(注意兼容性)
function elementName(evt){
evt = evt|| window.event;
var selected = evt.target || evt.srcElement;
alert(selected.tagName);
}
42. 对string对象进行扩展,使其具有删除前后空格的方法
String.prototype.trim = function() {
return this.replace(/(^\s*)|(\s*$)/g, "");
}
43.Javascript的typeof返回哪些数据类型;列举3种强制类型转换和2中隐式类型转换
1)返回数据类型
undefined
string
boolean
number
symbol(ES6)
Object
Function
2)强制类型转换
Number(参数) 把任何类型转换成数值类型。
parseInt(参数1,参数2) 将字符串转换成整数
parseFloat()将字符串转换成浮点数字
string(参数):可以将任何类型转换成字符串
Boolean() 可以将任何类型的值转换成布尔值。
3)隐式类型转换
1.四则运算
加法运算符+是双目运算符,只要其中一个是String类型,表达式的值便是一个String。
对于其他的四则运算,只有其中一个是Number类型,表达式的值便是一个Number。
对于非法字符的情况通常会返回NaN:
'1' * 'a' // => NaN,这是因为parseInt(a)值为NaN,1 * NaN 还是 NaN
2.判断语句
判断语句中的判断条件需要是Boolean类型,所以条件表达式会被隐式转换为Boolean。 其转换规则同Boolean的构造函数。比如:
var obj = {};if(obj){
while(obj);}
3.Native代码调用
JavaScript宿主环境都会提供大量的对象,它们往往不少通过JavaScript来实现的。 JavaScript给这些函数传入的参数也会进行隐式转换。例如BOM提供的alert方法接受String类型的参数:
alert({a: 1}); // => [object Object]
44.写出3个使用this的典型应用
function Thing() { } Thing.prototype.foo = "bar"; Thing.prototype.logFoo = function () { console.log(this.foo); } Thing.prototype.setFoo = function (newFoo) {this.foo = newFoo; } var thing1 = new Thing(); var thing2 = new Thing();thing1.logFoo(); //logs "bar" thing2.logFoo(); //logs "bar" thing1.setFoo("foo");thing1.logFoo(); //logs "foo"; thing2.logFoo(); //logs "bar"; thing2.foo = "foobar";thing1.logFoo(); //logs "foo"; thing2.logFoo(); //logs "foobar";
function Thing1() { } Thing1.prototype.foo = "bar"; function Thing2() { this.foo = "foo"; } Thing2.prototype = new Thing1(); function Thing3() {} Thing3.prototype = new Thing2(); var thing = new Thing3(); console.log(thing.foo); //logs "foo"
function Thing() {} Thing.prototype.foo = "bar"; Thing.prototype.logFoo = function () { function doIt() { onsole.log(this.foo); } doIt.apply(this); } function doItIndirectly(method) { method(); } var thing = new Thing();doItIndirectly(thing.logFoo.bind(thing)); //logs bar
三、VUE面试题(带数字,带序号)
1、v-show 与 v-if 有什么区别?
相同点: v-show和 v-if都是 控制 dom元素 的 显示和隐藏 的。
不同点: 1、原理:
v-show是通过控制元素的样式属性display的值,来完成显示和隐藏;
v-if是通过对dom元素的添加和删除,完成显示和隐藏
2、使用场景:由原理(做法)得出使用场景的区别
v-show:使用在dom元素频繁切换的场景
v-if:当dom元素的切换不频繁,可以使用。特别是,首次元素处于隐藏的情况下。
2、说说你对 SPA 单页面的理解,它的优缺点分别是什么?
1)、首先:
SPA的英文是single-page application ,也就是说整个项目中只有一个页面。
单页面应用的实现思路: 就是在 Web 页面初始化时加载所有的 HTML、JavaScript 和 CSS,页面的内容的变化,靠动态操作DOM。
2)、其次:
说说它的优点和缺点:
它的优点有三点:
第一点:**局部刷新。**用户体验好、快,内容的改变不需要重新加载整个页面。
第二点:**服务器的压力小。**基于上面一点,SPA 对服务器的压力小;
第三点:**前后端职责分离。**架构清晰,前端进行交互逻辑,后端负责数据处理;
它的缺点也有三点:
第一点:**初次加载耗时多。**为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
第二点:**前进后退路由管理问题。**由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理(这是vue-router做的);
第三点:**SEO 难度较大。**由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
3、怎样理解 Vue 的单向数据流
首先
单项数据流是发生在**父组件朝子组件传值的时候**,所有的 prop 使得其父子 prop 之间形成了一个单向下行绑定。
也就是说:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态。
而且
每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。所以,在一个子组件内部改变 prop,Vue 会在浏览器的控制台中发出警告。
4、computed 和 watch 的区别和运用的场景?
首先(相同点):
computed 和 watch都可以实现数据的监听。
其次(区别):
第一(本质): computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,当依赖的属性值发生改变时,才会重新计算 computed 的值,它可以设置getter和setter.watch: 更多的是观察的作用,每当监听的数据变化时都会执行回调进行后续操作,它只能设置getter.
第二(运用场景): computed:当我们需要进行数值计算,并且依赖于其它数据时,使用 computed。 watch:当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch。
5、直接用下标(索引)给一个数组项赋值,Vue 能检测到变化吗?
不能
因为,vue再做数据劫持时,只对数组做了劫持,没有对数组的每一项进行劫持。所以,用下标的方式修改数组项时,vue不能检测到数据的变化。
为了解决他们,Vue 也提供了操作方法: Vue.set Vue.set(数组名,下标,新值)
6、谈谈你对 Vue 生命周期的理解?
1)、生命周期是什么? Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
2)、各个生命周期阶段及其钩子函数
vue的生命周期核心经历了四个阶段,在四个阶段的前后分别有两个钩子函数。
第一阶段:数据挂载阶段:把配置项data中的属性,赋给了vue对象本身,并做了数据劫持。
该阶段前后的两个钩子函数:beforeCreate和created
第二阶段:模板渲染阶段:把vue对象的数据渲染到模板上。
该阶段前后的两个钩子函数:beforeMount和mounted
第三阶段:组件更新阶段:当数据发送变化时,组件会进行重新渲染,所以,准确的说是,组件重新渲染阶段。
该阶段前后的两个钩子函数:beforeUpdate和updated
第四阶段:组件销毁阶段:组件销毁。
该阶段前后的两个钩子函数:beforeDestroy和destroyed
可以补充:
当使用keep-alive包裹组件时,会有组件激活和停用之说,这两个钩子函数分别是:activited和deactivated
还可以补充:
项目场景,如:发送请求,如:父组件修改子组件的props引起子组件的更新等等。
详情请参考: 彻底理解vue的钩子函数,vue的生命周期理解,什么是vue的生命周期,钩子函数_田江的博客-CSDN博客_vue钩子函数和生命周期
7、Vue 的父组件和子组件生命周期钩子函数执行顺序?
Vue 的父子组件钩子函数的执行顺序可以归类为4个 部分:
第一部分:首次加载渲染:
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
第二部分:父组件修改子组件的props值时: 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
第三部分:父组件修改的数据跟子组件没有关系时:
不会影响子组件 父 beforeUpdate -> 父 updated
第四部分:销毁过程:
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
8、在哪个生命周期内调用异步请求?
答:大部分时候,会在created发送请求。 因为, 1)、此时data已经挂载到vue实例了,放在beforeCreate里太早,vue实例没有数据,放在mounted里太晚。 2)、放在 created 中有助于一致性,因为ssr不支持 beforeMount 、mounted 钩子函数。 Created的使用场景:如果组件的初始数据来自于后端,那就在created里发送请求
9、组件中 data 为什么是一个函数?
如果不用函数,即:data是引用各类型,那么所有组件对象的data会指向同一块内存区域,导致数据之间互相影响。
使用函数时,每次实例化组件时(<标签名>),调用(data)函数,return一个新(new)的data对象。这样每个组件实例的data所指向的内存空间是独立的,data的 属性值不会互相影响。
10、v-model 的原理?
v-model 指令主要用在表单元素上实现数据双向绑定的。 例如:input、textarea、select 等创建双向数据绑定,本质上不过是语法糖,其实v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
11、Vue 实现数据绑定的原理
vue数据绑定是通过 数据劫持和观察者模式 的方式来实现的。
1、数据劫持:使用Object.defineProperty();
目的是:**感知属性的变化**。当给属性赋值时,程序是能够感知的(知道的)。如果知道的话,就可以控制属性值的有效范围,也可以改变其它属性的值等。
当你把一个普通的 JavaScript 对象(json)传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
Object.defineProperty()函数:JavaScript中的Object.defineProperty()函数_田江的博客-CSDN博客
2、观察者模式(发布订阅模式):
目的是:**当属性发生变化时,使用该数据地方(模板,计算属性,watch等等)跟着变化**
12、Vue 组件间通信有哪几种方式?
Vue 组件间通信主要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式:
(1)props / $emit : 父子组件通信
(2)ref: 适用 父子组件通信
(3)EventBus ($emit / $on): 适用于 父子、隔代、兄弟组件通信
(4)$root:集中管理,适用于所有场景
(5)Vuex 适用于所有场景
13、谈谈你对 keep-alive 的了解?
keep-alive 可以缓存其它组件及其的组件的状态,避免了组件的频繁创建和销毁。
它有三个特性:
1)、用于缓存组件,一般结合路由和动态组件一起使用。
2)、提供 include 和 exclude 属性。两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
3)、对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。
14、Vue 中的 key 有什么作用?
首先
key 是Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
再说一下,diff算法的执行过程:
1)、oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex, 2)、在新节点和旧节点进行两两对比时,共有4种比较方式: a.newStartIndex 和oldStartIndex 、 b.newEndIndex 和 oldEndIndex 、 c.newStartIndex 和 oldEndIndex 、 d.newEndIndex 和 oldStartIndex, 如果以上 4 种比较都没匹配,如果设置了key,就会用 key 再进行比较。
15、你对 Vue 项目进行哪些优化?
第一个方面:代码层面的优化 v-if 和 v-show 区分使用场景 computed 和 watch 区分使用场景 v-for 遍历必须为 item 添加 key,且避免同时使用 v-if 长列表性能优化 事件的销毁 图片资源懒加载 路由懒加载 第三方插件的按需引入 优化无限列表性能 服务端渲染 SSR or 预渲染 第二个方面:Webpack 层面的优化 Webpack 对图片进行压缩 减少 ES6 转为 ES5 的冗余代码 提取公共代码 模板预编译 提取组件的 CSS 优化 SourceMap 构建结果输出分析 Vue 项目的编译优化 第三个方面:基础的 Web 技术的优化 开启 gzip 压缩 浏览器缓存 CDN 的使用 使用 Chrome Performance 查找性能瓶颈
16、动态路由传参2种方式params与query
1)、params
传
声明式:
<router-link :to="{ name: 'user', params: { id: 123 }}">User</router-link>
编程式:
$router.push({ name: 'user', params: { id: '123' }})
接
//模板里的写法:
$route.params.参数名
//脚本里的写法:
this.$route.params.参数名
动态路由匹配也行。
1)、路由配置:{ path: '/user/:id', component: User }
2)、传参:
//声明式 <router-link to="/user/01001">用户01001的信息</router-link> //编程式 $router.push("/user/01001");
3)、接值:
//模板里的写法: $route.params.参数名
//脚本里的写法: this.$route.params.参数名
2)、query
传:
// 带查询参数,变成 /register?plan=private
$router.push({ path: '/register', query: { plan: 'private' }})
注意:如果提供了 path,那么params 会被忽略
接:
//模板里的写法:
$route.query.参数名
//脚本里的写法: 0000
this.$route.query.参数名
区别: 1.首先就是写法得不同,query 得写法是 用 path 来编写传参地址,而 params 得写法是用 name 来编写传参地址,你可以看一下编写路由时候得相关属性,你也可以输出一下 路由对象信息 看一下 2.接收方法不同, 一个用 query 来接收, 一个用 params 接收 ,总结就是谁发得 谁去接收 3.query 在刷新页面得时候参数不会消失,而 params 刷新页面得时候会参数消失,可以考虑本地存储解决 4.query 传得参数都是显示在url 地址栏当中,而 params 传参不会显示在地址栏
17、vue实例和vue组件写法的区别
1、 data是个函数(面试题) 一个组件的 data 选项必须是一个函数,且要有返回object,只有这样,每个实例(vue组件对象)就可以维护一份被返回对象的独立的拷贝,否则组件复用时,数据相互影响,也就是说,组件的作用域是独立的。 2、组件模板(html代码)只能有一个根标签 3、组件名不可和html官方的标签名同名 4、组件没有el选项,只有根实例存在el 5、书写:组件名如果驼峰,那么使用时,用短横线(羊肉串的写法)
18、谈谈你对vueX的理解
1、vueX是干什么的
vuex能够**保存全局数据**,供整个应用使用,可以在组件之间传递数据。
vuex保存的数据是**响应式的**
vuex保存的数据可以**跟踪状态的变化**
2、vueX的核心概念
state : 数据仓库 ,存储所有的 **共享数据** ,相当于vue组件里的data
getter : 在state的基础上 **派生的数据**, 相当于vue组件里 computed
mutation:修改state的数据时,用mutation,这与**跟踪状态** 有关系
action:解决mutation里只能有同步代码的问题,action里可以有**异步代码**
3、vueX的数据流
组件里 dispatch(派发)vueX中的 action,action里commit(提交)mutation,mutation里修改state。state被修改后,会响应式到组件上。
![1599989118212](C:\Users\31759\AppData\Roaming\Typora\typora-user-images\1599989118212.png)
19、vue路由懒加载
vue的SPA(Single Page Application)应用里,当(webpack)打包构建时,会把所有的js打在一起,JavaScript 包会变得非常大,并在第一次请求时全部下载完毕,影响页面加载(性能)。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
目前有三种方式实现路由组件的懒加载,分别是:
- vue异步组件
- es 的 import()
- webpack的require.ensure()
1)、 vue异步组件
把路由配置,进行修改
{
path: '/shopcar',
name: 'shopcar',
component: resolve => require(['@/pages/ShopCar'],resolve)
},
1)、运行是打开chrome的network,就会看到进入路由 /shopcar 时,会多出另外 一个js文件。一般是0.js或者1.js
2)、用npm run build打包时,wepback就会多打了一个 js文件(如:0.b5a82d6947b2e30edcc8.js),这个js文件就是把ShopCar文件进行了单独打包。同样的在network里,就会看到进入路由 /shopcar 时,多出一个单独的js文件的请求
注:这种方式,webpack会把每个异步组件单独打成一个js文件。
2)、es的import()
主要是把原来的引入方式进行修改 ,路由配置就不用改了:
1、不指定webpackChunkName,每个组件单独打一个js文件
原来是:import ShopCar from '@/pages/ShopCar'
修改后:const ShopCar = () => import('@/pages/ShopCar');
修改后的做法是定义了一个函数,由于函数不调用不执行,所有,一开始时,并不会引入该组件,只有路由跳转时才会调用该函数。
2、指定webpackChunkName,相同的webpackChunkName就会打在同一个js文件里
1)、以下两个组件的webpackChunkName相同,所以,打在一个js文件里
const ShopCar = () => import(/* webpackChunkName: 'demot' */ '@/pages/ShopCar');
const GoodsDetailPage = () => import(/* webpackChunkName: 'demot' */ '@/pages/GoodsDetailPage');
2)、下面这个组件的webpackChunkName和上面两个不一样,所以,单独打在一个js文件里
const Login = () => import(/* webpackChunkName: 'demoty' */ '@/pages/Login');
3)、webpack的require.ensure()
这种方式,只改路由配置即可。
如:
{
path: '/GoodsDetailPage',
name: 'GoodsDetailPage',
component: r => require.ensure([], () => r(require('@/pages/GoodsDetailPage')), 'demot')
},
{
path: '/Login',
name: 'Login',
component: r => require.ensure([], () => r(require('@/pages/Login')), 'demot')
},
{
path: '/shopcar',
name: 'shopcar',
component: r => require.ensure([], () => r(require('@/pages/ShopCar')), 'demoty')
},
以上代码中,我把Login和GoodsDetailPage使用了相同的chunkName
20、MV*(MVC,MVP,MVVM)
答:
这是项目的架构模式。优点:耦合度低、重用性高、生命周期成本低、部署快、可维护性高、有利软件工程化管理。
**1、MVC是从后端演变后的项目架构模式。**
M:model,模型,主要完成业务功能,在数据库相关的项目中,数据库的增删改查属于模型(重点)。
V:view,视图,主要负责数据的显示
C:controller,控制器,主要负责每个业务的核心流程,在项目中体现在路由以及中间件上。
2、MVP
MVP是由MVC演变过来的。
P:Presenter 代替里C。
在MVP中View并不直接使用Model,而在MVC中View可以绕过Controller从直接Model中读取数据。
3、MVVM
MVVM是Model-View-ViewModel的缩写,MVVM模式把Presenter改名为ViewModel,基本与MVP模式相似。 唯一区别是:**MVVM采用数据双向绑定的方式**
在做vue的开发时,程序员写的代码和vue框架本身合起来是属于MVVM模式。
21、你了解Vue.js吗?
这种题,一般是比较难以回答的,问得很模糊,
如果不想多说,那就直接回答:了解并做过五个项目。
如果想回答详细的话,参考思路如下:
1)、vueJS是基于MVVM的JS框架
2)、有(常见的)13个指令:
3)、有(常见的)8个配置项:el,data,computed,watch,components,filter,directives,mixins
4)、vue实例生命周期分为四个阶段,八个生命周期函数
5)、vue做项目时会用到的全家桶技术:vueJS框架,vueX,vue-router,aixos,vant组件库等等
6)、我用vue框架一共做过五个项目。
………………………………
22、vue-router的两种路由模式的区别
路由模式分为两种:hash和history;通过设置vueRouter对象的mode属性来完成修改。
区别:
1)、外观上
hash模式时,路径上有#。
history模式时,路径上没有#。
2)、原理上
**hash模式通过修改location.href来完成**
使用锚点连接的思路,使用hash模式不会给后端发请求。当然,在hash值变化时,会同时触发window对象的onhashchange事件,并可以通过事件对象的oldURL属性和newURL属性 得到新旧URL。
<body>
<div>
<a href="#p01" >p01</a><br/>
<a href="#p02" >p02</a><br/>
<a href="#p03" >p03</a>
</div>
<br/><br/><br/><br/><br/><br/>
<a name="p01">我是第一个p</a>
<br/><br/><br/><br/><br/><br/>
<a name="p02">我是第二个p</a>
<br/><br/><br/><br/><br/><br/>
<a name="p03">我是第三个p</a>
<br/><br/><br/><br/><br/><br/>
</body>
</html>
<script>
window.onhashchange = function(event){
console.log("旧url",event.oldURL);
console.log("新的url",event.newURL);
}
</script>
2)、**通过修改history.pushState来完成**
如:
window.history.pushState(null,null,"p01.html");
window.location.reload();//想测试的话,尽量加上这句话,要不然,地址变了,但是页面并没有出现。
history模式会给后端发请求(如果刷新当前页面的话),一旦,后端和前端提供了同样的路径,那么,浏览器的请求结果就会有问题,到底是后端的资源还是前端的资源(不同的后端处理思路不停),还好,我们一般在后端apiserver的请求路径的前面习惯性的有个 /api。
所以,由于不同服务端的处理思路不同。所以,需要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 `index.html` 页面(单页面),这个页面就是你 app 依赖的页面。否则,就会返回404。
你可以改成history的模式,测试一下,如果刷新当前页面,那么,浏览器会朝后端发送请求(给当前路径)。
23、Vue路由守卫的三种方式,及其钩子函数和参数
1)、全局守卫
全局守卫有前置守卫和后置守卫,是vueRouter对象的两个钩子函数,分别是 beforeEach和afterEach。
**前置守卫:**
router.beforeEach((to, from, next) => {
// to: 目标路由
// from: 当前路由
// next() 跳转 一定要调用
next(false);//不让走
next(true);//继续前行
next('/login')//走哪
next({path:'/detail/2',params:{},query:{}})//带点货
}
后置守卫:
router.afterEach((to,from)=>{
//全局后置守卫业务
})
如果能够回答上过程,肯定有加分:
//过程: 1、请求一个路径:如:/Index 2、经历前置守卫 决定了能去哪个路径 3、根据去的路径,找对应component(路由配置) 4、经过后置守卫 5、创建组件
2)、路由独享守卫
写在路由配置里。钩子函数名:beforeEnter,只有前置守卫
如:
// src/router/index.js
{
path: '/user',
component: User,
beforeEnter: (to,from,next)=>{ //路由独享守卫 前置
console.log('路由独享守卫');
if(Math.random()<.5){
next()
}else{
next('/login')
}
}
}
3)、组件内部守卫
写在组件对象里。分别有前置守卫,后置守卫,路由改变守卫(当前组件被复用的情况,不是路径改变)三个钩子函数。
export default{
data(){return {}}
……………………
//组件内部钩子
beforeRouteEnter (to, from, next) {//前置
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {//后置
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
24、为何vue采用异步渲染?
1、vue采用异步队列渲染是为了提高性能,在异步队里会去掉重复的无效的渲染。
当vue中的数据发生改变后,vue框架会把该数据引起的dom更新放入异步队列( 缓冲在同一事件循环中发生的所有数据变更 ),进行排队。 如果同一个 watcher 被多次触发,只会被推入到队列中一次 。 这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的 。
2、如果不采用 异步渲染,而是在数据发生改变后,立即更新dom,如果有重复无效的渲染,那么,就会造成性能的浪费。
25、nextTick实现原理?
1)、为什么用Vue.nextTick( )
首先,JS是单线程的,那么,它如何处理异步操作。
-
所有同步任务都在主线程上执行,形成一个执行栈。
-
主线程之外,会存在一个任务队列,只要异步任务有了结果,就在任务队列中放置一个事件(所以,也叫事件队列),进行排队(处于等待状态)。
-
当执行栈中的所有同步任务执行完后,就会读取任务队列(事件队列)中的任务(事件)。即:任务队列中的任务就结束了等待状态,进入执行栈。
-
主线程不断重复第三步。直到任务队列和执行栈里的代码执行完毕。
了解一个事件循环: https://blog.csdn.net/jiang7701037/article/details/95887439
其次,vue更新DOM的思路。使用的就是**异步**更新队列,所以,就使用了事件循环。目的是提高性能,避免无效的重复的DOM更新。即:vue中更新数据后,并不会立即更新DOM,而是把数据引起的DOM更新放入到异步更新队列里。等待下次事件循环(tick),并在两个tick之间进行UI渲染。这样程序员就不能在更改数据后,立即获取更新后的DOM,也不知道什么时候DOM能够更新。基于此,vue提供了nextTick函数。让程序员操作更新后DOM的代码放入到nextTick的回调函数里。由nextTick内部,在更新完DOM后,调用回调函数。
示例代码:
this.msg = "hello"
this.$nextTick(()=>{
操作更新后DOM的代码。
});
2)、什么是Vue.nextTick()
Vue.nextTick的代码思路示意
function nextTick(cb){
//DOM 更新
cb();
}
那么,vue是如何知道DOM更新了?
- MutationObserver:这是HTML5新增的API。用于监视DOM变动的接口,它可以监听一个DOM对象上发生的子节点删除、属性修改、文本内容修改等
- 另外,考虑到,微任务比宏任务耗时少,浏览器的兼容性。所以,vue中延迟调用优先级如下: Promise > MutationObserver > setImmediate > setTimeout
3)、应用场景:
在Vue生命周期的created()钩子函数里,如果要进行DOM操作,一定要把DOM操作放在Vue.nextTick()的回调函数中。
在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作应该放在Vue.nextTick()的回调函数中。
26、何时需要使用beforeDestroy?
总体来说,需要清除的是:当前组件不会自动销毁的数据(不属于当前组件的数据),并且该数据只是在当前组件里使用。
1)、清除定时器(定时器是window对象的,不主动清除,是不会清除的)
2)、$on方法,那需要在组件销毁前解绑。($on虽然属于Vue的实例方法,但是,这个实例很有可能不是当前vue组件(如:事件总线中的用法))
3)、解除事件的绑定 scroll mousemove (这些事件是绑定在window对象或者document对象上的)
27、Vue中v-html会导致哪些问题
1)、可能会导致XSS攻击。因为V-html更新的是元素的 innerHTML 。内容按普通 HTML 插入, 不会作为 Vue 模板进行编译 。
2)、在单文件组件里,scoped 的样式不会应用在 v-html 内部。因为那部分 HTML 没有被 Vue 的模板编译器处理。怎么解决呢?如果你希望针对 v-html 的内容设置带······作用域的 CSS,你可以替换为 CSS Modules 或用一个额外的全局 <style>元素手动设置类似 BEM 的作用域策略。
3)、后台返回的html片段,以及css样式和js,但是返回的js是不执行的,因为浏览器在渲染的时候并没有将js渲染,这时要在$nextTick中动态创建script标签并插入
29、为什么v-for与v-if不能连用
应该说,建议不要连用,或者说,在循环时,通过if只能拿到少部分数据时,建议不要使用。
原因: v-for比v-if优先级高,如果遍历的数组元素个数比较多,但是满足v-if条件比较少的情况下。会浪费性能。而且,每次刷新页面时,都会执行这样性能不高的代码。
解决:可以在computed里循环数组,通过filter的方式,过滤出需要的数据。而computed是有缓存的,这样可以节约内存
如:
computed:{
isCompleteTask:function(){
return this.todos.filter(item=>item.isComplete);
}
}
30、如何自定义v-model
答: 使用vue组件中的选项model。
首先,官方组件里可以使用v-model。而且,vue框架针对官方组件(文本框,单选钮,复选框,下拉框)都有绑定的属性和事件。如:文本框所绑定的属性是value,绑定的事件是input。
那么,自定义组件里,如何使用v-model指令。就需要在vue组件里增加model选项。
如下示例:
Vue.component('my-checkbox', {
props:['ccc','ddd'],//声明了自定义组件的属性。
model: {
prop: 'ccc', //表示v-model所绑定的属性是 ccc
event: 'change'//表示v-model所绑定的事件是是 change
},
……………………
}
官网地址: https://cn.vuejs.org/v2/api/#model
31、什么是作用域插槽
官网地址: https://cn.vuejs.org/v2/api/#vm-scopedSlots
32、diff算法的时间复杂度
33、描述组件渲染和更新过程
34、vue模板编译原理
35、vue中相同逻辑如何抽离
36、为什么要使用异步组件
37、Vue3.0你知道哪些改进
-
A一Za一z ↩