- 一、VUE
- 二、JS
- 三、CSS
- 四、HTML
- 五、网络
- 六、git
- 七、单元测试
迈出了勇敢且踏实的第一步,是时候记下自己的薄弱点了
一、VUE
1. $attrs

如果在第二层组件声明第三层组件的加入属性 v-bind="$attrs" ,则第三层组件能接收到除第二层组件props声明的变量之外第一层组件传递给第二层组件的属性。
<template><div><h1>grandparent组件</h1><Parent:a="123":b="456":c="789"@confirm="onConfirm"@cancel="onCancel"/></div></template><script>import Parent from './parent';export default {components: { Parent },name: 'Grandparent',methods: {onConfirm: e => console.log(`grandparent onConfirm ${e}`),onCancel: e => console.log(`grandparent onCancel ${e}`)}};</script>
<template><div><br /><h1>parent组件</h1><h3 @click="$emit('confirm', 'parent')">a: {{ a }}</h3><Child v-bind="$attrs" v-on="$listeners" /></div></template><script>import Child from './child';export default {components: { Child },props: ['a'],name: 'Parent'};</script>
<template><div><br /><h2 @click="$emit('confirm', 'child')">child组件</h2><h3 @click="$emit('cancel', 'child')">b: {{ b }}</h3><h3>c: {{ c }}</h3><h4>a: {{ a }}</h4></div></template><script>export default {props: ['a', 'b', 'c'],name: 'Child'};</script>
拓展:
$listeners
与$attrs类似,只不过不会说第二层组件传递过的事件,第三层组件就无法传递,所有事件,子孙组件都能响应。只不过需要在第二层组件内声明第三层组件的属性中加入v-on="$listeners"
2. vue-router中钩子
2.1 全局钩子
beforeEach(to, from, next)
afterEach(to, from)
2.2 路由内共享
befoterEnter(to, from, next)
2.3 组件内拦截
beforeRouterEnter(to, from, next)
3. v-model 和 v-bind
3.1 v-model
- v-model 是
:value="msg" @input="msg=$event.target.value"的语法糖 - v-model 建立的双向绑定对输入型元素input, textarea, select等具有优先权,会强制实行双向绑定
3.2 vue底层Object.defineProperty()
Vue底层通过Object.defineProperty()劫持各个属性的getter和setter来实现双向绑定(订阅发布者模式)
4. hash和history区别

总结来看就是hash(默认hash模式)和history模式都就可以变更URL而无需重新加载页面,但是history模式需要后台配置所有资源都返回同一个index.html页面。
5. 状态存储
- 传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
- 经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
5.1 Mutation 需遵守 Vue 的响应规则

mutation 必须是同步函数
5.2 Action
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
- Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用context.commit 提交一个 mutation,
- 或者通过 context.state 和 context.getters 来获取 state 和 getters。
- 当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
- 一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
6. v-for为什么不能跟v-if一起用
v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。
v-for的优先级高会造成性能浪费,v-if会每个v-for循环体内都执行一遍
7. vue生命周期

8. v-for中key的作用
vue文档中有两段内容描述此问题,具体内容如下:

默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染
- 声明
key之后vue能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key attribute - 字符串或数值类型的值。

9. data 为什么要声明为一个函数
因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。
10. computed 和 watch
computed: 有缓存,依赖于另一些值,当那些值变更时才会变更watch: 需要在数据变化时执行异步或开销较大的操作
11. MVVM
- model
- 真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。
- view
- 用户在屏幕上看到的结构、布局和外观(UI)。
viewmodel
keep-alive缓存过滤(:include=”[]” :exclude=”[]”)vue-devtools显示的组件名称
13. $nextTick
- 官方文档:
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
例如,当你设置 vm.someData = ‘new value’,该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。
14. vue父子组件生命周期函数执行顺序

