面试准备---202年6月15日

HTML部分

你是如何理解 HTML 语义化的?

HTML语义化就是使用合适的标签书写合适的内容,避免使用过多无意义的标签,例如div。举例来说:段落使用p标签,标题使用h1-h6,连接使用a标签,动画使用canvas等。HTML语义化可以便于搜索引擎更好的解析代码,也利于浏览器爬虫获得更多的信息,并且对团队的合作开发和维护也更有利。

meta viewport 是做什么用的,怎么写?

针对移动端优化,在手机上,不想让页面缩放,不采用媒体查询,应使用meta vieport。Viewport 就是浏览器窗口,它不是一个 HTML 结构,因而不能通过 CSS 去改变它的形态。

<meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1 minimum-scale=1 user-scalable=no”>
  • width: 控制视口的宽度,
  • device-width:表示缩放为100%时,以css像素计量的屏幕宽度
  • initial-scale:控制页面最初加载时的缩放等级
  • minimum-scale:允许用户的最小缩放值,为一个数字,可以带小数
  • maximum-scale:允许用户的最大缩放值,为一个数字,可以带小数
  • user-scalable:是否允许用户缩放,
    浏览器会扩展视口(而不是放大页面)来适应屏幕

你用过哪些 HTML 5 标签?

  • section:定义文档中的一个章节
  • header:定义页面和文章的头部
  • footer:定义文章的底部
  • main:定义文章的主要内容
  • aside:定义侧边栏内容
  • canvas:绘制图形
<canvas id = "canvas" width="150" height="150"></canvas> 
//width和height已经限制了canvas 的长度和宽度,并且width和height是HTML标签属性,
//不是使用CSS设置的。使用css对width和height进行设置,只是拉伸了canvas的长宽,
//并不是真的修改了.
const canvas = document.getElementById('canvas')
//获取2d渲染上下文
const ctx = canvas.getContext('2d')
//填充
ctx.fillStyle = "red"
//画矩形
ctx.fillRect(10,10,3000,3000)
//上面限制了canvas的跨宽高,所以3000,3000最大只能为150-10,150-10
  • video:用于在文档中嵌入媒体播放器,用于支持当前文档内的视频播放。
<video controls>
  <source src="myVideo.mp4" type="video/mp4">
  <source src="myVideo.webm" type="video/webm">
  <p>Your browser doesn't support HTML5 video. Here is
    a <a href="myVideo.mp4">link to the video</a> instead.</p>
</video>
  • audio:用于在文档中表示多个音频内容。
//使用source元素
<audio controls="controls">
  Your browser does not support the <code>audio</code> element.
  <source src="foo.wav" type="audio/wav">
</audio>
//不适用source元素
<audio src="http://developer.mozilla.org/@api/deki/files/2926/=AudioTest_(1).ogg" autoplay>
  Your browser does not support the <code>audio</code> element.
</audio>
  • touchstart事件,touchmove事件,touchend事件

H5 是什么?

H5表示微信等移动端的解决方案。这个解决方案包括HTML5的新特性,例如audio,canvas,拖曳特性,本地存储等。也包括盒模型,绝对定位等一系列的前端知识。H5特指基于HTML5技术的交互网页应用,以商业用途为主。

H5动画,微信H5

HTML5 跨平台性,更有效的数据推送,本地存储,无插件播放视频audio,图像动画canvas

HTML5新特性包括哪些?

  1. 语义化更好的标签
1. 结构标签:article,header,nav,footer,aside,section
2. 其他元素:video,audio,canvas,menu,meter,time
  1. 废除了一些部分浏览器支持的元素,以及对页面产生负面影响的元素。html5支持iframe
  2. 新增了一些API,
  • canvas
获取canvas的上下文对象,利用该上下文的绘图功能进行绘制
let ctx = canvas.getContext('2d')
  • Svg是html5的另一种图形功能,是一种矢量图形,是一种文件格式,有自己的API
<svg height = "100" width = "100">
    <circle cx = "50" cy = "50" r = "50"></circle>
</svg>
  • 音频和视频(video和audio),浏览器原生支持,不需要在安装。媒体元素也向web页面提供了通用,集成可脚本化的API
  • XMLHttpRequest Level2:改进了XMLHttpRequest和进度事件,实现了使用CORS实现XMLHttpRequest,跨源请求包含一个Origin 头部,他为HTTP提供了请求的头部信息。
  • WebSockets:要连接远程主机,只需新建一个WebSocket实例,提供希望连接的对端URL。
  • Web Storage API:sessionStorage(保存在session中,浏览器关闭,数据消失)、localStorage(保存在客户端本地,除非手动删除,否则一直保存)
  • Web Workers:可以让应用程序具有处理后台的能力,对于多线程的支持非常好,但是它不能直接访问web页面和DOMapi。
  • 拖放API:draggable属性、拖放事件(dragstart、drag、dragenter、dragleave、dragover、drap、dragend)、dataTransfer对象
  • 新表单特性和函数:placeholder、autocomplete、autofocus、spellcheck、list特性、datalist元素、min和max、step、required

Label标签的 作用是什么?

Label表情通常写在form表单内,通常关联一个控件

  • 语法
<label for = "username" accesskey = "N">
  <input id = "username" type = "text">
</lbael>
  • 含义
    for属性:表示这个label标签是为了那个控件服务的,通常对应input标签的ID或Name,当点击Label标签时,所绑定的空间元素将获得焦点,点击label所包裹的内容,自动指向for指定的Id 或Name。
    accesskey属性:定义了访问这个空间的热键( 所设置的快捷键不能与浏览器的快捷键冲突,否则将优先激活浏览器的快捷键)

CSS部分

两种盒模型分别说一下

  • 盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border);
  • IE盒模型: width= content+ padding + border
  • W3C标准盒模型: width=content
box-sizing: content-box ;标准盒模型 (默认)
bo-sizing:border-box; IE盒模型
  • 平时使用border box,因为更好用。因为我们的网页逐渐从固定尺寸转变为使用百分比来自适应尺寸,当一个盒子内的两个内容有padding的时候,使用content-box计算起来就比较麻烦,此时使用border-box的优势就体现了。

如何垂直居中?

flex布局怎么用,常用属性有哪些?

  • flex布局是一维的布局。
  • display:flex:容易变成flex容器,容器内的直接子元素为flex元素
  • flex-direction:定义主轴 row,row-reverse,column,column-reverse
  • flex-wrap:wrap:实现多行效果。
  • flex-frow:为flex-directionflex-wrap:wrap的缩写
  • flex-grow:flex-grow属性是处理flex元素在主轴上增加空间的问题
  • flex-shrink属性是处理flex元素收缩的问题。
  • align-items: 属性可以使元素在交叉轴方向对齐。
  • justify-content:属性可以使元素在主轴方向对齐。

BFC 是什么?

  1. 清楚浮动,父元素设置overflow:hidden / float:left
  2. 实现左右布局,左边元素float: left,width: 100px; 右边元素overflow: auto
  3. FC会阻止垂直(父子)的外边距折叠:只要把父元素设置为BFC就可以了。
  • 可以触发BFC
  1. 浮动元素(元素的 float 不是 none)
  2. 绝对定位元素(元素的 position 为 absolute 或 fixed)
  3. 行内块元素
  4. overflow 值不为 visible 的块元素
  5. 弹性元素(display为 flex 或 inline-flex元素的直接子元素)

CSS 选择器优先级

  1. 越具体优先级越高
  2. 后面的会覆盖前面的
  3. !important优先级最高,但不建议经常使用

清除浮动说一下

.clearfix:after {
  display: block;
  content: '';
  clear: both;
}
//IE兼容
 .clearfix{
     zoom: 1; /* IE 兼容*/
 }

响应式布局

  1. 响应式布局指的是同一页面在不同的屏幕有不同的布局。
  2. 响应式布局与自适应布局的区别:
    响应式布局只有一个页面,根据视口分辨率的不同,做不通的代码代理,之后显示相应的内容和布局,自适应布局,有很多页面,根据视口分辨率的不同,判断是Pc端还是手机端等,之后返回一套完成的页面用于展示。
  3. 响应式布局的实现方案
  • 媒体查询@media + px:可以针对不同的媒体类型写对应的样式,当重置浏览器窗口大小的过程中,页面会根据浏览器的宽度和高度渲染页面。
    判断移动优先还是PC优先
因为后面的样式会覆盖前面的样式,
移动优先应使用min-width
PC优先,应使用max-width
  • 百分比布局
    通过百分比单位,可以使浏览器组件中的宽高随着浏览器的宽高的变化而变化,从而实现响应式布局。
  • rem布局
    rem 是css3新增的属性,并且移动端的支持度很高,rem 的单位是相对于根元素html的font-size来确定的。根元素的font-size提供了一个基础,当页面的size发生变化的时候,只要更改font-size即可,那么依赖font-size的rem 就自动变化。
    rem 布局的缺点:在响应式布局中,必须通过js来动态控制根元素font-size的大小,也就是说css样式和js代码有一定的耦合性,且必须将改变font-size的代码放在css样式之前
  • 视口单位
    vw:表示相对于视图窗口的宽度。vh:表示相对于视图窗口的高度.
    依赖视口大小而自动缩放,无论视口过大还是过小,它也随着时候过大或者过小,失去了最大最小宽度的限制.可以结合rem
  • 图片响应式:图片能够随着页面的大小进行缩放
