HTML
1. 重绘与回流
一个页面从加载到完成,首先生成DOM树,然后根据DOM节点的几何属性生成render树(渲染树),当渲染树构建完成,页面开始根据DOM树布局,渲染树也会根据设置的样式渲染节点
回流: 当我们删除或修改元素高度时,页面会重新布局,DOM树发生变化,引起渲染树重新渲染,这个过程叫做回流(回流一定造成重绘)
重绘: 当修改元素的颜色,渲染树会根据新规则重新渲染,这个过程叫做重绘(重绘不一定造成回流)
如何减少回流
- 对DOM进行多次添加删除操作时,使用documentfragment对象(在该对象中对DOM进行操作,完成后append到文档中,即可只进行一次回流)
function addDivs(element) { var div; // Creates a new empty DocumentFragment. var fragment = document.createDocumentFragment(); for (var i = 0; i < 20; i ++) { div = document.createElement('a'); div.innerHTML = 'Heya!'; fragment.appendChild(div); } element.appendChild(fragment);}复制代码
- 使用定位脱离文档流改变位置
- 避免逐项更改样式,将样式列表定义为class并一次性更改class属性
- 避免循环读取offsetLeft等属性,在循环之前把它们缓存起来。
2. 获取点击元素的下标
------ html-------
- Coffee
- Milk
- Soda
- 原生js
let lis = document.getElementsByTagName('li')for (let i=0; i
- jquery的index()方法
$('li').click(function () { console.log($(this).index())})复制代码
CSS
1. css布局,实现顶部高固定,左侧导航宽固定,右侧自适应
2. 三大定位,相对定位放在固定定位产生什么影响?
fixed、relative、absolute
相对定位和固定定位,都会使块级元素产生BFC,下面通过步骤检测一下
-
设置父元素为固定定位,不设置高度,内部box设置高度和宽度,根据BFC内部box垂直排列的特征,效果如下
1.sj{ position: fixed; top: 0; left: 0; width: 200px; background-color: #ccc; } .sj>div{ height: 20px; width: 100px; background-color: #2db7f5; }复制代码
- 若将内部box设为绝对定位,即内部box会产生BFC,根据BFC与外部互不影响的特征,内部box将无法撑起父元素高度,如下
.sj>div{ height: 20px; width: 100px; position: absolute; background-color: #2db7f5;}复制代码
3. 伪类和伪元素
伪类:向某些选择器设置特殊效果,用于选择器选择不到的元素
伪元素:向某些选择器添加特殊效果
- 伪类本质为了弥补选择器的不足,以此获取更多信息
- 伪元素创建一个新的虚拟容器,该容器内不包含dom节点,但可以添加内容,并且可对伪元素追加样式
- 伪类“:”,伪元素“::”
- 可以同时使用多个伪类,但同时只能使用一个伪元素
4. 纯css画三角形
5. CSS BFC是什么?
BFC是一个独立的块级渲染容器,拥有自己的渲染规则,不受外部影响,不影响外部
特征
- 内部box垂直往下排列
- 内部块元素受maigin特征的影响,上下外边距会塌陷
- BFC区域不会遮盖浮动元素区域
- 计算BFC高度时,浮动元素高度也计算在内
- BFC是独立渲染容器,外部元素不影响内部,反之亦然
产生条件
- 固定定位和绝对定位
- float除了none外
- overflow除了visible外(hidden、auto、scroll)
- display为以下其一(inline-block、table-cell、table-caption)
作用
- 清除浮动
- 消除margin重叠
- 布局(左浮动,右BFC自适应)
6. 清除浮动的方式
1. 父元素设置伪类:clear:both + zoom:1
设置zomm为了兼容IE
.parent1{ border: 1px solid red;}.parent2{ height: 100px; border: 1px solid blue;}.left{ width: 200px; height: 200px; background-color: #5cadff; float: left;}.clearfloat::after{ display: block; clear: both; content: ''; visibility: hidden; height: 0;}.clearfloat { zoom: 1}复制代码
2. 结尾处添加空白标签:claer:both
.clearfloat{ clear: both;}复制代码
3. 父元素产生BFC
BFC内浮动元素高度计算在内
7. 水平垂直居中的实现方式,尽可能多
方法一、定位 + transform
.parent{ height: 500px; width: 500px; border: 1px solid red; position: relative; } .child{ height: 80px; width: 80px; background-color: #515A6E; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }复制代码
方法二、margin + transform
.parent{ height: 500px; width: 500px; border: 1px solid red; } .child{ height: 80px; width: 80px; background-color: #515A6E; margin: 50% 0 0 50%; transform: translate(-50%, -50%); }复制代码
方法三、定位 + 负margin
.parent{ height: 500px; width: 500px; border: 1px solid red; position: relative; } .child{ height: 80px; width: 80px; background-color: #515A6E; position: absolute; top: 50%; left: 50%; margin: -40px 0 0 -40px; }复制代码
方法四、flex
.parent{ height: 500px; width: 500px; border: 1px solid red; display: flex; align-items: center; justify-content: center; } .child{ height: 80px; width: 80px; background-color: #515A6E; }复制代码
方法五、table-cell
.parent{ height: 500px; width: 500px; border: 1px solid red; display: table-cell; text-align: center; vertical-align: middle; } .child{ display: inline-block; height: 80px; width: 80px; background-color: #515A6E; }复制代码
8. 盒子模型
标准盒子模型
width = content
IE盒子模型
width = border + padding + content
9. 块级元素和行内元素的区别?img可设置宽高吗?
块级元素
- 独占一行,在默认情况下,其宽度自动填满其父元素的宽度
- 块级元素可以设置width、height属性
- 块级元素即使设置了宽度也是独占一行,块级元素可以设置margin、padding属性
行内元素
- 行内元素不会独占一行,相邻的行内元素会排列在同一行里,直到行排不下,就自动换行,其宽度随内容而变化
- 行内元素的width、height属性则无效
- 水平方向的padding、margin会产生边距效果,竖直方向的padding、margin不会产生边距效果
行内置换元素
浏览器依据元素的标签和属性来决定元素的具体显示内容
img、input、textarea、select、object属于行内置换元素, 具有块级元素的特征(除宽度外)
10. absolute定位 不设置top、right、bottom、left有什么效果?
元素脱离文档流,但因为没有设置属性导致无法具体定位,紧跟在上个元素之后,但下个元素排列时会忽略此元素
11. css权重
222 // 红色div span{ color: blue;}#a { color: red;}复制代码
!important(10000) > 内联样式(1000) > id(100) > class|伪类|属性选择(10) > 标签|伪元素(1) > 通配符(0) > 继承(无)
JavaScript
1. js输出数组中出现最多的元素和次数
2. 100阶台阶一次走1步或2步有多少种走法
假设只走一个台阶,有1种走法;两个台阶,2中走法,三个台阶,3种走法;四个台阶,5种走法...
1->1; 2->2; 3->3; 4->5...可以看出此问题是斐波那契数列,即下个值是前两个值的和
公式为:f(n) = f(n-1) + f(n-2)
方法一、遍历相加
function test (num) { let num1 = 1, num2 = 2, res = 0 for (let i=2; i
方法二、递归 (不推荐)
function test (num) { if (num === 1) return 1 else if (num === 2) return 2 else return test(num-1) + test(num-2)}复制代码
3. 如何实现抽奖转盘
4. 闭包及使用场景
-
闭包
- 外部函数能够访问内部函数的变量
- 闭包所在的函数变量不能被垃圾回收机制回收
- 常驻内存,占用内存使用量
-
使用场景
- 给对象设置私有变量并且利用特权方法去访问私有属性
- 采用函数引用方式的setTimeout调用
- 封装相关功能集
5. 什么是原型、原型链?
- 原型对象也是普通的对象,是对象一个自带隐式的__proto__属性,原型也有可能有自己的原型,如果一个原型对象的原型不为null的话,我们就称之为原型链。
- 原型链是由一些用来继承和共享属性的对象组成的(有限的)对象链。
6. 类的创建与继承
- 创建:new 一个Function,在prototype中添加属性和方法
- 继承:
- 原型链继承,基于原型链,是父类和子类的实例,无法实现多继承
function Cat(){}Cat.prototype = new Animal();Cat.prototype.name = 'cat';// Test Codevar cat = new Cat();复制代码
- 构造函数继承:复制父类的实例给子类,只继承父类实例的属性与方法,不继承原型上的,可实现多继承
function Cat(name){ Animal.call(this) this.name = name || 'Tom';}var cat = new Cat()复制代码
- 组合继承(原型链继承+构造函数继承)
7. new做了什么事
- 创建一个空对象
- 将该对象的原型指向创建该对象的构造函数的prototype上
8.创建对象的方式
- 工厂模式
- 构造函数模式
- 原型模式
- 混合构造函数与原型模式
9. 深拷贝
- 数组的深拷贝
let newArr = JSON.Parse(JSON.Stringify(oldArr))复制代码
扩展运算符let newArr = [...oldArr]复制代码
- 对象的深拷贝
function deepClone (obj) { let newObj = obj instanceof Array ? [] : {} for(var i in obj){ newObj[i] = typeof obj[i] == 'object' ? deepClone(obj[i]) : obj[i] } return newObj}复制代码
10. 数组操作
11. 写一个js函数,实现对一个数字每3位加一个逗号
function test (num) { let arr1 = [], arr2 = [],arr = [] // arr1保存小数前, arr2保存小数后 arr = num.toString().split('.') arr2 = arr[1] ? [...arr[1]] : [] // 判断是否存在小数,并将每项转为数组元素 arr1 = [...arr[0]] let newArr1 = arr1.map((item, index) => arr1.length === (index+1) && (index+1)%3 === 0 ? item : (index+1)%3 === 0 ? item+',' : item) let newArr2 = arr2.map((item, index) => arr2.length === (index+1) && (index+1)%3 === 0 ? item : (index+1)%3 === 0 ? item+',' : item) // 数组为空则map()不检测 newArr2.unshift('.') console.log(newArr1.concat(newArr2).join('')) } test(123456789.123)复制代码
12. 手写快速排序
快速排序采用分治法的思想,将一个复杂问题分为两个或多个子问题,直到子问题简单到可以直接求解,那么子问题的解的组合便是原问题的解
function quickSort (arr) { if (arr.length <= 1) return arr; //取中间位置的数据作为基准值,并从原数组中删除该基准值 let jzIndex = Math.floor(arr.length/2) // 获取基准值下标 let jzNum = arr.splice(jzIndex, 1) // 删除并获取基准值 let leftArr = [], rightArr = [] // 分别保存小于和大于基准值的数据 arr.forEach(item => { if (item < jzNum[0]) { leftArr.push(item) } if (item >= jzNum[0]) { rightArr.push(item) } }) //concat()连接两个数组 return quickSort(leftArr).concat(jzNum, quickSort(rightArr)) } console.log(quickSort([10,5,15,2,4]))复制代码
13. 函数的防抖与节流
防抖: 任务频繁触发情况下,只有两次任务间隔超过指定时间,才会执行。若还未超过却又一次触发,则时间重新开始计算
// 防抖函数 function debounce (fn, time) { // 新建一个变量保存定时器 let timeout = null; return function () { // 每次用户点击、输入时,清空上一个定时器 clearTimeout(timeout) timeout = setTimeout(() => { fn.call(this, arguments) }, time) } } // 处理函数 function handler () { console.log('防抖成功!') } // 触发 debounce(handler, 1000)复制代码
节流: 频繁触发任务,任务按照一定时间间隔进行执行
// 节流函数 function throttle (fn, time) { // 利用闭包保存是否可执行的变量 let canRun = true return function () { // 如果为false,则终止函数执行 if (!canRun) return; // 执行前将变量设为false canRun = false setTimeout(() => { fn.call(this, arguments) canRun = true }, time) } } // 处理函数 function handler () { console.log('节流成功!') } // 触发 throttle(throttle, 1000)复制代码
14. 随机生成指定范围内的随机数
ES6
1. Promise相关问题
1. 什么是Promise?
- promise是异步编程的解决方案,比起传统的异步解决方案“回调函数”、“事件”更合理强大,已被ES6纳入规范
- 具有三种状态:pending 过渡态、fulfilled 完成态、rejected 失败态,状态一旦确定不可修改
2. Promise优缺点?
优点:
- 解决回调地域
- 可读性高,便于维护
缺点:
- 无法停止,一旦创建Promise立即执行
- 必须指定回调,否则内部抛出异常
- 当处于pending状态时,无法确定此时进行到哪一阶段(刚开始还是即将完成)
3. Promise的方法有哪些?作用都是什么?
-
Promise.prototype.then()
Promise 实例添加状态改变时的回调函数,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
-
Promise.prototype.catch()
.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
-
Promise.prototype.finally()
无论Promise最后状态如何,都会执行finally内的函数,ES2018引入
-
Promise.all()
多个Promise同时执行,若全部成功,则以数组形式返回执行结果;若有一个是rejected,则只返回rejected的结果
-
Promise.race()
多个Promise同时执行,返回最先结束Promise执行任务的结果,无论成功或失败
-
Promise.resolve()
返回一个新的 Promise 实例,该实例的状态为resolve
- 若参数为Promise实例,则返回该实例
- 参数是一个thenable对象,(thenable为具有then方法的对象),将此对象转换为Promise并立即执行then方法
- 参数不是具有then方法的对象,或根本就不是对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved
- 若不带任何参数,直接返回一个resolved状态的 Promise 对象
-
Promise.reject()
返回一个新的 Promise 实例,该实例的状态为rejected
4. promise如何使用?
- Promise是构造函数,需要new一个Promise的实例
- Promise构造函数的参数为一个函数,函数内的参数为resolved和rejected
- 实例后用then方法分别指定resolved状态和rejected状态的回调函数
- then方法接受两个参数,rejected非必须;then返回Promise对象,则可链式调用
5. 手写一个promise
// 定义Promise构造函数 function Promise (exector) { let self = this; this.value = undefined; this.reason = undefined; // 定义Promise的状态 this.status = 'pending' // 存储then中成功的回调函数 this.onResolvedCallbacks = []; // 存储then中失败的回调函数 this.onRejectedCallbacks = []; function resolve (value) { if (self.status === 'pending') { self.value = value self.status = 'resolved' self.onResolvedCallbacks.forEach(fn => fn()) } } function reject (reason) { if (self.status === 'pending') { self.reason = reason self.status = 'rejected' self.onRejectedCallbacks.forEach(fn => fn()) } } // 异常处理 try{ exector(resolve, reject) }catch (e) { reject(e) } } // 在Promise原型上定义then方法,参数为成功和失败的回调 Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; if (this.status === 'resolved') { onFulfilled(self.value) } if (this.status === 'rejected') { onRejected(self.reason); } if (this.status === 'pending') { this.onResolvedCallbacks.push(() => { onFulfilled(self.value) }) this.onRejectedCallbacks.push(() => { onRejected(self.reason) }) } } let promise = new Promise((resolve, reject)=> { setTimeout(() => { resolve('success') }, 1000) }) promise.then(data => { console.log(data) }, err => { console.log(err) })复制代码
webpack
webpack是模块打包工具,对js模块和扩展语言进行打包供浏览器识别运行
- webpack只能处理js代码,其他格式需要通过loader进行转换
- 可对浏览器不能识别的规范和静态文件进行分析、压缩、合并、打包成可供浏览器支持的代码
1. webpack和Grunt以及Gulp相比有什么区别
-
Grunt和Gulp属于任务流工具Tast Runner
-
webpack属于模块打包工具 Bundler
2. webpack 的 loader 和 plugin 区别,举几个常用的 loader 和 plugin 并说出作用
- loader用于对模块代码进行转换,可将不同格式语言转换为JavaScript,或将图像转换为Data Url,由于webpack只能够识别JavaScript,所以不同类型的模块需要对应的loader进行转换
- plugin是webpack的扩展插件,可完成loader无法完成的复杂功能,可控制webpack每个打包环节的流程,极大丰富了webpack的扩展性
3. webpack打包过程
- 读取文件,分析模块的依赖
- 对模块进行解析执行(深度遍历)
- 针对不同的模块使用不同的 loader
- 编译模块,生成抽象语法树(AST)
- 遍历 AST,输出 JS
4. webpack打包优化
- 使用compression-webpack-plugin压缩代码
- 使用cdn引入第三方库
- 按需引入第三方库里不同组件
Vue
1. 兄弟组件如何通信,无嵌套,回调触发
方法一、通过父组件通信
此方法需要保证兄弟组件A、B都在同一个父组件下;
父组件通过接受子组件A传递过来的事件消息,并调用子组件B
子组件Athis.$emit('transmit', 'msg')复制代码
父组件transmit (data) => { this.msg = data }复制代码
子组件B、需要使用watch来监听父组件props穿过来的数据变化watch (new, old) { 数据操作... }复制代码
方法二、eventBus
通过创建Bus.js注册一个全局实例,通讯组件通过调用实例的方法达到通讯目的
- 创建Bus.js
// eventBus 共享vue实例,用于兄弟组件数据传递import Vue from 'vue'const Bus = new Vue({})export {Bus}复制代码
- 组件A导入Bus.js 并emit消息
import {Bus} from './Bus.js'Bus.$emit('transmit', 'msg')复制代码
- 组件B导入Bus.js并在mounted中检测数据变化
import {Bus} from './Bus.js'mounted () { Bus.$on('transmit', (data) => { 操作... })}由于$on事件无法主动销毁,所以需要根据业务手动进行销毁在组件销毁前方法中销毁beforeDestory () { Bus.$off('transmit')}或者在使用前进行销毁mounted () { Bus.$off('transmit') Bus.$on('transmit', (data) => { 操作... })}复制代码
2. vueRouter的工作原理
Vue Router 是路由管理器,可以改变url而不向服务器发送请求,页面无需刷新
有hash和history两种路由模式
hash模式
- #后的hash值改变,会触发onhashchange事件
- hash的变化会被浏览器记录(历史访问栈)下来,可以前进、后退、刷新而不向服务器发送请求
- 通过匹配#后面的hash,与vueRouter配置文件里的path对应做路由跳转
history模式
- 基于浏览器history的pushState()、replaceState()、popState()来实现,可以读取、修改浏览器历史记录栈
- 可以前进、后退不发往服务器发送请求,但刷新会向服务器发送请求
- 若匹配不到资源,则返回404,因此需要后台支持,重定向到根目录主页
动态路由匹配中会复用同一组件,这就导致再次访问组件不被重新渲染,声明周期钩子不会执行,这就需要我们用watch去监听路由的变化
watch: { $route(to, from) { ······ }}复制代码
3. Vuex的理解
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
Vuex包含五个属性:state、getter、mutation、action、module
- state:存储数据,存储状态;注册了Store实例后,可通过this.$store.XX来访问;对应组件内的data,数据为响应式,组件通过store来读取vuex的数据,若数据改变,则组件的数据也会改变
- getter:store的计算属性,它的返回值会被依赖缓存起来,当依赖值发生变化时才会被重新计算
- mutation:更改Vuex的store中状态的唯一方法是提交mutation
- action:包含任意异步操作,通过提交 mutation 间接更变状态
- module:将 store 分割成模块,每个模块都具有state、mutation、action、getter、甚至是嵌套子模块
当组件进行数据修改的时候,通过调用dispatch来触发actions里面的方法,actions里每个方法都有commit方法,通过执行commit方法来触发mutation里的方法进行数据修改,由于mutation里每个函数都有一个state参数,进而可对state进行修改,当数据修改完毕后,会传导给页面。页面的数据也会发生改变。
4. vue Dom渲染的过程和原理
- new Vue 初始化Vue实例
- 通过三种渲染模式 Render、el、template 生成Render函数
- 通过Watcher监测数据变化
- 当数据变化时,通过Render函数生成VNode
- 通过patchVnode对比前后变化,通过diff进行更新、添加、删除等操作生成真实Dom节点
5. watch用法
watch用来监听并响应数据的变化
- 可以直接监听基本数据类型数据
- 若是监听对象,则需要开启deep(深度监听)
- 若是监听数组,则不需要开启deep监听
- immediate 初始化绑定值时即执行监听
- watch首次初始化绑定不执行,但监听的值发生变化时则执行监听
data () { return { age: 20, obj: { age: 24 }, arr: [1,2,3] }}复制代码
1. 监听基本类型watch: { age (newd, oldd) { ... }}复制代码
2. 监听对象watch: { obj: { handler (newd, oldd) { ... }, deep: true, // 开启深度监听 immediate: true // 首次即执行 }}复制代码
3. 监听对象某个属性*** 采用字符串watch: { 'obj.age': { handler (newd, oldd) { ... } }}*** 利用computed计算属性computed: { age () { return this.obj.age }}watch: { age (newd, oldd) { ... } // 也可写为 age: { handler (newd, oldd) { ... } }}复制代码
6. vue nextTick
异步更新队列
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的Promise.then和MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。
例如,当你设置 vm.someData='newvalue',该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新
nextTick
由于DOM是异步执行更新的,有时我们修改完数据等待DOM更新后进行操作,则此刻可使用Vue.nextTick(callback)
Vue.component('example', { template: '{ { message }}', data: function () { return { message: '没有更新' } }, methods: { updateMessage: function () { this.message = '更新完成' console.log(this.$el.textContent) // => '没有更新' this.$nextTick(function () { console.log(this.$el.textContent) // => '更新完成' }) } }})复制代码
因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的ES2016async/await语法完成相同的事情:
methods: { updateMessage: async function () { this.message = 'updated' console.log(this.$el.textContent) // => '未更新' await this.$nextTick() console.log(this.$el.textContent) // => '已更新' }}复制代码
7. vue 生命周期有哪些?
- beforeCreate ----创建前
- created ---- 成功创建
- beforeMount ---- 挂载前
- mounted ---- 成功挂载
- beforeUpdate ---- 更新前
- updated ---- 成功更新
- beforeDestroy ---- 销毁前
- destroyed ---- 成功销毁
8. vue 响应式数据是如何实现的?
- 遍历实例中的data的所有属性,并通过Object.defineProperty将其转换为setter、getter
- 每个实例都有一个Watcher、并通过Dep去关联data中的setter、getter的依赖
- 当data改变时,setter会触发Dep依赖,并触发Watcher.update
- 由于Object.observe的废除,导致无法检测对象的变化
- 无法检测根级对象的属性变化,可通过Vue.set(this.obj, key, value)来向对象动态添加响应式属性
网络
1. 浏览器输入地址后发生什么
- 解析URL
- DNS解析、监测缓存
- 客户端与服务端建立TCP连接(三次握手)
- 请求与传输数据
- 进行数据渲染
- 解析HTML、生成DOM树、解析CSS代码、将数据渲染到网页上
2. 轮询、长轮询、长连接、websocket区别
- 轮询:定时向服务器发送ajax请求,无需等待响应
- 长轮询:客户端向服务器发送请求,服务器保持连接,当有更新消息立即返回客户端,连接关闭。
- 长连接:客户端向服务器发送请求,服务器保持连接,当有更新消息立即返回客户端,连接保持。
- websocket:h5提供的基于TCP的双向通讯协议,服务器主动推送消息到客户端,只需一次握手连接,即可建立持久连接
3. 三次握手、四次分手
- 三次握手
客户端 ->(建立连接)-> 服务器 客户端 <-(确认接受,建立连接)<- 服务器 客户端 ->(确认接受)-> 服务器复制代码
- 四次分手
客户端 ->(关闭连接)-> 服务器 客户端 <-(确认关闭)<- 服务器 客户端 <-(关闭连接)<- 服务器 客户端 ->(确认关闭)-> 服务器复制代码
4. 请求状态码
2XX 请求成功
- 200 请求成功
- 204 请求成功,但未有任何资源返回
- 206 服务器成功处理部分get请求)
3XX 重定向
- 301 url已更新
- 302 url重定向
- 303 是否访问新的url
- 304 访问缓存资源,服务器无数据返回
4XX 客户端错误
- 401 需要验证
- 403 不允许访问
5XX 服务器错误
5. 安全问题
XSS(跨站脚本攻击)是在网页中注入非法的js脚本,获取cookie达到控制浏览器的目的
- DOM xss : 使用DOM可以允许程序和脚本动态的访问和更新文档的内容、结构和样式
- 反射性 xss:发出请求时,XSS代码出现在URL中,最后输入提交到服务器,服务器解析后在响应内容中出现这段XSS代码,最后浏览器解析执行。
- 存储型 xss:当攻击者提交一段XSS代码后,被服务器端接收并存储,当所有浏览者访问某个页面时都会被XSS,其中最典型的例子就是留言板。
危害:
- 利用虚拟输入表单获取用户信息
- 获取cookie,可代替用户进行危险操作
防范方法:
- httpOnly: 在 cookie 中设置 HttpOnly 属性,使js脚本无法读取到 cookie 信息
- 前后端校验输入表单格式,过滤不合理字符
CSRF 跨站点请求伪造,冒充用户发起请求,完成一些违背用户意愿的事情(如修改用户信息等)
危害:
- 利用已认证过的用户对商品信息、个人信息进行违法操作等
防范方法:
- 验证码: 强制用户与应用进行交互
- 减少get请求
- token验证CSRF防御机制是公认最合适的方案
token使用原理
- 后端随机生成一个token,并将此token携带在cookie中,然后将此token传递给前端页面
- 前端获取此token后,每次请求都携带token
- 后端验证token是否一致,若一致则请求合法
6. ajax请求过程
- 创建XMLHttpRequest对象,
- 创建http请求,并指明请求的url,方式和参数
- 设置http请求状态变化的函数
- 发送http请求
- 通过回调获取返回的数据
- 渲染
7. 跨域的方式
同源策略: 具有相同的协议(http/https)、域名、端口即为同源,不存在跨域,反之亦然
-
跨域资源共享(CORS)
需后端设置
-
服务器转发代理
请求同源地址的代理接口,服务器访问跨域接口并返回数据
-
jsonp
利用script标签不受同源策略限制的特征,在src内写入请求地址,末尾回调处理数据
复制代码
8. cookie、sessionStorage、localStorage
cookie
- 存储大小最大为4k
- 总是携带于http请求中
- 需设置max-age,超过时间则被销毁
sessionStorage
- 存储大小最大为5M
- 存储于本地中
- 当前会话有效,页面或浏览器关闭则销毁
- 从A跳转到B,B可与A共享sessionStorage;若单独打开B,则获取为null
localStorage
- 存储大小最大为5M
- 存储于本地中
- 若未设置过期时间,则永久保存
- 页面之间共享
优化
1. 图片优化
图片大致分为png、jpg、gif三种
- gif体积小,很好的压缩效果,支持动画,支持透明效果;但色彩效果最低
- png压缩效果好,支持透明效果,色彩不如jpg,最为通用
优化方案:
- 雪碧图
- 即css sprites, 就是把很多小图片制作成一个大图,然后作为背景图片使用,定位即可
- 优点: 体积小,引入一张图片即可,减少http请求
- 图片压缩
- 使用canvas的drawImage()进行压缩
- 上传至七牛云或阿里云进行压缩
- base64
- 利用编码存储图片,减少http请求
- 响应式图片
- 利用srcset 和 sizes 属性做响应式图片
- 延迟加载
- 当图片很多时,全部加载耗时过多也很可能造成卡顿
- 仅加载可视区域内的图片,非可视区域使用通用图片占位,当页面滚动进入可视区域后,再加载此区域内的图片
当加载图片过大时,会出现局部逐步加载情况,用户体验差;可通过哟图片的onload方法来判断图片是否加载完成,未加载完前先display:none;进行隐藏,当onload后在进行加载。
其他
1. 轮流向圆形桌子放硬币,如果保证能赢,谁先放?
假设桌子小到只能放下一枚硬币,那么先放者赢;若不确定桌子大小,则首先在圆心处放硬币,然后在对手放完后的关于圆心的对称点处再放硬币,确保先放者赢