由上图可知:
1. 父组件 beforeCreate
- 父组件 created
- 父组件 beforeMount
- 子组件 beforeCreate
- 子组件 created
- 子组件 beforeMount
- 子组件 mounted
- 父组件 moutend
15. mixin选项合并策略
data 数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。先调用混入的钩子
值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
16. Keep-alive原理
原文:https://www.cnblogs.com/wangjiachen666/p/11497200.html
副本:图片副本展示
17. 单向数据流和双向绑定
17.1 单向数据流
- 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
- view层无法直接变更model层,view层会通知model层变更,model层变更完数据后,引起view层变更
17.2 双向绑定
v-model 是单向数据流的语法糖
18. props自定义验证函数
// 自定义验证函数propFunc: {validator: function (value) {// 这个值必须匹配下列字符串中的一个return ['success', 'warning', 'danger'].indexOf(value) !== -1}}
二、JS
1. 事件循环机制
js运行时有 栈 和 队列 以及 堆
当点击事件触发的时候,进入队列(先进先出),然后向栈中压入一个栈(先进后出)帧,相关的对象变量存入堆中,如果需要第二个变量就再次压入一个栈帧,直至栈空,队列首出队,继续执行下一个事件。所以说是一个事件循环,且非阻塞的
while (queue.waitForMessage) {queue.processNextMessage();}
2. js值引用和对象引用
- 值引用:基本类型直接使用的是值本身
- 对象引用:非基本类型使用的是内存地址
3. 箭头函数与普通函数区别
- 箭头函数
- 作用:
- 更简短的函数
- 不绑定this
- 作用:
- 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
- 普通函数作用:
- 如果是该函数是一个构造函数,this指针指向一个新的对象
- 在严格模式下的函数调用下,this指向undefined
- 如果是该函数是一个对象的方法,则它的this指针指向这个对象
4. Object.defineProperty
// 使用 __proto__var obj = {};var descriptor = Object.create(null); // 没有继承的属性// 默认没有 enumerable,没有 configurable,没有 writabledescriptor.value = 'static';Object.defineProperty(obj, 'key', descriptor);// 显式Object.defineProperty(obj, "key", {enumerable: false,configurable: false,writable: false,value: "static"});// 循环使用同一对象function withValue(value) {var d = withValue.d || (withValue.d = {enumerable: false,writable: false,configurable: false,value: null});d.value = value;return d;}// ... 并且 ...Object.defineProperty(obj, "key", withValue("static"));// 如果 freeze 可用, 防止后续代码添加或删除对象原型的属性// (value, get, set, enumerable, writable, configurable)(Object.freeze||Object)(Object.prototype);
4.1 Object.defineProperty缺陷
原文:https://www.jianshu.com/p/f78bb8ac06b4
副本:图片副本展示
5. 区分array和object
- Object.prototype.toString.call
- Object.prototype.toString.call(arr);//”[object Array]”
- Object.prototype.toString.call(arr); //”[object Object]”
6. instanceof和typeof区别
- instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
- typeof 操作符返回一个字符串,表示未经计算的操作数的类型。
6.1 typeof
- 数据类型
- 基本数据类型(可用typeof判断)
- undefined:typeof instance === “undefined”
- Boolean:typeof instance === “boolean”
- Number:typeof instance === “number”
- String:typeof instance === “string
- BigInt:typeof instance === “bigint”
- Symbol :typeof instance === “symbol”
- null:typeof instance === “object”。(特殊:视为对象的空指针)
- 引用数据类型
- 基本数据类型(可用typeof判断)