max-width:100%; height: auto
不用width,是因为width无法进行图片的自适应

使用srcset

<img srcset="photo_w350.jpg 1x, photo_w640.jpg 2x" src="photo_w350.jpg" alt="">
  • 媒体查询需要注意点
设置viewport
媒体查询
字体的适配(字体单位)
百分比布局
图片的适配(图片的响应式)
结合flex,grid,BFC,栅格系统等已经成型的方案

:before和:after的区别是什么?

  • 他们都是伪类元素
  • :before在元素渲染逻辑头部添加信息
  • :after在元素渲染逻辑后面添加信息
  • 并且他们添加的信息不会对页面结构造成影响

你用过哪些伪类?

  1. a标签相关伪类
a:link {color:#FF0000;} /* 未访问的链接 */
a:visited {color:#00FF00;} /* 已访问的链接 */
a:hover {color:#FF00FF;} /* 鼠标划过链接 */
a:active {color:#0000FF;} /* 已选中的链接 */
  1. :focus
  2. :first-child
  3. :first-of-type
  4. :last-child

animation对应的属性

  • animation属性用来指定一组或多组动画,之间用逗号分隔。
  • animation-name: 属性指定应用的一系列动画,每个名称代表一个由@keyframes定义的动画序列。
  • animation-duration: 性指定一个动画周期的时长。
  • animation-timing-function:属性定义CSS动画在每一动画周期中执行的节奏。可能值为一或多个 <timing-function>
  • animation-delay:属性定义动画于何时开始,即从动画应用在元素上到动画开始的这段时间的长度。
  • animation-itaration-count:定义动画在结束前运行的次数 可以是1次 无限循环.
  • animation-direction:属性指示动画是否反向播放,它通常在简写属性animation中设定
  • animation-fill-mode:设置CSS动画在执行之前和之后如何将样式应用于其目标。
  • animation-play-state:属性定义一个动画是否运行或者暂停。可以通过查询它来确定动画是否正在运行。另外,它的值可以被设置为暂停和恢复的动画的重放。
animation:none duration timig-function delay iteration-count direction

px em rem vh vw的区别?

  • rem是相对于根元素html的font-size计算大小的。
  • em是相对于父元素的font-size计算大小的。
  • vw:表示相对于视图窗口的宽度
  • vh:表示相对于视图窗口的高度
  • px:像素单位,显示像素的基本单位

原生 JS

ES 6 语法知道哪些,分别怎么用?

var let const 区别

  1. var 存在变量提升,可以声明写在下面,使用写在上面。此时JS 只有函数作用域和全局作用域
  2. let 使JS 存在块级作用域,必须先声明在使用,(存在暂时性死区,即在声明之前使用是不可以的。)let 更适合和for结合使用,每次都会生成一个新的块级作用域。
  3. let 不允许重复声明,但允许重复赋值,let a = 1,a=2可以,let a = 1,let a = 2不可以.
  4. const 只有一次声明和赋值的机会,不允许重复声明及赋值.

箭头函数

  1. 箭头函数只能做赋值,不能做声明
let a = (x,y) => {return x + y}
  1. 函数只有在调用的时候,才能确定this的值,箭头函数可以保证,函数内的this与函数外的this值相等.
  2. 箭头函数没有this的概念,箭头函数的第一个参数就是一个参数,没有隐藏的this
let a = () => {console.log(this)}
a.call({name:"ll"})
a() 
//以上结果相等  this为window
  1. 箭头函数的应用,
  • 简化回调函数
let arr = [1,2,3]
arr.map(number => number * number) //[1,4,9]
  • this 指向固定化,因为箭头函数没有this,导致函数内部this就是外部代码块的this
  1. Vue中不要直接使用箭头函数,Vue依赖this,可以当this捕获之后,在使用箭头函数.
  2. 箭头函数返回一个对象,必须用(),因为{}被理解为块级作用域
let x = () => ({name: 'qq'})
  1. 箭头函数还可以和变量解构结合使用
let x = ({x,y}) => {return x + y}
  1. 箭头函数的注意事项
  • 箭头函数的this是在定义函数时确定的,而不是使用。
  • 箭头函数不能用作构造函数,即不能使用关键字new
  • 箭头函数不可以使用arguments对象,该对象在函数体内不存在,可以使用rest代替
  • 不可以使用yield命令,因为箭头函数不能用在Generator函数
  • 箭头函数没有this,使用call,bind,apply改变this的指向没用。
  • 不适用场景
  1. 定义对象的方法,且该方法内包含对象
let obj = {
  name: 'lili',
  fn: () => {this.name} //this指向window
}
此种错误,箭头函数的this是指创建函数时所在的作用域,但是对象不能创建作用(只有函数才可),所以此时This为window
//改建
let obj = {
  name: 'lili',
  fn: function(){
    let f = () => {this.name}
  }
}
  • 需要动态this的时候,不能使用箭头函数
button.onClick = function(){
  this.classList.add('active')
}
//此时就不能用箭头函数

展开操作符(...)

MDN: 展开语法(Spread syntax), 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。(译者注: 字面量一般指 [1, 2, 3] 或者 {name: "mdn"} 这种简洁的构造方式)
展开运算符不仅适用于数组,还适用于对象,只能用于可迭代对象

  1. 伪数组变真数组的方法
//ES6
let args = [...arguments]   OR
let args = Array.from(arguments)
//ES5
let args = Array.prototype.slice.call(arguments)
  1. 获取array1的剩余部分
let array1 = [1,2,3,4,5,6,7]
let [a,b,...array2] = array1
 // array2 = [3,4,5,6,7]
//语法糖
[,,...array2] = array1
  1. 复制一个数组
let array1 = [1,2,3]
let [...array2] = array1 //let 很重要
// array2 = [1,2,3]
  1. 在array1前面加1,在后面加7
let array1 = [3,4,5]
let array2 = [1,...array1,7]
// array2 = [1,3,4,5,7]
  1. 函数调用
function sum(x,y,z){
  return x + y + z
}
const numbers = [1,2,3]
sum(...numbers) //6   等价于apply  sum.apply(null,numbers)
  1. 使用 new 关键字来调用构造函数时,
var dateFields = [1970, 0, 1]; // 1970年1月1日
var d = new Date(...dateFields);
  1. 连接多个数组,代替concat()
let arr1 = [1,2,3]
let arr2 = [4,5,6]
let arr3 = arr1.concat(arr2)
console.log(arr3)  //[1,2,3,4,5,6]

//展开运算符
let arr3 = [...arr1,...arr2]
console.log(arr3)  //[1,2,3,4,5,6]
  1. 字面量对象展开运算符,浅拷贝
let obj1 = {foo: 'zzz',num: 23}
let obj2 = {foo: 'xxx',age: 45}
let obj3 =  {...obj1}
console.log(obj3) //{foo: 'zzz',num: 23}

默认参数

默认参数允许在没有值或undefined传入的时候,使用默认形参

  1. 设置了默认参数,即使函数调用时显示把参数设置为undefined,值还是默认值,但是设置null,参数值就是null
function sum(a = 1){
  console.log(a)
}
sum(undefined)   //1
sum(null) //null
sum('') //''
  1. 在函数被调用时,默认参数值就会被解析,每次都会创建一个新的参数对象。
function array (value, arr = []){
  arr.push(value)
  console.log(arr)
}
array(1)   // [1]
array(2)  // [2]   不是[1,2]
这是两个完全不同的数组。

解构赋值

解构赋值是JavaScript的一种表达式,通过解构赋值可以把属性/值从对象 或数组中取出来,赋值给其他变量.

  1. 赋值,剩余数组必须是最后一个元素
[a,b,...rest] = [1,2,3,4,5,6,7]
console.log(a)  //1
console.log(b)  //2
console.log(rest)  //[3,4,5,6,7]
  1. 对象
//取对象的属性值
let a,b;
({a,b} = {a: 'li',b:20})
console.log(a)  //li 
console.log(b)  //20
//  其他情况,一个变量可以独立于声明进行结构赋值
let a,b;
({a,b} = {name: 'li',b:20})
console.log(a)   //undefined 
console.log(b)   //20
// 其他情况,浅拷贝,代替object.assign()
let rest;
({...rest} = {name: 'li',b:20})
console.log(rest) // {name: 'li',b:20}
等价于 let rest = Object.assign({},{name: 'li',b:20})
  1. 为了防止从数组中取出undefined对象,可以为变量设置默认值
let [a=2,b=3] = [1]
console.log(a) //1
console.log(b) //3
  1. 交换两个变量的值
let a = 1,b = 2;
//左边为需要赋值的变量,
[a,b]= [b,a]
console.log(a)  //2
console.log(b)  //1
  1. 可以给新的变量名赋值
let o = {q: 42,p:23 }
let {q: foo,p: bar} = o
console.log(foo)  //42
console.log(bar)  //23
  1. 解构对象时会查找原型链(如果属性不在对象自身,将从原型链中查找)

impoer export

export 用于从模块中导出函数,对象,原始值等,以便其他程序通过import导入相应的模块进行使用.

  1. 有名字的导入导出
eaport {name.age.city} 
import {name,age,city} from "1/js"
使用时,使用什么变量import什么变量即可
  1. 默认导出
export default Name
import Name from "1.js"
  1. 导出模块变量名相同,需要重新设置变量名
import {name as name1} from "1.js"
//name 重命名为name1
  1. 导入模块的所有
import * as name from '1.js';
  1. 关键字import可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise。
import("1.js").then(() => {},() =>{})
  1. 对于不支持import 和export 的浏览器,建议使用babel.js

JS数据类型,ES6新增的数据类型

  • JS数据类型
null ,number,String,undefined,object,boolean,symbol, 
  • ES6新增的数据类型
Symbol
  1. Symbol()不支持new()语法,因为他不具有constructor()函数.
new Symbol() //报错
  1. 每个从Symbol()返回的symbol值都是唯一的
var a = Symbol()
var b = Symbol()
console.log(a === b)  //false
  1. 一个Symbol值可以作为对象属性的标识符,这是该数据类型仅有的目的。。ES6之前,对象的属性标识符都是字符串,现在也可以使Symbol
var a = Symbol()
var obj = {}
obj[0] = '000'   //[0]0 其实是字符串
obj[a] = 'sss'

  1. symbol是一种基本数据结构,不是对象
var a = Symbol()
typeof a //symbol
  1. symbol可以实现有一个真的私有属性
{
  let a = Symbol()
  let obj = {
    name: 'li',
    age: 18,
    [a]:'隐藏属性'
  }
  window.object = obj
}
console.log(object)
console.log(object['Symbol()'])  //undefined

如何实现数组去重?

  1. 利用hash来实现
  • 代码
let array = [1,2,3,4,5,3,4,2,5,1,6,8]
function unip(array){
  let result = []
  let hash = {}
  for(let i = 0; i < array.length; i++){
    hash[array[i]] = true

  }
  console.log(hash)
//遍历对象使用for...in
  for(let key in hash){
    result.push(key)
  }
  console.log(result)
}
unip(array)
  • 缺点
    返回的数组元素时字符串,不是数字,以为ES6之前,对象的属性名只能是字符串
    无法区分原数组中的字符串数字和数字,比如4和'4',会被认为是一个
    无法统计对象,比如[1,2,{name:'li'}]返回的是[1,2,'object']
  1. 使用ES6 的set方法实现数组去重
Array.from()方法从一个类似数组或可迭代对象创建一个新的浅拷贝的数组实例
Set()方法允许存储任意类型的唯一值,无论是原始值还是对象引用值
let array = [1,2,3,4,5,3,4,2,5,1,6,8]
function unip(array){
  return Array.from(new Set(array))
//语法糖 return [...new Set(array)]
}
unip(array)
  1. 利用filter()过滤和indexOf的索引
 //只将数组中元素第一次出现的返回
 // 之后出现的将被过滤掉
function unip(array){
  return array.filter((item,index) => {
    console.log(item + '----' + index)
    
    return array.indexOf(item) ===index 
  })
}
let array = [1,3,5,2,4,6,4,2,4,3,1]
unip(array)

  1. 利用Map()对象
function unip(array) {
  const newArray = [];
  const tmp = new Map();
  for(let i = 0; i < array.length; i++){
        if(!tmp.get(array[i])){
            tmp.set(array[i], 1);
            newArray.push(array[i]);
        }
    }
    return newArray;
}

什么是 JSONP,什么是 CORS,什么是跨域?

  1. 同源策略
    浏览器出于安全机制的考虑,只允许同源相互访问数据,不允许跨域请求。同源指的是:同协议,同域名,同端口。window.origin / location.origin可以获取到当前的源。
  2. 为什么跨域可以使用CSS,JS,图片等
    同源策略限制的是数据的访问,引用CSS,JS,图片的时候,不知道内容是什么,只是单纯的在引用,使用ajax的时候,需要获取数据,所以背会浏览器限制。
  3. CORS 实现跨域
    CORS:跨域资源共享,主要用过设置响应头,决定浏览器是否允许跨域请求数据。
    如果frank.com想访问11.com里面的数据,就是跨域,正常浏览器出于安全机制,是不允许访问的,通过在11.com的服务器端设置reponse setHeader('Access-Control-Allow-Origin','http://frank.com')即可实现在frank.com访问11.com数据.
    若要允许多个网站访问reponse.setHeaders['refers']
    Access-Control-Allow-Origin:允许的域名
  4. JSONP实现跨域
  • JSONP是通过script标签加载数据的方式获取数据当做JS代码来执行,提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名在原始数据上(包裹)这个函数名,发送给前端。换句话说,JSONP需要对应接口的后端的匹配才能实现。
  • 虽然数据不能访问,但是JS 还是可以引用的,所以可以把数据放在JS中,通过引用JS,即可实现跨域。因为.js文件可以被所有网站引用,可以对其进行一个referer检查,这就是JSONP的定向分享。
  • frank.com想访问11.com中的数据,在11.com下创建一个index.js文件,内容widnow.xxx = {{data}}
    设置后台
 if(path === '/index.js'){
      if(request.headers['referer'].indexOf('http://frank.com') === 0 ){
        reponse.statusCode = 200;
        reponse.setHeader('Content-type',"text/javascript",'charset = utf-8')
        const string = fs.readFileSync('/index.js').toString()
        const data = fs.readFileSync('./index.json').toString()
        const string1 = string.replace({data},data)
        reponse.write(string1)
        reponse.send()
    }else {
    response.statusCode = 404
    reponse.send()
    }
}

在frank.com中只要引用index.js即可<script src = "http://11.com/index.js"></script>
拿到数据之后,要移除script,防止页面一直在请求数据,script只有onloa事件

script.onload = function(){
  script.remove()
}
  • JSONP封装
function jsonp(url) {
  return new Promise((resolve, reject) => {
    let random = Math.random();
    window[random] = (data) => {
      resolve(data);
    };
    let script = document.createElement("script");
    script.src = `${url}?callback=${random}`;
    script.onload = () => {
      script.remove();
    };
    script.onerror = () => {
      reject();
    };
    document.body.appendChild(script);
  });
}
// 使用
 jsonp("http://localhost:9991/friends.js").then((data) => {
  console.log(data);
});

对11.com服务器后台的改变

 if (path === "/index.js") {
    if (request.headers["referer"].indexOf("http://frank.com") === 0) {
      response.statusCode = 200;
      //query查询字符串
      console.log(query.callback);
      response.setHeader("Content-Type", "text/javascript;charset=utf-8");
      const string = fs.readFileSync("./index.js").toString();
      const data = fs.readFileSync("./index.json").toString();
      const string2 = string
        .replace("{{data}}", data)
        .replace("{{xxx}}", query.callback);
      response.write(string2);
      response.end();
    } else {
      response.statusCode = 404;
      response.send();
    }
  1. 一定要进行后台设置,才能拿到数据(主动沟通才有可能实现跨域)
  2. 面试题JSONP
    在跨域请求的时候,由于当前浏览器的某些原因不支持CORS,所以需要采用JSONP
    JSONP原理:请求JS文件,JS文件汇之星一个回调函数,回到里面就有需要的数据。回调函数的函数名是随机生成,把这个函数名以callback的形式传递给后台,后台会执行callback并在次返回给我们。
    JSONP优点:1,IE支持 2,实现跨域
    JSONP缺点: 由于它是script标签,读取不到ajax那么精确的状态,也不知道状态码是什么,也不知道响应头是什么,他只知道成功和失败。并且他只能发送get请求.
    CORS是在服务器端加上响应头,采用的是ajax请求。CORS支持所有类型的HTTP请求。

手写ajax

  • 浏览器提供的XMLHttpRequest对象,这个对象使得浏览器可以发出HTTP请求与接收HTTP响应,实现在页面不刷新的情况下和服务端进行数据交互。使用XMLHttpRequest对象来发送一个Ajax请求。
let xml = new XMLHttpRequest()
xml.open('get','/xxx')
xml.onreadystatechange = function(){
   if (xml.readyState === 4){
       if((xml.response.status >= 200 && xml.response.status < 300) || (xml.response.status === 304)){
           let data = xml.reponseText
       }else {
           console.log('失败')
       }
   }
}
xml.send()

//post请求只需要在 xml.send()中写入传递参数即可

xml.send('userbane=lili&passwoed=123')
  • readyState:存有XMLHttpRequest的状态。从0~4发生变化
0:请求未初始化,尚未调用open()方法
1:服务器连接已建立,启动,已经调用open()方法,但尚未调用send()方法。
2:发送,已经调用send()方法,但尚未接收到响应。
3:请求处理中,已经接收到部分响应数据。
4:请求处理完成且响应就绪。此时,触发load事件。

new一个函数发生了什么?

new命令的作用:执行构造函数,返回一个实例对象,this指对象实例。未使用new命令,构造函数就是普通函数,this指的就是window对象。

var fn = new Fn() 
//new Fn()生成的实例对象保存在变量fn中
  1. 创建一个空对象,作为将要返回的对象实例var a = {}
  2. 将这个空对象的原型指向构造函数的prototype属性a.__proto__ = FN.prototype
  3. 将这个空对象赋值给构造函数内部的thisthis = a
  4. 执行构造函数内部代码
    如果构造函数内部有return语句,并且return一个对象,那么new构造函数返回的对象就是return的对象,否则不管return语句。对普通函数,内部没有this的函数,new会返回一个空对象。
    总结:new一个函数,总是会返回一个对象,要么是实例对象,要么是return语句的对象.

闭包,立即执行函数,异步

  • 闭包
  1. 闭包:函数及内部函数的总和就构成了闭包
  2. 作用:隐藏变量。
function foo(){
  var local = 1
  function bar(){
    local ++ 
  }
  return bar()
}
var f = foo()
f() //访问到了闭包
  • 立即执行函数
  1. 立即执行函数:创建一个匿名函数,立即执行这个匿名函数
  2. 作用: 创建一个独立作用域,防止产生全局fn
(function(){alert('声明一个立即执行函数')})()
  • 异步函数
  1. 异步:一种并行处理机制,不等待结果,例如:数据加载过程
  2. 同步:等待结果,比如登录
  3. 异步的形式: 轮询和回调
  4. 判断异步的方式
如果函数的返回值出于以下三个内,就是异步函数
1. ajax (XMLHttpRequest)
2. setTimeout()
3. addEventListener()
  1. 关于异步和回调
1. 异步任务不能拿到结果
2. 于是传递一个回调给异步任务
3. 异步任务完成后调用这个回调
4. 异步的结果为回调函数的参数
  1. 例子
function f(){
    setTimeout(() => {
        return 1
    },1000)
}
//f函数并没有return,默认return undefined
//此时这个异步函数拿不到结果,需要借助一个回调函数

function f(fn){
    setTimeout(() => {
        fn(1)
    })
}
function f1(x){console.log(x)}
f(f1)
  • setTimeout()关于this
    由setTimeout()调用的代码运行在与所在函数完全分离的执行环境上,这会导致,这些代码里的this在非严格模式下指向window(全局对象),严格模式下指向undefined。
    在严格模式下,setTimeout()的回调函数的this扔指向window ,
    确定this的值,使用箭头函数或bind
  • 回调函数
  1. 回调,定义一个函数,在另外一个函数中调这个函数,这个函数就是回调函数
function f1(fn){
    fn()
}
f1(f2)
function f2(){

}
//f2即回调函数
  • .then(这里是回调)

  • 高级函数
    以一个函数或多个函数为输入,返回一个函数的函数就是高阶函数

  • 柯里化函数
    把多参数函数变成接受一个参数的函数,并且返回接受其他参数的函数的函数。

bind(),call(),apply()的区别

作用:改变函数执行时的执行上下文,具体一点就是改变函数运行时的this指向.

  • bind():返回改变上下文后的一个函数
  • call()和apply()改变执行上下文后边执行这个函数
  • call()和apply()的第一个参数都是this,call的第二个参数是以参数列表的形式展示的,apply是把数组作为第二个参数的。

这段代码里的 this 是什么?

  • this 的确定看函数的调用
1. fn()
this => window/global
2. obj.fn()
this => obj
3. fn.call(xx)
this => xx
4. fn.apply(xx)
this => xx
5. fn.bind(xx)
this => xx
6. new Fn()
this => 新的对象
7. fn = ()=> {}
this => 外面的 this
  • this是call的第一个参数
var obj = {
    foo: function(){
        console.log(this)
    }
}
var bar = obj.foo
obj.foo()  //obj  === obj.foo.call(obj)
bar()  //window === bar.call()

不用 class 如何实现继承?用 class 又如何实现?

  • class关键字是语法糖,JS是基于原型的。
//不用class实现继承
function Person(age){
    this.age = age
}
Person.prototype.move = function(){console.log('人类会移动')}

function Man(name){
    Person.call(this,age) //指定this为Man()。也可以使用Person.apply(this,age)
    this.name = name
}
//以下三行代码实现Man.prototype.__proto__ = Person.prototype
function temp(){}
temp.prototype = Person.prototype
Man.prototype = new temp()

Man.prototype.run = function(){console.log('男人会跑')}
var man = new Man(18,'li')

//使用class实现继承
class Person{
    constructor(age){
        this.age = age
    }
    move(){}
}
class Man extends Person{
    constructor(age,name){
        super(age)
        this.name = name
    }
    run(){}
}

原生JS有哪些获取元素的方法

1. 通过ID获取:document.getElementById('id')
2. 通过标签获取:document.getElementsByTageName()
3. 通过class获取:document.getElementsByClassName('class')
4. 通过name获取: document.getElementsByName('name')
5. 获取HTML:document.document.element()
6. 获取body:document.body 
7. 选择器选择一个元素:querySelector()
8. 选择器选择一组元素:querySelectorAll()

数组拷贝

1.扩展运算符浅拷贝数组

let a = [1,2,3,4,5,6]
let array = [...a]
console.log(array) //[1,2,3,4,5,6]
  1. for循环浅拷贝数组
let a = [1,2,3,4,5,6]
let array = []
for(let i = 0; i < a.length; i++){
    array.push(a[i])
}
console.log(array) //[1,2,3,4,5,6]
  1. Array.map()浅拷贝数组,map()接受一个回调函数,作为对数组的处理
let a = [1,2,3,4,5,6]
let array = a.map(x => x)
console.log(array) //[1,2,3,4,5,6]
  1. Array.filter()浅拷贝数组,filter()也接受一个回调函数
let a = [1,2,3,4,5,6]
let array = a.filter(x => x)
console.log(array) //[1,2,3,4,5,6]
  1. Array.slice()浅拷贝,指定start,end对原数组进行拷贝,包括start,不包括end
let a = [1,2,3,4,5,6]
let array = a.slice(2,5)
console.log(array) //[3,4,5] 
  1. Array.concat()浅拷贝,将原数组和其他数组结合
let a = [1,2,3,4,5,6]
let array = a.concat(2,5)
console.log(array) //[1, 2, 3, 4, 5, 6, 2, 5]
  1. Array.from()浅拷贝,可以将任何一个可迭代对象转化为数组
let a = 'foo'
let array = Array.from(a)
console.log(array) //["f", "o", "o"]
//实例2
let a = [1,2,3,4,5,6]
let array = Array.from(a)
console.log(array) //[1,2,3,4,5,6]

数组翻转,使用reverse()即可实现数组的翻转

let a = [1,2,3,4,56]
let array = a.reverse()
console.log(array) // [56, 4, 3, 2, 1]

JS的原型是什么?

  • 原型链是实现JS对象之间共有属性的继承

typeof的结果有哪些?typeof null?为什么?

  1. typeof的结果
1. string  2.number  3. boolean  4. symbol  5. undefined  6. object  7. function
  1. typeof null
typeof null: object
原因:在JS最初的版本中,使用的是32位系统,为了性能考虑,使用地位存储性能变量的信息,对象是000开头,null全是0,所以误将null认为是对象了。
  1. typeof object
typeof 判断对象,除了function 显示function 之外,其他都显示对象,所以typeof不能很
好的判断对象。

instanceof的作用是什么?实现原理的思路是什么?

  • instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
  • 可以通过instanceof判断对象的类型,它的内部机制是通过原型链来判断的。
  • 判断对象
let Person = function(){}
let p1 = new Person()
console.log(p1 instanceof Person)  //true
  • 判断原始类型
let a = 'acb'
console.log(a instanceof String)  //false
- 正确的判断方法
let a = 'acb'
let b = new String(a)
console.log(b instanceof String)  //true

如何用正则实现 trim()?

String.prototype.trim = function(){
    return this.replace(/^\s+|\s+$/g, '')
}
//或者 
function trim(string){
    return string.replace(/^\s+|\s+$/g, '')
}

手写函数防抖和函数节流

防抖和节流都是为了防止函数的高频复用

  • debounce(防抖函数):连续wait毫秒内多次调用,只触发最后一个
let timerId;
function debounce(fn,wait = 0){
  if(timerId){
    clearTimeout(timerId)
    timerId = null
  }else {
    timerId = setTimeout(() => {
      fn()
      timerId = null
    },wait)
  }
}
  • throttle(节流函数):连续多次调用,在指定时间内按照指定的时间间隔来执行.
function throttle(fn,wait = 200){
  let last = 1;
  let timerId;
  const now = + new Date()
  return function(...rest){
    //在时间间隔之内,不触发
    if(last && now-last < wait){
      clearTimeout(timerId)
      setTimeout(() => { 
        last = now
        fn.apply(this,rest);
      },wait)
    }else {
      fn.apply(this,rest)
      clearTimeout(timerId)
    }
  }
}

null和undefined的区别是什么?

Object.create()原理?

ES6引入的新方法,用来创建一个对象,这个对象的原型就是调用create方法时传入的参数

var a = Object.create(b)  
//a 的原型就是b

hasOwnPrototype()

是JS中唯一处理属性且不会遍历整个原型链的方法

必考 Promise、Promise.all、Promise.race 分别怎么用?

手写一个 Promise

移动端适配方案

常见的移动端兼容问题

用过token么?他有啥通,你把token放在那边了?

DOM相关

事件委托

用mouse事件写一个可拖曳的div

HTTP相关

一个URL从输入到页面加载共发生了什么?

无论是地址栏输入还是代码里加载,都会有一个URL,通过这个URL可以发送HTTP/HTTPS请求。请求过程,先查看缓存:浏览器缓存-系统缓存-路由缓存,如果缓存有,直接在缓存中获取,没有的话,对这个URL进行解析,提取信息,在进行DNS解析,得到对应域名的IP地址,通过三次握手,建立TCP连接,通过GET/POST等方式发送HTTP/HTTPS请求,服务器处理请求并响应请求,返回资源,浏览器拿到资源进行解析渲染,四次挥手,断开连接。

三次握手

  1. 客服端发送SYN报文 SYN = 1, seq = x 进入SYN_SEND阶段
  2. 服务器端接受SYN报文,返回一个ACK响应ACK = 1,SYN = 1,ack = x+1,seq = y进入SYN_RECV阶段
  3. 客户端接受服务器的SYN报文,返回一个ACK响应axk = y+1,seq = x+1.ACK=1进入Etablished阶段

四次挥手

  1. 客户端发送FIN=1,请求关闭连接SYN=1,seq = x进入FIN-WAIT-1阶段
  2. 服务器端返回ACK,ACK =1,seq = y,sck = x+1,进入CLOSE_WAIT阶段,客服端进入FIN-WAIT-2阶段
  3. 服务器通知应用程序关系连接,之后返回给客户端ACK=1,seq = z,ack = x+1,FIN=1进入LAST_ACK阶段
  4. 客户端返回ACK = 1,FIN = 1,seq = x+1,ack = z+1进入TIME_WAIT,服务端进入CLOSED阶段

为什么是三次握手,四次挥手呢?

因为服务器在接收到客户端关闭连接的请求时,是先返回一个应答ACK,表示收到这个请求了,之后通知应用程序去关闭连接,当应用程序关闭连接后,服务器才会通知客户端,已关闭,所以这个地方是分两步走的,所以比握手建立连接多了一步。

HTTP的状态码及意思

  1. 状态码:
    • 1XX:Informational(信息性状态码),接收的请求正在处理。
    • 2XX:Success(成功状态码),请求正常处理完毕。
    • 3XX:Redirection(重定向状态码),需要进行附加操作以完成请求。
    • 4XX:Client Erroe(客户端错误状态码),服务器无法处理请求。
    • 5XX:Server Error(服务器错误状态码),服务器处理请求出错。
  2. 200状态码:(成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
  3. 301状态码:(永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
  4. 302状态码:(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
  5. 304状态码:(未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
  6. 403:状态码:(禁止) 服务器拒绝请求。
  7. 404状态码:(未找到) 服务器找不到请求的网页。
  8. 500状态码:(服务器内部错误) 服务器遇到错误,无法完成请求。
  9. 503状态码:(服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
    HTTP常见状态码

GET和POST的区别

GET和POST本质上就是TCP链接,并无差别,但是GET产生一个TCP数据包;POST产生两个TCP数据包。

  1. 作用不同:GET获取资源,像后台要数据;POST传输实体主体,像后台传输数据。
  2. 请求参数的展现发送不同:GET请求会用?将请求参数拼接成url;POST请求的请求参数在请求主体内。
  3. 传输数据长度不同:因为浏览器地址栏对自动对url进行截取,所以当数据量过大时,GET请求会造成数据丢失。
  4. 安全性:因为GET请求会拼接成url展示,但是浏览器的历史记录和服务器的请求日志,都会保存其信息,造成信息的不安全。

请求的动词

  • GET:获取资源。GET方法用来请求访问已被URI识别的资源,指定的资源经服务器端解析后返回响应内容。HTTP1.0~1.1
  • POST:传输实体主体。HTTP1.0~1.1
  • PUT:传输文件。就像FTP协议的文件上传一样,要求在请求报文的主体中包含文件的内容,然后保存到请求URI指定的位置。HTTP1.0~1.1
  • HEAD:获取报文首部。与GET方法类似,用于确认URI的有效性及资源更新的日期时间等等。HTTP1.0~1.1
  • DELETE:删除文件。是与PUT方法相反的。HTTP1.0~1.1
  • OPTIONS:询问支持的方法。用来查询针对请求URI指定的资源支持的方法。HTTP1.1
  • TRACE:追踪路径。是让Web服务器端将之前的请求通信返回给客户端。HTTP1.1
  • CINNECT:要求用隧道协议连接代理。实现用隧道协议进行TCP通信。HTTP1.1
  • LINK:建立和资源之间的连接。
  • UNLINK:断开连接关系。

OSI的七层模型是什么?TCP/IP是哪四层模型?

  • OSI七层模型
    七层模型,也叫OSI(OPen System Interconnection)参考模型,是国际标准化组织(ISO)指定的一个用于计算机或通信系统间互联的标准体系。其中包括:
    1. 应用层
    2. 表示层
    3. 会话层
    4. 传输层
    5. 网络层
    6. 数据链路层
    7. 物理层
  • TCP/IP四层模型
    其中主要包括:
  1. 应用层:
    应用层决定了向用户提供应用服务时通信的活动。下述的三层负责处理网络通讯的相关细节,这部分需要稳定高效,因此它们是在操作系统的内核空间中,而应用层是在用户空间实现的,负责处理众多业务逻辑,如文件传输、网络管理。
    应用层的协议众多:
    • 运行在TCP协议上的协议
      1.1 HTTP(80端口):主要用于普通浏览。
      1.2 HTTPS(443端口):HTTP协议的安全版本。
      1.3 FTP(20和21端口):用于传输文件。
      1.4 POP3(110端口):收邮件用。
      1.5 SMTP(25端口):用来发送电子邮件。
      1.6 SSH(22端口):用于加密安全登录用。
    • 运行在UDP协议上的协议
      2.1 DCHP(67端口,动态主机配置协议),动态配置IP地址。
    • 其他
      3.1 DNS(域名服务):用于完成地址查找,邮件转发等工作(运行在TCP/IP协议上)。
      3.2 SNMP(简单网络管理协议):用于网络信息的收集和网络管理。
      3.3 ARP(地址解析协议):用于动态解析以太网硬件的地址。
  2. 传输层:
    传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输,即为应用程序隐藏了数据包跳转的细节,负责数据包的收发、链路超时重连等。
    传输层的协议:
    • TCP协议:传输控制协议,是一种面向连接的,可靠的,基于字节流的传输层通信协议。
    • UDP协议:用户数据报协议,不可靠的传输层协议。
  3. 网络层:
    网络层用来处理在网络上流动的数据包。
    网络层协议:
    • IP协议: 是网络层最核心的协议,它根据数据包的目的IP地址来决定如何投递该数据包。若数据包不可直接发送给目标主机,那么IP协议就为它寻找一个合适的下一跳路由器,并将数据包交付给该路由器去转发,如此循环直至到达目标主机或者发送失败而丢弃该数据包。
    • ICMP协议: 因特网控制报文协议ICMP,是IP协议的补充,用于检测网络的连接状态,如ping应用程序就是ICMP协议的使用。
  4. 链路层:
    用来处理连接网络的硬件部分。包括控制操作系统、硬件设备驱动、NIC(网络设备器即网卡),及光纤等物理课件部分(还包括连接器等一切传输媒介)。

说一下request包括哪些header

  • HTTP请求方式
  • HOST 请求的web服务器的域名地址
  • User-Agent 客户端运行的浏览器的详细信息
  • Accept 客户端能够接受的内容类型
  • Accept-Language 客户端展示返回信息所选择的语言
  • Accept-Encoding 客户端能够结束返回信息的编码方式
  • Accept-Charset 客户端能够接受的字符编码集
  • Content-type 客户端显示提交内容的类型
  • Connection 是否需要持久连接 keep-alive/HTTP1.1持久连接
  • keep-alive: 持久连接的时间
  • cookie:请求发送时,会把保存在该域名下的cookie一起发送给服务器
  • referer: 包含URL

说一下reponse包括哪些header

第一行状态行: 协议版本号 状态码 状态信息
Content-Length 服务器返回消息的正文的长度
Content-type 返回的字符类型和字符编码格式
Date 显示当前的时间

HTTP缓存有哪几种?

  • 缓存相关的首部字段


    缓存相关的首部字段.PNG
  • 缓存对比图


    缓存对比图.PNG
  • Expires是用时刻来标记时间的,这个时间是相对于服务器时间而言,但是用户可以更改自己电脑的系统时间,导致浏览器的时间和服务器的时间不一致,此时设置过期时间也没有什么用了。Expires是HTTP1.0提出的,兼容性比较好
  • Cache-Control:解决了Expires的问题,使用时间间隔来标记过期时间的,和本地时间无关,但是是HTTP1.1提出的,兼容性没那么好,所以很多时候Cache-ControlExpires连起来用。Cache-Control的优先级高于Expires
  • Last-Modified服务器将资源返回给浏览器,也会将资源最后的修改时间一同返回给浏览器,下一次请求的时候,带着这个If-Modified-Since字段,包含Last-Modified信息,先进行最后修改时间比较,未发生改变,返回304,但是他无法处理文件一秒内多次修改的情形,而且只要文件修改了,哪怕实质内容未更改,也会发送请求。
  • ETag解决了Last-Modified的问题,服务器会通过算法计算出资源的唯一标识符,返回给浏览器,下一次进行请求时,带着If-None-Match,这个字段包含ETag,这个唯一的标识,先对这个标识符进行比较,为改变返回304.,通过算法计算表示符也浪费性能。ETag是通过对比浏览器和服务器资源的特征值(例如MD5)来决定是否要发送文件内容,如果一样就只发送304.
  • Etag / If-None-Match优先级高于Last-Modified / If-Modified-Since,同时存在则只有Etag / If-None-Match生效。
  • Expires和Cache-Control都有一个问题就是服务端作为的修改,如果还在缓存时效里,那么客户端是不会去请求服务端资源的(非刷新),这就存在一个资源版本不符的问题,而强制刷新一定会发起HTTP请求并返回资源内容,无论该内容在这段时间内是否修改过;而Last-Modified和Etag每次请求资源都会发起请求,哪怕是很久都不会有修改的资源,都至少有一次请求响应的消耗。
    对于所有可缓存资源,指定一个Expires或Cache-Control max-age以及一个Last-Modified或ETag至关重要。同时使用前者和后者可以很好的相互适应。
    前者不需要每次都发起一次请求来校验资源时效性,后者保证当资源未出现修改的时候不需要重新发送该资源。而在用户的不同刷新页面行为中,二者的结合也能很好的利用HTTP缓存控制特性,无论是在地址栏输入URI然后输入回车进行访问,还是点击刷新按钮,浏览器都能充分利用缓存内容,避免进行不必要的请求与数据传输。
  • 浏览器发送请求过程


    浏览器发送请求过程.PNG

HTTP1.0、HTTP1.1 和 HTTP2.0 的区别

  1. HTTP1.0(1996年)和HTTP1.1(1999年)的区别
  • 缓存处理
    HTTP1.0采用If-Modified-SinceExpires
    HTTP1.1采用更多的信息来保存缓存If-Unmodified-Since 、 If-Match,If-none-Mach
  • 带宽优化及网络连接的使用
    HTTP1.0会传输整个信息,即使你只需要一部分,并且不支持断点续传功能
    HTTP1.1在头部引入range信息,允许请求某个资源的一部分
  • 错误通知的管理
    HTTP1.1引入了24个状态码,比如410:服务器上的资源被永久删除,409请求的资源与当前资源冲突
  • HOST头部处理
    HTTP1.0 没有host头部,只有一个IP地址,但随着虚拟机的出现,在HTTP1.1中,使用HSOT信息,无此信息会报400错误,bad request
  • 长连接
    在HTTP1.1中支持长连接很请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和响应 ,减少了建立请求和关闭请求的时间。在HTTP1,1中,默认开启Connection:keep-alive,一定成都还是那个,弥补了HTTP1.0每次请求都要建立连接的缺点
  1. HTTP和HTTPS的区别
  • HTTP是运行在TCP协议之上的,HTTPS是运行在SSL/TLS之上的,是加密的,SSL/TLS是运行在TCP协议之上的。
  • HTTPS需要到CA申请证书
  • HTTP端口是80,HTTPS端口是443
  • HTTPS可以有效防止运营商劫持
  1. SPDY对HTTP1.x的优化
  • 降低延迟:多路复用,解决了浏览器阻塞问题
  • 设置了请求优先级,重要的请求先解决
  • header压缩,对header头部进行压缩
  • 基于HTTPS的加密协议传输,大大提高了数据的可靠性
  • 服务端推送
  1. HTTP2.0和SPDY的区别
    HTTP2.0是对SPDY的升级,但也有不同
  • HTTP2.0 支持明文HTTP传输,但是SPDY强制使用HTTPS
  • HTTP2.0头部压缩采用HPACK算法,SPDY采用DEFLATC算法
  1. HTTP2.0 和HTTP1.x的区别
  • 新的二进制格式
    HTTP2.0采用二进制解析,HTTP1.x采用文本解析
  • 多路复用,连接共享。即每一个request都是用作连接共享的机制,给request添加ID,便于区分
  • header压缩
    HTTP1.X有大量的header信息,HTTP2.0 对其进行了压缩
  • 服务端推送
    支持server push。服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。

HTTP2.0的多路复用和HTTP1.X中的长连接复用有什么区别?

  • HTTP/1.* 一次请求-响应,建立一个连接,用完关闭;每一个请求都要建立一个连接;
  • HTTP/1.1 Pipeling解决方式为,若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的线头阻塞;
  • HTTP/2多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其它连接的正常执行;

Cookie VS LocalStorage(本地存储) VS SessionStorage(会话存储) VS Session

  1. Cookie VS LocalStorage
  • 主要区别是Cookie会被发送到服务器,而LocalStorage不会
  • Cookie的存储位置:浏览器和服务器,LocalStorage是只读属性,并且值存储在浏览器。
  • Cookie一般最大是4k ,LocalStorage可以是5MB,甚至是10MB,各浏览器不同
  1. LocalStorage VS SessionStorage
  • LocalStorage一般不会自动过期,除非手动删除,而SessionStorage在会话结束时过期,如关闭浏览器
  1. Cookie VS Session
  • Cookie存在浏览器的文件里,Session存在服务器的文件里
  • Session是基于Cookie实现的,具体做法是把SessionID放到Cookie中。
  • SessionId的生成一般采用JWT算法。

框架VUE相关

watch和method和computed三者的区别

  • computed(计算属性),返回一个属性,依赖缓存,缓存的内容没有变化,就不会重新计算。
  • methods(方法),没有缓存,页面刷新,就重新执行该方法
  • watch(侦听属性),代码是命令式和具有复杂性,可能返回一个属性,还可能做别的事情(如上报数据)
  • 数据量大,需要缓存使用computed,每次确实需要重新加载,不用缓存,使用methods

Vue有哪些钩子函数,有什么用?

  • 生命周期: 初始化---运行中---销毁(开始创建--初始化数据--编辑模板--挂载DOM--渲染->更新->渲染--销毁)
  • 钩子函数
  1. beforeCreate:vue对象被创建了,但是Vue属性还没有绑定,template模板还没有HTML元素。
  2. created:vue属性被绑定了,但此时DOM还没有生成,$el还没有值
  3. beforeMount:this.$el有值,但是数据还没有挂载到页面上,{{}}内的变量还没有被替换
  4. mounted:模板编译完成,数据挂载完毕.此时可以发送异步ajax请求
  5. beforeUpdate:数据更新之前执行的函数,只有数据更新才会触发beforeupdate。此数据一定是在模板上出现的数据,否则,不会,也没有必要触发组件更新(因为数据不出现在模板里,就没有必要再次渲染)
  6. updated:组件更新之后执行的函数
  7. beforeDestory:组价对象销毁之前执行的函数
  8. destoryed:组件销毁了执行的函数.

Vue如何实现组件间的通信

  1. 父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed
//父组件
<template>
  <div id="app">
    <HelloWorld v-bind:users = "users"/>
  </div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
  name: 'App',
  data(){
    return {
      users: ['li','xiao','qi']
    }
  },
  components: {
    HelloWorld
  }
}
</script>
//子组件
<template>
  <div>
    <ul>
      <li v-for="user in users">{{user}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'HelloWorld',
  props: { //通过props接受从父组件传来的数据
      users: {
        type: Array  
      }
  }
}
</script>
  1. 父子组件:使用v-on通过事件通信
    子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。
//子组件
<template>
  <div>
    <h1 @click = "changeTitle">{{title}}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return {
      title: "this is a son"
    }
  },
  methods: {
    changeTitle(){
      this.$emit('changeTitle','子组件想父组件传值')
    }
  }
}
</script>
//父组件
<template>
  <div id="app">
    <HelloWorld v-on:changeTitle = "updatetitle"/>
    <h2>{{title}}</h2>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  data(){
    return {
      title: "一个值"
    }
  },
  methods: {
    updatetitle(e){
      this.title = e
    }
  },
  components: {
    HelloWorld
  }
}
</script>
  1. 爷孙组件:使用两次v-on通过爷爷爸爸通信,爸爸儿子通信实现爷孙通信
  2. 任意组件:使用eventBus = new Vue()来通信,eventBus.$oneventBus.$emit是主要的API
var eventBus = new Vue()
eventBus.$emit(事件名,数据)
eventBus.$on(事件名,data =>{})
  1. 任意组件: 使用vuex通信

Vuex的原理及如何使用Vuex

  • Vuex是vue的状态管理器。存储的数据是响应式的。
  • Vuex的组成结构图


    vuex的组成结构.PNG
  • 含义
  1. state:数据处理中心,用来存储数据
  2. getters:getters和组件的computed类似,方便生成一些直接可用的数据,当组装的数据要在多个页面使用的时候,使用getters比较好。
  3. Mutations:提交更改数据,使用store.commit()方法更改state的存储状态。
  4. Action:提交的是mutation,而不是直接更改状态,Action可以直接包含异步操作。由组件中的$store.dispatch('action名称',deta1)来触发,然后由commit()`来触发mutation的调用,间接更新State。
  • 安装及引用
npm install vuex --save / yarn add vuex
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
  • 在Vue组件中获取Vuex的状态(State)
  1. this.$store.state获取
    通过在根实例中注册store选项,改store实例会注入到根组件下的所有子组件,且子组件都能通过this.$store获取到
this.$store.state.count
  1. mapState()辅助函数获取
    当一个组件需要多个状态时,推荐使用mapState,他帮助我们生成计算属性。
import {mapState} from "vuex"
computed: mapState({
  count: state => state.count,
  //穿字符串'count'等同于传入'state => state.count'
  countAlias: 'count'
  //为了使用This,所以不能使用箭头函数
  countPlusLocalState(state){
    return state.count + this.countAlias
  }
})
  • Getter的获取方式
    Vuex允许我们在store中定义"getter",就像计算属性一样,getter的返回值会根据他的依赖被缓存起来,且只有当他的依赖发生变化时,才会重新计算
  1. 通过属性访问
store.getters.doneTodos
  1. 通过方法访问
getters: {
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

getter在通过方法访问时,每次都会去进行调用,而不会缓存。

  1. mapGetters辅助函数获取
import {mapGetters} from "vuex"
export default {
  ....
   computed:{
      //使用对象展开符将getters混入computed 对象中
      ...mapGetters([
        'doneTodoCount',
      ])
    }
  }
  • Mutation
    更改vuex的store中的状态的唯一办法是提交mutation,vuex中的mutation非常类似于事件,每个Mutation都有一个字符串的事件类型(type)和一个回调函数(handler),这个回调函数就是我们实际进行状态更改的地方,并且它接受state作为第一个参数.
  1. 使用常量替代Mutation事件类型
const store = new Vuex.Store = ({
  state: {...},
  mutations: {
    [SOME_MUTATION](state){
      //使用常量作为函数名
      //mutate state
    }
  }
  1. 可以在组件中使用this.$store.commit('xxx')提交mutation,或者使用mapMutations辅助函数将组件中的methods映射为store.commit调用
export default{
  ...mapMutaions([
    'increment',
    //将this.increment映射为this.$store.commit('increment')
  ])
}
  • Action
    Action函数接受一个与Store实例具有相同方法和属性的context对象,因此可以调用context.commit提交一个Mutation或者通过context.state和context.getters来获取state和getters。
const store = new Vuex.Store({
  state: {count: 0},
  mutations: {
    increment(state){state.count ++}
  },
  actions: {
    increment(context){
      context.commit('increment')
    }
  }
})
  1. 分发action
store.dispatch('increment',{
  acount:10
})
store.dispatch({
  type: 'increment',
  acount: 10
})
  1. 在组件中分发action
    在组件中使用this.$store.dispatch('xxx')分发action,或者使用mapActions辅助函数将组件中的Methods映射为store.dispatch调用
export default {
  methods: {
    ...mapMutations([
      'increment'
    ])
  }
}

vue数据响应式是怎么做到的

  • Vue.js的特点是非侵入性的响应式系统,数据模型是普通的JS对象,修改他们时,视图会进行更新渲染,但是由于JS的限制,Vue不能检测数组和对象的变化。
  • 一个普通的JS对象传入Vue实例作为data选项时,Vue将遍历此对象的所有property,并使用Object.defineProperty把对象的所有property转化为getter/setter
  • 对于对象,vue无法检测到property的添加和删除
  1. property必须在data对象上存在才是响应式的。
var vm = new Vue({
  data: {
    a:1
  }
})
 // vm.a 是响应式的
vm.b =1 
//vm.b不是响应式
  1. 对于已经创建的实例,Vue不允许动态添加根级别的响应式property,但是可以使用Vue.set(object,propertyName,value)或者this.$set(object,propertyName,value)方法嵌套对象添加响应式property。
Vue.set(vm.object,'b',2)
this.$set(this.object,'b',2)
  1. 为对象赋值多个属性时
this.object = Object.assign({},this.object,{a:1,b:2})
  • 对于数组
  1. vue不能检测到一下数组的变动
    1.1利用索引直接修改一个数组的选项时vm.items[indexOf] = newValue
    1.2当修改数组的长度时,vn.length = newLength
  2. 对于1.1的情况,使用set,并且还可以触发响应式更新
Vue.set(vm.items,indexOf,newValue)
this.$set(this.items,indexOf,newValue)
  1. 对于1.2的情况,使用splice
this.$items.splice(newLength)
  • 声明响应式property
    由于Vue不允许动态添加根级响应式property,所以必须在初始实例前声明所有根级响应式property,哪怕是一个空值
var vn = new Vue({
  data:{
    message:''
  }
})
  • 异步更新队列
    vue更新DOM是异步的,主要侦听到数据变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更,如果同一个watcher被多次触发,只会被推入到队列中一次,然后在下一次循环'tick'中,Vue将刷新队列并执行实际操作。
    采用技术Promise.then,MutationObserver,setImmdiate
    为了在数据变化之后等待Vue完成更新DOM,可以在数据变化之后立即使用Vue.nextTick,这样回调函数将在DOM更新完成后被调用
    使用Object.defineProperty把这些全部转为getter/setter
    Vue不能检测到对象属性的添加或删除,解决办法是手动调用Vue.set或者this.$set

Vue set有什么用

vue 是非侵入性响应式系统,所有的数据模型都是JS对象,一个JS对象传入Vue实例作为data选项时,VUE会遍历对象所有的property,使用Object.defineProperty将他们转化为getter/setter,,但是vue不支持动态添加根级别的响应式property,无法检测property的添加或者移除,所以采用set对属性进行添加和移除.

VueRouter怎么用

vue router是vue.js官方的路由管理器

  • 相关API
  1. router-link
    导航,根据to属性去匹配对应的组件,然后渲染到正确的位置,若使用a标签,页面会发生跳转,router-link默认会渲染成a标签
<router-link  :to="{path:'/home'}" tag="li" ctive-class="activeClass" event="mouseover" exact>
 <span>home</span>
</router-link>
tag: 设置生成指定的标签名
to:页面要跳转的文件位置,可以动态绑定
active-class:设置单独的,独有的激活状态的颜色
event:指定事件
exact:其他路径被点亮的时候去掉根路径的active
redirect:重定向
  1. router-view
    命名视图,在同级同时展现多个视图,而不是嵌套展示,要对router-view起一个名字
<router-view name  = "silder"></router-view>
  1. 路由信息对象的params属性
    路由传参params ,只有通过路由配置的name跳转,才会生效,即使没有使用动态路由,也可以通过params传参
    如果使用动态路由,那么在动态路由页面刷新路由,params参数依然存在,
    如果没有使用动态路由,params参数只在跳转路由时有效,刷新页面则会丢失params
this.$route.params 获取到自己配置的动态路由参数,路由ID

一个路由信息对象,表示当前激活的路由的状态信息,包含当前URL解析得到的信息,还有URL匹配得到的路由记录。

  1. 其他
this.$route.back() //回退一步
this.$route.foreard() //前进一步
this.$route.go() //指定回退前进的步数
this.$route.push() //导航到不同的URL,向history栈中添加一个新的记录
this.$route.replace() //导航到不同的URL,替换掉栈中当前的记录
this.s4route.meta() //访问Meta中的数据
  • 导航钩子函数
    导航发生变化时,导航钩子主要用来拦截导航,让他完成跳转或取消
  1. 钩子函数
router实例上:beforeEach,afterEach
单个路由中: beforeEnter
组件内部的钩子: beforeRouterEnter beforeRouteUpdate BeforeRouteLeave 
参数: to from next
  • history模式
  1. vue-router默认是Hash模式,当URL改变时,页面不会重新加载。history模式,充分利用history.pushState API来完成URL跳转页面无需重新加载页面。当路由变化时,JS会根据路由渲染对应的VUE组件。
  2. vue-router是为单页面服务的需要在服务端增加一个覆盖所有情况的候选资源,如果URL匹配不到任何静态资源,返回一个index.html页面,也是app依赖的页面。这个index.html是我们项目的入口,index.html里面会读取当时打包好的app.js,就可以读取到路由配置了。
    hash是不需要配置的,因为浏览器会忽略#和?后面的参数
const router = new VueRouter({
  mode:'history',
  routes: [...]
})
  1. 警告:
    这么做以后,服务器就不在返回404页面,因为对于所有的路径都会返回Index.html,为了避免这种情况,应该在vue应用里面覆盖所有的路由情况,然后给出一个404
const router = new VueRouter({
  mode:'history',
  routes: [{
      path: '*',component: NotFoundComponent
    }]
})
  • 路由懒加载
  1. 原因:打包构建应用时,JS包会变得非常大,影响页面的加载。
  2. 解决:不同路由对应的组件分割成不同的代码块,然后当路由被访问时,加载对应的组件。
  3. 实现路由的懒加载:
    Vue的异步组件和webpack的代码分隔功能。
  4. 步骤
    4.1可以将异步组件定义为返回一个Promise的工厂函数
const Foo = () => Promise.resolve({/*组件定义对象*/})

4.2在webpack2中,使用动态Import语法来定义代码分块点

import ('./Foo.vue')
  1. 定义一个能够被webpack自动代码分隔的异步组件
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
  routes: [
    {path:'/foo',component: Foo}
  ]
})

路由守卫是什么?

  • vue-router提供的导航守卫主要用来通过跳转或取消的方式来守卫导航。参数或查询的改变并不会触发离开/进入的导航守卫。可以通过观察$route对象来观察这些变化,或使用beforeRouteUpdate的组件内守卫。
  • 守卫是异步解析执行,此时导航在所有守卫resolve之前是出于等待中。
  • 全局前置守卫 beforeEach按照创建顺序调用
const router = new VueRouter({...})
router.beforeEach((to,from,next) => {})
  • 各参数含义
to: Route:即将要进入的目标对象
from:Route:当前导航正要离开的路由
next:Function:一定要调用这个方法来resolve这个钩子,执行效果依赖Next方法传递的参数.
如果钩子全部执行完,导航的状态是confirmed(确认的)
  • 确保next函数在任何给定的导航守卫中都要被严格调用一次,他的出现可以多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远不会被解析或报错
router.beforeEach((to,from,next) => {
  if(to.name !==Login && !isLogin)next({name: 'Login'})
  else next()
})
  • 全局前置解析beforeResolve
    在导航被确认之前同时在所有组件守卫和异步路由组件被解析之后,解析守卫就会调用(这也是和前置守卫的区别)
  • 全局后置钩子afterEach
    与守卫不同的是,钩子不会接受next函数也不会改变导航本身。
  • 路由独享守卫beforeEnter,直接定义在路由上
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
  • 组件内部守卫:路由组件内定义的守卫
  1. beforeRouteEnter:不能获取组件实例this,此时组件实例还未创建
  2. beforeRouteUpdate: 组件复用的时候调用,可以获取组件的实例this
  3. beforeRouteLeave:组件离开的时候调用,可以获取组件实例this
  4. beforeRouteEnter不能获取组件实例this,但是可以给next函数传递一个回调函数来访问组件的实例,在导航被确认的时候执行回调,并且吧实例作为回调的参数
beforeRouteEnter(to,from,next){
  next(vm => {})
}

beforeRouteEnter是唯一一个支持next传递回调函数的导航守卫,其他都不可以。

  1. beforeRouteLeave离开守卫通过进制用户在还未保存修改前突然离开,next(false)可以取消该导航.
  • 完整的导航解析流程
  1. 导航被触发
  2. 在失活的组件里调用beforeRouteLeave
  3. 调用全局的beforeEach守卫
  4. 在复用的组件里调用beforeRouteUpdate守卫
  5. 在路由配置里调用beforeEnter守卫
  6. 解析异步路由组件
  7. 在被激活的组件里调用beforeRouteEnter
  8. 调用全局的beforeResolve
  9. 导航被确定
  10. 调用全局的afterEach钩子
  11. 触发DOM更新
  12. 用创建好的实例调用beforeEnter守卫中传给next回调函数

Vue有哪些指令

  • 指令
  1. 解释: 带有v-前缀的特殊属性
  2. 作用: 当表达式的值发生概念时,将其产生的影响响应式的作用于DOM。
  • v-text:更新元素的textContent
  • v-html:更新元素的innerHTML
  • v-bind:当表达式的值改变时,将其产生的影响响应式的作用于DOM
  • v-on:绑定事件
事件修饰符
.stop 阻止冒泡,调用 event.stopPropagation()
.prevent 阻止默认事件,调用 event.preventDefault()
.capture 添加事件侦听器时使用事件捕获模式
.self 只当事件在该元素本身(比如不是子元素)触发时触发回调
.once 事件只触发一次
  • v-modal:在表单元素上创建双向绑定数据,作用:监听用户的输入事件以更新数据
  • v-for:遍历,使用V-for的时候,要使用key属性
  • v-if:根据表达式的值的真假条件,销毁或重建元素
  • v-show:根据表达式之真假值,切换元素的 display CSS 属性
  • v-cloak:防止网速慢的时候出现{{}}
  • v-once:只渲染元素和组件一次

你发送请求放在哪个生命周期,beforeDestory应用场景

  • beforeDestory实例被销毁之前调用,此时实例还是可以使用的。
  • 发送请求放在created中,因为此时已经拿到了data。

vue中的filters是干甚用的

  • Vue.js允许自定义过滤器,用于一些常见的文本格式化。
  • 过滤器可以使用在两个地方
{{}} --- 双花括号插值 {{message | capitalize}}
v-bind表达式 <div v-bind:id = "ravID | formatId"></div>
  • 可以在组件内定义一个本地过滤器 也可以在创建实例之前定义一个全局过滤器
filters: {
  'capitalize': function(){}
}
Vue.filter('capitalize',function(){})
  • 过滤器的作用:
  1. 过滤器可以通过管道符写多个过滤器 {{messa | filter1 | fliter2}}而写多个函数就是一个很麻烦的事情,可能会产生很多的对象代理,依赖收集等一大推降低性能的问题
  2. Array原型链上可以用v-for循环解决,没必要在methods方法上面添加太多

vue 为什么data必须是函数。

JS的实例是由构造函数创建的,一个构造函数可以New出很多个实例,Vue 的data其实是vue 原型上的属性,数据是保存在内存中的,为了保证数据之间是互相独立的,互不影响的,使用return即函数,而不是对象,因为对象是内存引用。

slot的作用

  • 插槽: 在2.6.0中为具名插槽和作用域插槽引入了统一语法v-slot.是对组件的扩展,通过slot插槽向组件内部指定位置传递内容,通过slot可以父子传参
  • 插槽<slot>作为承载分发内容的出口
  • 父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
  • 具名插槽,一个不带 name 的 <slot> 出口会带有隐含的名字“default”。
<slot name = "headerr"></slot>

在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称: v-slot 只能添加在 <template> 上

<template v-slot = "header'></template>
  • 作用域插槽
    作用域插槽可以显著提高组件的通用性和复用性。作用域插槽允许你传递一个模板,而不是已经渲染好的元素给插槽,之所以叫作用域插槽,是因为模板虽然在父级作用域中渲染,但却能访问子组件的数据。作用域插槽主要应用的是列表组件。
<ul>
  <slot name = "item" v-for = "item in items" :text = "item.text" :myname = "item.myname">
    slotmore内容
  </slot>
</ul>
<Child>
  <template slot = "item" v-slot = "props">
    <li>{{props.myname}}</li>
  </template>
</Child>

vue-router你说怎么传参的,有几种方式传参,他们的区别是什么?

  • 编程式的导航 router.push
  1. 直接在路由路径后面写参数${}
//页面列表传递参数
this.$router.push({
  path: '/custome/customeDetailPage/${id}'
})
//对路由的配置
{
  path: '/custome/customeDetailPage/:id'
}
//获取路由参数
this.$route.params.id
  1. 通过name属性来匹配路由,通过params传递参数
//页面列表参数
this.$router.push({
  name: 'CDetailPage', //name 属性是必须的
  params: {
    dataObj: id
  }
})
//路由配置
{
  path: '/custome/customeDetailPage',
  name: 'CDetailPage', //那么属性必须存在
  component: ''
}
//接受参数
  1. 通过path匹配路由,然后通过query传递参数
//页面列表参数
this.$router.push({
  path: '/custome/customeDetailPage',
  query: {
    name: 'id',
    dataObj: id
  }
})
//路由配置,name属性可有可无
{
  path: '/custome/customeDetailPage',
  component: ''
}
//接受参数
this.$route.query.dataObj
  • 声明式的导航 <router-link>
  1. 字符串
<router-link to = "news"></router-link>
  1. 命名路由
<router-link :to ={name: 'news',params: {userId: id}}></router-link>
  1. 查询参数
<router-link :to ={path: 'news',query: {userId: id}}></router-link>
  • 区别:
  1. 命名路由搭配params,刷新页面数据会丢失
  2. 查询路由搭配query,舒心页面数据不会丢失。
  3. 命名路由的URL没有参数,只有/news
  4. 查询路由的Url带有参数,/news?id = 111

vuex的mutation action的作用

  • mutation:处理的是同步事物,必须是同步函数。
  • 要更改vuex的state的状态的唯一办法就是提交mutation。
  • mutation需要遵守的规则:
  1. 最好提前在store中初始化好所有所需的属性
  2. 需要在对象上添加属性时使用set或者展开符[...]
  • 组件中可以使用this.$store.commit('xxx')提交Mutation,或者使用mapMutations辅助函数将methods映射为store.commit()
state: {
  num: [1,2,3,4]
}
mutations: {
  numChange(state){
    state.num[0]++
  }
}
  • action类似于mutation,但是不同于:
  1. action提交的是Mutation,而不是直接更改状态
  2. action可以包含异步操作
  • action函数可以接受一个与store实例具有相同方法和属性的context对象,因此可以使用context.commit()提交一个mutation,或者通过context.state或者context.getters来获取state或getters
  • 触发action:this.store.dispatch()或者mapMutations辅助函数

axios是如何做到响应式拦截的

借助axios的拦截器实现Vue.js中登陆状态校验

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345