一、html/css
1.盒模型中普通盒模型和怪异(ie)盒模型的区别
| 普通盒模型的width和height计算是按照content内容的大小算的,不包括padding,border,margin |
| IE盒模型的width和height包括padding和border,和content |
复制
2.块元素和行内元素的区别,常见的块元素和行内元素?
| 块元素单独占据一行,行内元素一行可以存在多个。 |
| 块元素可以设置width和height,行内元素设置无效 |
| 块元素内可以包含行内元素,行内元素内部不可以包含块元素 |
| |
| 常见的块元素有div,li等,常见的行内元素有i span等 |
复制
3.html语义化标签有哪些?
| <header></header> |
| <h1></h1> |
| |
| <footer></footer> |
| <main></main> |
| <section></section> |
| <aside></aside> |
| <small></small> |
| <strong></strong> |
| <nav></nav> |
复制
4.伪类和伪元素的区别
| 伪类:一些标签的状态属性(:active ,:focus ,:hover ,:link ,:visited ,:first-child ,:lang) |
| 伪元素:不添加就不存在的元素( :before ,:after) |
| 区别:单双括号,伪元素是创建了一个元素,插在指定元素的某些位置 |
复制
5.css实现居中的方法
| 使用position:absolute和transform 的 translate(-50%,-50%)实现 |
| 使用flex布局: diplay:flex,justify-content:center,align-item:center |
复制
6.css常见的选择器
| id选择器,class类选择器,标签选择器,*通配符选择器,伪类选择器,属性选择器,后代选择器,子选择器。 |
复制
7.css的优先级如何计算?
| important > id > class > 标签 > 子 > 后代 > 伪类 > * |
复制
8.长度单位px、em和rem的区别是什么?
| px: 像素单位,像素点的个数 |
| em: 父元素的font-size的px大小 = 1em |
| rem: 根元素的font-size的px大小 = 1rem |
复制
9.讲一下flex弹性盒布局?
| flex弹性布局是制造一个容器包裹子元素并施加一定要求的一种布局方式 |
复制
| |
| |
| flex-direction: row / column; |
| |
| flex-wrap: wrap / nowrap; |
| |
| justify-content: flex-satrt(左对齐) / flex-end / center / space-around(成员之间间隔相等) / space-between(两端对齐,成员间间隔相等); |
| |
| align-items: flex-start / flex-end / center / stretch(默认值,交叉轴方向上有拉伸); |
| |
| align-content: flex-start / flex-end / center / space-between / space-around; |
| |
| flex-flow: row / nowrap; |
| |
| |
| |
| order: 4 |
| |
| flex-grow: 1; |
| |
| flex-shrink: 1; |
复制
10.浮动塌陷问题解决方法是什么?
| 浮动塌陷:当父元素由子元素撑开时,子元素设置定位,父元素失去宽高产生浮动塌陷。 |
| 解决方法:浮动元素后面添加一个元素(为元素)并且增加clear:both属性 |
| 给父元素添加overflow:hidden属性 |
| 给父元素加高度 |
复制
11.position属性的值有哪些?各个值是什么含义?
| postion: fixed / absolute / relative / static; |
| |
| 根据浏览器窗口定位 |
| |
| 根据元素本身原来的位置定位 |
| |
| 根据上层定位元素的位置定位 |
| |
| 默认值,没有定位 |
复制
12.BFC、IFC是什么?
| BFC: 块级化容器,内部元素不会和外部产生影响 |
| IFC: 行内话容器 |
复制
二、javaScript
1.谈谈对原型链的理解。
点此 原型链问题详解
| 在javascript中,每个对象都有一个[[prototype]]属性,这个属性指向当前对象构造函数的原型。 |
| 该对象可以通过该属性(在chrome中表现为__proto__)访问到原型上的属性和方法。 |
| 而原型本身也可以拥有该属性,所以就能形成一个链式结构,一直查找到Object |
复制
2.js如何实现继承?
| 构造函数法:创建一个构造函数A,并在A的原型上添加方法。将构造函数B的prototype指向A创建的实例。此时B构造函数创建的实例就能继承A的方法 |
| Object.create(): 又名函数的委托,需要继承属性的对象A = Object.create(B) B是包含方法的对象 |
| Object.setPrototype(需要被继承的对象,继承者) |
复制
3.js有哪些数据类型?
| 基本数据类型:Number, String, null, undefined, Boolean, Symbol |
| 引用数据类型:Object, Array, Error, Date, regPex, Function, set, map |
| 基本数据类型存储在栈中,引用数据类型的地址存储在栈中,数据本身存在堆中 |
复制
4.js有哪些判断类型的方法?
| typeOf , isArray |
| Object.prototype.toString.call() |
| isinstanceof(判断是否属于某个构造函数) |
复制
5.如何判断一个变量是否数组?
复制
6.Null 和 undefined 的区别?
| Null: 这个值虽然定义了,但它并未指向任何内存中的对象。 转为数值0 typeof(Null) == Object (底层未修复的错误) |
| undefined: 未定义 / 表示变量声明过但并未赋过值。 转为数值NaN typeof(undefined) == undefined |
复制
7.call bind apply的区别?
| call 和 apply 都能改变this指向,并且返回执行结果。 |
| call 和 bind 传参方式是列举,apply 传递数组 |
| bind 改变this指向,并返回一个未执行的函数 |
复制
8.防抖节流的概念?实现防抖和节流。
| 防抖:在 n 时间以后触发函数,若时间未到再次被触发,则会重置执行函数的倒计时。(多用于搜索框优化) |
| 节流:在 n 时间内无论触发多少次函数,只会执行第一次触发。从而稀释出发频率。 (多用于按钮,以及页面滚动) |
复制
9.深拷贝、浅拷贝的区别?如何实现深拷贝和浅拷贝?
| 深拷贝:创建新的内存空间,并将基本、引用数据类型的值复制一份到新的内存空间 |
| 浅拷贝: 将引用数据类型的地址复制到拷贝的属性上,只是内存地址的应用 |
| |
| 实现浅拷贝:contact() , Object.assign() , 引用数据类型直接赋值。 |
| 实现深拷贝:JSON.parse(JSON.Stringify()) (不能拷贝函数,null,undefined)。 |
| 递归遍历对象数组拷贝所有的属性。 |
| 函数的深拷贝:将函数转换成字符串,截取函数内的内容,用new Function() 创建新的函数 |
复制
10.对比 一下var、const、let。
| var可以作用域内变量提升,const 和 let 只能先声明后使用。 |
| const 定义常量,后不可修改(引用类型内部可修改)。 let 定义变量,可修改。let 可以创建块作用域 |
复制
11.ES6 新特性有哪些?
| let ,const ,箭头函数 ,扩展运算符(...) ,set(数组去重) map数据结构 ,Symbol ,解构赋值 |
| 字符串拼接`${}` ,promise ,bind ,calss ,object.assign() ,Object.Key |
复制
12.箭头函数和普通函数区别是什么?
| 箭头函数this指向上层作用域,箭头函数没有prototype,箭头函数不能作为构造函数,箭头函数只有匿名函数,箭头函数没有argumnets。 |
复制
13.使用new创建对象的过程是什么样的?
| 创建一个空对象,并将函数内的this指向这个新的对象,同时将该对象的[[prototype]]指向构造函数的prototype。 |
复制
14.this指向系列问题。
| 普通函数:this一般指向函数的调用者。直接调用则指向windows |
| new调用,将this指向新创建的对象。 |
| call, apply, bind的this指向绑定对象 |
| 函数作为参数的时候指向windows,变量赋值的时候this指向被赋值的对象 |
复制
15.手写bind方法。
| function fn () { |
| console.log(arguments) |
| console.log(this.a) |
| } |
| |
| function Jbind () { |
| var that = this; |
| var target = [].shift.call(arguments) |
| var param = arguments |
| function a (){ |
| that.call(target,...param,...arguments) |
| } |
| return a |
| } |
| |
| Function.prototype.Jbind = Jbind |
| |
| var obj = { |
| a : 10 |
| } |
| |
| var fn2 = fn.Jbind( obj ,'111') |
| |
| fn2('333') |
复制
16.谈谈对闭包的理解?什么是闭包?闭包有哪些应用场景?闭包有什么缺点?如何避免闭包?
| 闭包:调用不在当前作用域的值,会形成闭包 |
| 应用场景: 创建私有作用域,延长变量生命周期。 |
| 缺点: 垃圾回收机制无法生效,造成内存泄漏。 |
| 如何解决闭包缺点: 手动给不使用的变量赋值Null |
复制
17.谈谈对js事件循环的理解?(Event loop)
| js的运行机制就是事件循环 |
| js先执行同步任务,后执行异步任务(将同步任务塞入主线程,异步任务压入执行栈(分为宏任务和微任务)) |
| 异步任务中先执行微任务再执行宏任务。 |
| 微任务:由js自身发起 例如 (promise.then) |
| 宏任务: 由浏览器发起 例如 (setTimeout) |
复制
18.谈谈对promise理解?
| promise: 对异步操作进行封装并返回结果的构造函数,可以避免地狱回调。 |
| 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果 |
| Promise需要用catch接受错误,否则会被吞掉 |
复制
19.手写 Promise(还未写完)。
| function Jpromise (fn) { |
| var that = this |
| function resolve () { |
| |
| } |
| function reject () { |
| |
| } |
| fn(resolve, reject) |
| } |
| Jpromise.prototype.then = function (callback) { |
| |
| } |
| |
| Jpromise.prototype.catch = function (callback) { |
| |
| } |
| |
| var j = new Jpromise((resolve ,reject)=>{ |
| if(true){ |
| console.log('aaa') |
| resolve('ccc') |
| } |
| if(false){ |
| reject() |
| } |
| }) |
| j.then((res)=>{ |
| console.log(res) |
| }) |
复制
20.Typescript中type和interface的区别是什么?
| 都是用来定义对象和函数 |
| type 可以定义基本类型、联合类型或元组类型, interface 会自动合并 |
复制
21.讲讲Typescript中的泛型?
| 不提前指定入参类型,函数执行的时候根据传入参数的类型来进行数据类型的限制。类似any,但是any不会触发类型保护 |
复制
22.Typescript如何实现一个函数的重载?
| Ts利用一个函数来实现的重载,但是需要多重定义(定义各种情况下的入参) |
复制
23.CommonJS和ESM区别?
| commonjs模块: 输出的是值的拷贝,运行时加载,只能整体加载,不能只加载单一的输出项 |
| ESM模块: 输出的是值的引用,编译时加载,只能使用 improt 引用 |
复制
24.柯里化是什么?有什么用?怎么实现?
| 柯里化就是将多入参函数装换成能分步传参函数的过程。 |
| 如实现一个正则验证手机号的函数,可分步先入参正则,返回入过参数的函数,再对不同的手机号做验证 |
| 实现:在分布的过程中做函数内部的缓存(利用闭包特性) |
复制
25.讲讲js垃圾回收机制。
| 防止内存泄漏,寻找未被使用的内存区域并且释放他们。 |
| 局部作用域和全局作用域,局部作用域在不产生闭包的情况下会在不使用的情况下释放内存,全局作用域下的会在关闭浏览器以后释放 |
| 回收有两种方式:标记清除,和计数引用 |
| 标记清除:在变量进入执行环境的时候标记,在离开的时候标记一下并清除 |
| 内存泄漏就是由计数引用次数判断错误导致的 |
复制
26.实现一个发布订阅。
| var Event = (function () { |
| var listen, |
| trigger, |
| cashFn = {}; |
| listen = function ( key, fn) { |
| (!cashFn[key]) && (cashFn[key] = []) |
| cashFn[key].push(fn) |
| } |
| trigger = function () { |
| var key = [].shift.apply(arguments) |
| cashFn[key] && cashFn[key].forEach((fn)=>{ |
| fn(...arguments) |
| }) |
| } |
| return { |
| listen, |
| trigger |
| } |
| })() |
| |
| Event.listen('call', function () { |
| console.log(arguments) |
| }) |
| Event.trigger('call', 'bbbbbb', '222') |
复制
27.如何实现数组拍平?
| var multDim = [[ 1,[ 2, 3]],[ 4, 5],[ 6, 7, { a : 8 }]] |
| |
| var oneDimA = (function () { |
| var one = [] |
| function oneDim (arr) { |
| arr.forEach((item) => { |
| if(Array.isArray(item)){ |
| oneDim(item) |
| }else{ |
| one.push(item) |
| } |
| } ) |
| return one |
| } |
| return { |
| oneDim |
| } |
| })() |
| |
| var a = oneDimA.oneDim(multDim) |
| console.log(a) |
| |
| |
| var str = multDim.toString() |
| var newArr = str.split(',') |
| console.log(newArr) |
| |
复制
28.如何实现数组去重?
| var arr = [1, 1, 2, 2, 3, 3, 4, 4, 5] |
| var newSetArr = [...(new Set(arr))] |
| console.log(newSetArr) |
| |
| |
复制
三、Vue
1.讲讲Vuex的使用方法。
| vuex是为vuejs开发的状态管理模式 |
| |
| 五种 mutation, action, Getters, state, modules |
| state: 单一状态数,全局可以通过this.$store 访问到state中数据 |
| |
| Getter: vuex中的计算属性,不会对state产生影响,依赖state中的值做操作,通过$store.getters 取用计算方法 |
| |
| mutation: 唯一状态更新方法,页面中通过$store.commit('模块名/xxx',{})来提交更新方法到mutation, mutation中不能很好的处理异步,只能写同步方法 |
| |
| action: 替代mutation执行异步操作,页面通过$store.dispatch('模块名/xxx',{})提交更新方法到 action, 后续由action操作mutation进行状态更新 |
| |
| moudles: 模块,每个模块拥有自己的state、mutations、actions、getters等,实现页面的低耦合 |
| |
| mapState,mapGetters 将state中的数据映射到computed中。(...mapState({count,count1:count})) |
| |
| mapMutation,mapAction 将方法映射到methods中。(...mapMutation({methods:'mutation中的方法'}))通过事件传参 |
复制
2.讲讲Vue双向绑定原理。
| vue2: 通过Object.defineProperty({被操作对象},'',{ |
| get () { |
| 在取用属性的时候触发 |
| }, |
| set (newVal) { |
| 在属性赋值的时候触发 |
| } |
| }) |
| defineProperty 的属性描述符(第三个参数) |
| 公共描述符(enumerable、configurable) |
| 数据描述符(value、writable) |
| 存取描述符(get、set) |
| 注意:数据描述符和存取描述符不能都用,公共描述可以都用 |
| |
| 缺点:数组原生方法不会触发set方法。vue拦截原生方法获取当前的数据变更情况 |
| |
| vue3: 用proxy构造实例,传入需要被代理的对象和劫持方法对象。实现对对象的代理,返回的实例就是被代理过的对象(数组)。 |
| Reflect 上挂载了语言本身的内部方法,含由Object上的方法,暂时有13个静态方法 |
| Reflect上的set,get 返回原本数据在get,set操作下的返回值。 |
| 代码如下 |
复制
| var Arr = [ 1, 2, 3] |
| |
| var handler = { |
| set ( target, key, value, reciever) { |
| console.log(value) |
| console.log(1111) |
| return Reflect.set(target, key, value) |
| }, |
| get (target, key, reciever) { |
| console.log(target, key, reciever) |
| console.log(2222) |
| return Reflect.get(target, key, reciever) |
| } |
| } |
| |
| var newArr = new Proxy( Arr, handler) |
| |
| newArr[0] = 3 |
| newArr[0] |
复制
3.mvvm和mvc区别是什么?
| mvvm: model(数据保存) view(用户界面) viewModel |
| |
| model 和 view层通过 viewModel层实现双向数据绑定,而不直接进行通信, |
| view通知 vm, vm层通知model, model层数据变更,model通知vm层,vm更改view层显示 |
| |
| mvc: model(数据保存) view(用户界面) controller(业务逻辑) |
| |
| 首先在用户界面,用户执行变更,通知controller层执行业务逻辑, |
| controller层执行业务完成通知view层状态变更。 |
复制
4.vue组件间通信有哪些
| 父子通信: |
| 父组件v-bind(属性绑定)将数据传递给子组件,子组件通过props接受,子组件通过$emit发送消息,父组件v-on(事件绑定) 接受数据 |
| v-model绑定属性传递,子组件通过props中的value接受,子组件通过$emit('input')发送数据到父组件 |
| .sync 与 v-model 类似 不过是通过$emit("updata:属性名")传递数据 |
| 其他 如vuex ,ref 等 |
| |
| 兄弟组件通信: |
| bus: 创建一个vue的新实例,选择export抛出 使用处 import 或者挂载在vue构造函数的原型上面,再通过增加监听函数,和通知函数,来控制两个组件的通信(bus.$on()监听,bus.$emit()通知) (发布与订阅者模式) |
| |
| 其他 vuex , 父组件通信 |
复制
5.computed和watch区别是什么?
| computed是计算属性,通常有值的依赖,并且计算会有缓存,当值未发生变化的时候不会发生变化。通常可用于多对一场景,计算多个值的关系合值。 |
| |
| watch 是监听的属性,没有缓存,值一旦发生变更就会执行通知。通常用于一对多的场景,当一个值发生变更通知到多处 |
复制
6.v-for和v-if同时使用有问题吗?
| 不能同时使用 |
| v-for比v-if的优先级更高,所以循环内的所有v-if都会执行判断,内存消耗过大 |
| 为了避免可在v-for的外层判断v-if,或者用计算属性计算好了以后再渲染循环 |
复制
7.讲讲前端路由原理。比较一下history和hash这两种路由。
| 前端路由: (单页面应用spa)在通过不请求后端的情况下,跳转页面实现前端页面的管理(改变url)。 |
| 区别: history模式与传统的页面跳转样式上类似,都是/隔开。hash模式是通过 |
| hash的 url变更触发 onhashchange事件,history模式url变更触发 popstate 事件。 |
| hash模式无内置方法,需要自行保存页面状态,history有内置方法,可以在栈中访问页面状态信息。 |
| history强制刷新浏览器的时候会将整个url拿去向后端请求页面,hash模式只会请求 |
| history变更页面通过pushState 和 replaceState方法 hash 通过 a标签的herf值变更 |
| |
| 相同点:都能在不向后端请求的情况下更改页面内容。 |
复制
8.讲讲Vue的虚拟DOM,原理,好处是什么?相对于手动操作DOM,性能更好吗?
| 虚拟dom本质上就是将页面的dom以js对象的形式存储起来的。 |
| 虚拟dom在页面上的dom需要发生变更的时候通过对象的对比(diff算法)找到发生改变的dom对象,对改变过的dom对象单独修改。 |
| 相对于手动操作dom只会多出js的对比性能消耗,可以忽略不计,大多数情况下比手动操作更加节省性能,手动操作会有不必要的dom操作和重复操作。 |
复制
9.说说Vue的keep-alive使用及原理。
| 能将不活跃的组件缓存到内存中,减少重复加载。(抽象组件) |
| 提供了三个属性 includes(白名单) excludes(黑名单) max(缓存上限) |
| |
| 原理: |
复制
10.Vue父子组件生命周期触发顺序是怎样的?
| 父组件beforecreated => 父组件created => 父组件beforeMounted => 子组件beforeCreate => 子组件created => 子组件的beforeMount => 子组件的mounted => 父组件的mounted |
| |
| 更新过程 父组件 beforeUpdate => 子组件 beforeUpdate => 子组件 updated => 父组件 updated |
| |
| 销毁阶段 父组件 beforeDestroy => 子组件 beforeDestroy => 子组件 destroyed => 父组件 destroyed |
复制
11.Vue.nextTick的实现?
| nextTick 会在event loop执行完成 ,在UI-render执行完成后执行nexTick传入的代码 |
复制
12.讲讲Vue diff算法。
| 按层次比较,当新dom当前层的节点与旧dom不同的时候,直接替换。 |
| 相同时候使用双端比较,当前层次新旧dom的两端 会有四个指针,进行头头,尾尾,头尾,尾头比较,如未找到,则用新列表的第一个节点对比遍历旧列表的所有节点,若能找到则移动到首位,否则创建新节点 |
复制
四、性能优化
1.前端页面性能如何优化?
| 资源压缩:精灵图,base64格式图片 |
| 使用懒加载 |
| 通过用户行为,对某些资源使用预加载的方式,来提高用户需要访问资源时的响应速递 |
复制
2.讲讲回流和重绘的区别,如何避免回流和重绘?
| 回流: 元素的大小位置发生变化,删除添加dom,浏览器窗口大小发生变化,或者元素产生动画影响其他元素,会导致页面元素的重新渲染 |
| 重绘:元素的background-color ,color ,visibility变化,只改变自身样式,不会影响到其他元素 |
| 如何避免:少使用table,多使用修改类名的方式改变样式,而不是直接修改样式。使用定位,使元素脱离文档流减少对其他元素的影响。统一处理样式的变动。 |
复制
四、计算机基础
1.进程与线程区别是什么?
| 1、进程是资源分配的最小单位,线程是程序执行的最小单位(资源调度的最小单位) |
| 2、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。 |
| 而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 |
| 3、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。 |
| 4、但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 |
复制
2.讲讲TCP三次握手、四次挥手,为什么要三次握手、四次挥手?
| TCP三次握手:为了确保客户端和服务端之间能正常的传递数据 |
| 1.客户端向服务端申请建立联系 |
| 2.服务端接收到客户端的消息,同意客户端传递数据 |
| 3.客户端收到消息,并回复服务端已接受到可以传递数据的通知。 |
| |
| 四次挥手:为了正常的断开数据 |
| 1.客户端向服务器发器断开申请 |
| 2.服务端接收到申请,并且告知已经接受到断开申请 |
| 3.等待数据传递接受,服务端向客户端告知准备断开 |
| 4.客户端接收到服务端的确认报文,等待2msl以后断开连接 |
复制
五、网络通信
1.说说从输入url到页面展示出来的整个过程。
| 1.用户在浏览器中输入url地址 |
| 2.查询浏览器中的url地址缓存,如果没有解析过,则新建dns域名查找 |
| 3.三次握手建立连接 |
| 4.执行http请求数据 |
| 5.http服务器返回静态资源给客户端 |
| 6.四次挥手断开连接 |
| 7.浏览器执行页面的绘制( |
| 解析html资源,构建DOM Tree, |
| 解析css资源,构建CSS Rule Tree |
| 解析完成后综合DOM Tree和CSS Tree会生成Render Tree,计算每个元素的位置 即回流 |
| 绘制页面 |
| ) |
| |
复制
2.什么是跨域?为什么会出现跨域?如何解决跨域问题?jsonp原理 是什么?
| 什么是跨域:当前页面请求的接口与当前页面的地址在协议,域名,端口上有任意一项的不同,就是跨域 |
| 为什么会出现跨域:浏览器为了保证安全,出现的同源协议策略 |
| 如何解决跨域问题: |
| 1.cors ,'Access-Control-Allow-Origin', '*' |
| 2.使用nginx反向代理(服务器之间是没有同源策略的) |
| 3.Jsonp: 利用script标签可以跨域请求的原理,将回调函数当成参数拼接在url上,缺点就是只能get请求 |
复制
3.http常见状态码有哪些?
| 1xx: 代表请求已被接受,需要继续处理。响应是临时响应,只包含状态行和某些可选响应头信息,并以空行结束。 |
| 2xx: 成功 |
| 3xx: 重定向 |
| 4xx: 这些状态代码表示请求可能出错,妨碍了服务器的处理。 |
| 5xx: 服务器在尝试处理请求的时候发生了内部错误,是服务器本身的错误 |
复制
4.http有哪些方法?
| get , post , put , delete |
复制
5.get和post的区别
| 1.get用于信息的获取,他是安全的(数据库安全),数据在url栏中不可见。post用于修改服务器上的资源,只有https才能加密安全 |
| 2.get请求可以被缓存,post请求不可以被缓存 |
| 3.get数据长度有限制,post长度无限制 |
| 4.get通过url传递参数,post放在request body中 |
复制
6.讲讲http缓存机制。
| 浏览器请求数据都是优先请求缓存中的数据,拿不到在去向服务器请求 |
| 强制缓存:在强缓存下,客户端获取缓存中的数据,若当前数据未失效,则直接取用当前缓存中的数据。若缓存失效,则继续向服务器发起请求,将请求到的数据更新入缓存。 |
| 对比缓存:在对比缓存下,客户端也会优先访问缓存中的数据,但是同时会继续向服务发送消息,对比当前数据是否发生 变更,若未变更直接取用缓存中的数据 |
| 若发生变更,则将新的数据存入缓存并取用 |
复制
7.cdn是什么?它的原理是什么?
8.浏览器有哪些缓存?
| cookie: 由服务端生成,保存在客户端,用来保存登录等。默认浏览器关闭后销毁 |
| session: 依赖于cookie,保存在服务器的内存中,用来验证客户状态 |
| webStorage 如下 |
| sessionStorage: 短期存在,会在浏览器关闭后销毁 |
| loaclStorage: 长期存在,除非手动清除 |
复制