因为 **Class** 是 function 的语法糖,所以 **typeeof class A {}** 返回为 ‘function’
7. set和map区别
- set类似于数组,但是数据不会重复,使用 new set() 创建set对象
- map数据会重复,类似于对象,key可以是任意数据类型
- 求长度
.size() - 作用:数组去重
**[...new Set(Array)]**; arguments也属于类数组范围
8. call、apply、bind
注意:该方法的语法和作用与
apply()方法类似,只有一个区别,就是call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组。
8.1 call
function.call(thisArg, arg1, arg2, ...)
- 参数列表
thisArg
在 function 函数运行时使用的 this 值arg1, arg2, ...
指定的参数列表。
- 返回值:使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回
undefined。 - 如果没有传递第一个参数,this 的值将会被绑定为全局对象。
- 注意:在严格模式下,this 的值将会是
undefined。
8.2 apply
func.apply(thisArg, [argsArray])
- 参数列表
thisArg
必选的。在 func 函数运行时使用的 this 值。argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
- 返回值:调用有指定this值和参数的函数的结果。
8.2.1 将数组各项添加到另一个数组
push
将该数组作为单个元素添加,而不是将这个数组内的每个元素添加进去,因此我们最终会得到一个数组内的数组。但是我觉得使用...可解决此问题concat
不是将元素添加到现有数组,而是创建并返回一个新数组applylet arr1 = [1, 2, 3];let arr2 = [4, 5, 6];arr1.push.apply(arr1, arr2);console.info(arr1); // [1, 2, 3, 4, 5, 6]
calllet arr1 = [1, 2, 3];let arr2 = [4, 5, 6];arr1.push.call(arr1, ...arr2);console.info(arr1); // [1, 2, 3, 4, 5, 6]
8.2.2 找出数组中最大/小的数字
Math.max()Math.min()Math.max(...arr)applylet arr1 = [1, 3, -23, 56, 78, -56, 32, 85];/* 使用Math.min/Math.max以及apply 函数时的代码 */let max = Math.max.apply(null, arr1); /* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */let min = Math.min.apply(null, arr1);
8.3 bind
function.bind(thisArg[, arg1[, arg2[, ...]]])
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。- 参数
thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArg是null或undefined,执行作用域的 this 将被视为新函数的 thisArg。arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
- 返回值
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。
8.3.1 将一个类似于数组的对象(array-like object)转换成一个真正的数组
Array.prototype.slice.apply(arguments);
let funcSlice = Function.prototype.apply.bind(Array.prototype.slice);funcSlice(arguments);
9. 类数组

- 类数组定义
- 有
length属性 - 有
(0...length-1)范围的整数属性
- 有
- 比如:
{length: 3, 0: 'a', 1: 'b', 2: 'c'}
9.1 类数组转换为真正的数组

Array.prototype.slice.call(arguments)[].slice.call(arguments)Array.prototype.slice.apply(arguments)[].slice.apply(arguments)let funcSlice = Function.prototype.apply.bind(Array.prototype.slice);funcSlice(arguments);
Array.from(arguments);[...arguments]使用此方法有个前提,该元素必须包含iterable,VM287:1 Uncaught TypeError: arr is not iterable
9.1.1 Iterator(遍历器)的概念
next
一个无参数函数,返回一个应当拥有以下两个属性的对象:done(boolean)- 如果迭代器可以产生序列中的下一个值,则为 false。(这等价于没有指定 done 这个属性。)
- 如果迭代器已将序列迭代完毕,则为 true。这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。
value- 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。
- next() 方法必须返回一个对象,该对象应当有两个属性: done 和 value,如果返回了一个非对象值(比如 false 或 undefined),则会抛出一个 TypeError 异常(”iterator.next() returned a non-object value”)。
let arrLike = {length: 3, 0: 'a', 1: 'b', 2: 'c'};arrLike[Symbol.iterator] = function() {let nextIndex = 0;return {next: () => {return nextIndex < this.length ? {value: this[nextIndex++],done: false} : {done: true};}};}[...arrLike]; // ["a", "b", "c"]
10. for…of和for…in区别

for...in遍历可枚举属性for...of迭代的数据
11. 防抖和节流
- 防抖(debounce):**触发事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。**
- 节流(throttle):**高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。**
// 防抖function debounce(func, delay) {const [func1, delay2, ...args] = arguments;let timer = null;return () => {if (timer) {clearTimeout(timer);}timer = setTimeout(() => func.apply(this, args), delay);}}
// 节流function throttle(fn, delay){const [func1, delay2, ...args] = arguments;let timer = null;return () => {if (!timer) {timer = setTimeout(() => {fn.apply(this, ...args);timer = null;}, delay)}}}
// (立刻执行版)节流function throttle (action, delay) {let timeout = nulllet lastRun = 0return function () {if (timeout) {return}let elapsed = Date.now() - lastRunlet context = this // 重新设定this指向用let args = arguments // 获取请求参数let runCallback = function () {lastRun = Date.now()timeout = falseaction.apply(context, args) // 使用apply将this执行绑定到当前函数}if (elapsed >= delay) {runCallback()} else {timeout = setTimeout(runCallback, delay)}}}
12. symbol
symbol无法被 for…in 以及 数组遍历到
13. 多维数组展开成一维数组
function expand(arr) {while(arr.some(item=> Array.isArray(item))){// 关键点arr = [].concat(...arr)}return arr}expand([1, [2, 3, [4, 5]], [6, [[7, 8]]]]) // log: [1, 2, 3, 4, 5, 6, 7, 8]
14. splice和slice区别
14.0 区别
- splice会修改原有数组,slice不会
- splice返回值是修改的元素,slice返回值是选中的左闭右开区间内的元素
14.1 splice
splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
let test = [0, 1, 2, 3, 4, 5]test.splice(2, 0, 666, 777); // out: [], test: [0, 1, 666, 777, 2, 3, 4, 5]test.splice(2, 1); // out: [666], test: [0, 1, 777, 2, 3, 4, 5]

14.2 slice
slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
左闭右开区间

15. new
15.1 new做了什么操作
new 关键字会进行如下的操作:
- 创建一个空的简单JavaScript对象(即{});
- 为步骤1新创建的对象添加属性proto,将该属性链接至构造函数的原型对象 ;
- 将步骤1新创建的对象作为this的上下文 ;
- 如果该函数没有返回对象,则返回this。
15.2 new演示说明
当代码 new Foo(…) 执行时,会发生以下事情:
- 一个继承自 Foo.prototype 的新对象被创建。
- 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
- 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
16. substr和substring区别
16.1 substr

16.2 substring

17. reduce
18. Array.of

三、CSS
1. 垂直居中方法
display: flex;flex-direction: row;align-items: center;justify-content: center;
line-height: 100px;
display: inline-block;vertical-align:middle;
- 绝对定位(缺点:需要计算)
**margin: 0 auto;**top、margin
position: relative; /* 相对定位或绝对定位均可 */width:500px;height:300px;top: 50%;left: 50%;margin: -150px 0 0 -250px; /* 外边距为自身宽高的一半 */
top、transform
position: absolute; /* 相对定位或绝对定位均可 */width:500px;height:300px;top: 50%;left: 50%;transform: translate(-50%, -50%);
2. flex布局
页面分为头、中部无限滑动内容区和尾三个部分,如何使用flex布局解决:
<div style="display:flex; height: 100vh"><div style="height:40px">头</div><div style="flex:1; overflow:scroll">中部无限滑动区</div><div style="height:40px">尾</div></div>
3. transform、translate和transition
3.1 transform

3.2 translate

3.3 transition

4. 清除浮动
.clear-fix&::aftercontent: ""display: tableclear: both
5. 0.5px边框
.border-top-1px, .border-right-1px, .border-bottom-1px, .border-left-1pxposition: relative&::before, &::aftercontent: ""display: blockposition: absolutetransform-origin: 0 0.border-top-1px&::beforeborder-top: 1px solid $color-row-lineleft: 0top: 0width: 100%transform-origin: 0 top.border-right-1px&::afterborder-right: 1px solid $color-col-linetop: 0right: 0height: 100%transform-origin: right 0.border-bottom-1px&::afterborder-bottom: 1px solid $color-row-lineleft: 0bottom: 0width: 100%transform-origin: 0 bottom.border-left-1px&::beforeborder-left: 1px solid $color-col-linetop: 0left: 0height: 100%transform-origin: left 0@media (min-resolution: 2dppx).border-top-1px&::beforewidth: 200%transform: scale(.5) translateZ(0).border-right-1px&::afterheight: 200%transform: scale(.5) translateZ(0).border-bottom-1px&::afterwidth: 200%transform: scale(.5) translateZ(0).border-left-1px&::beforeheight: 200%transform: scale(.5) translateZ(0)@media (min-resolution: 3dppx).border-top-1px&::beforewidth: 300%transform: scale(.333) translateZ(0).border-right-1px&::afterheight: 300%transform: scale(.333) translateZ(0).border-bottom-1px&::afterwidth: 300%transform: scale(.333) translateZ(0).border-left-1px&::beforeheight: 300%transform: scale(.333) translateZ(0)
6. 重排和重绘
- 重排
- 重排一定引起重绘
- DOM节点几何尺寸变更
- 重绘
- 仅DOM节点背景颜色,颜色之类的变更,不涉及几何变更
- 故优化方案是尽可能修改className的方式变更元素,而不是style修改
7. 阻止用户复制
**user-select:none;**
8. flex-direction默认值
**row**
四、HTML
svg、canvas、语义化(footer、nav)、worker、audio和video
五、网络
1. HTTPS和HTTP
1.1 HTTP
- 运行在TCP之上
- 无状态协议
- 一般是80端口
- 请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体
- 应答报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体
- 请求过程:由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如”HTTP/1.1 200 OK”,以及返回的内容,如请求的文件、错误消息、或者其它信息。
- 状态码
- 1xx消息——请求已被服务器接收,继续处理
- 2xx成功——请求已成功被服务器接收、理解、并接受
- 3xx重定向——需要后续操作才能完成这一请求
- 301 永久重定向,更新书签
- 302 临时重定向,不更新数签
- 303 跟302类似功能,只不过明确表示将POST请求改成GET请求

- 304 服务器允许请求访问资源,但未满足条件
- 307 跟302类似功能,只不过会按照标准,不会将POST请求变更为GET请求
- 4xx请求错误——请求含有词法错误或者无法被执行,客户端错误
- 5xx服务器错误——服务器在处理某个正确请求时发生错误,服务器错误
- HTTP缺点
- 明文传送
- 消息完整性检测的缺乏 (报文头部包含了本次传输数据的长度, 而对内容是否被篡改不作确认)
1.2 HTTPS
- 一般是443端口
- 提供对网站服务器的身份认证,保护交换数据的隐私与完整性。
- HTTPS报文中的任何东西都被加密,包括所有报头和荷载
- HTTPS = HTTP + SSL
- HTTPS 在 HTTP 应用层的基础上使用安全套接字层作为子层。
1.3 HTTPS和HTTP区别


1.4 GET和POST区别
get把参数放在URL连接上,不安全,且在不同的浏览器有最大长度限制,理论上没限制。可缓存
post把请求参数放在消息主体里,不可缓存,后退刷新会重复提交,可携带更多参数。
1.5 三次握手
为了保证服务端能收接受到客户端的信息并能做出正确的应答而进行前两次(第一次和第二次)握手,为了保证客户端能够接收到服务端的信息并能做出正确的应答而进行后两次(第二次和第三次)握手。
1.6 用户浏览器地址栏输入到展示网页过程

2. header请求头 ???
3. HTTP缓存
- 强缓存
- 不发起http请求,直接使用本地缓存,浏览器地址栏回车,浏览器刷新按钮。
Expires/max-age生效的情况下,触发的都是 Expires绝对过期时间max-age相对过期时间cache-control=no-cache
- 不发起http请求,直接使用本地缓存,浏览器地址栏回车,浏览器刷新按钮。
- 弱缓存(协商缓存)
- 使用本地缓存前,先与服务器协商,核对缓存文件是否为最新。
4. 预检请求
重点讲一下为什么要预检请求:
浏览器的同源策略,就是出于安全考虑(避免跨站请求伪造(英语:Cross-site request forgery)CSRF),浏览器会限制从脚本发起的跨域HTTP请求,像XMLHttpRequest和Fetch都遵循同源策略。
浏览器限制跨域请求一般有两种方式:
- 方式一:浏览器限制发起跨域请求
- 方式二:跨域请求可以正常发起,但是返回的结果被浏览器拦截
一般浏览器都是第二种方式限制跨域请求,造成的后果就是请求已到达服务器,并有可能对数据库里的数据进行了操作,但是返回的结果被浏览器拦截了,那么我们就获取不到返回结果,这是一次失败的请求,但是可能对数据库里的数据产生了影响。
为了防止这种情况的发生,规范要求,对这种可能对服务器数据产生副作用的HTTP请求方法,浏览器必须先使用
**OPTIONS**方法发起一个预检请求,从而获知服务器是否允许该跨域请求:如果允许,就发送带数据的真实请求;如果不允许,则阻止发送带数据的真实请求。
5. Cache-Control


六、git
1. reset和revert的区别
reset是真正意义上的回退到指定的以前的commit,提交需要添加‘-f’
revert是反作用力,撤销之前提交的某个commit,中间的commit,之后的不撤销用此方法。git revert -n commit-id
七、单元测试
组件的单元测试有很多好处:
- 提供描述组件行为的文档
- 节省手动测试的时间
- 减少研发新特性时产生的 bug
- 改进设计
- 促进重构






