2019年是互联网最难受的一年。可是同时也是最好的一年。可是市场不管怎么变,本身若是能时刻保持学习,即可以以不变应万变。下面整理一下最新鲜的面试题。你确定看上去就很熟悉。可是又以为没那么熟悉,下面就一块儿复盘一下。(大神略过)css
答案部分并非所有详细的回答,毕竟面试的时候不可能给你太长时间去细讲,主要是写一下核心的死里以及主要代码。若是要想仔细了解,查一下资料,每一个答案都有不少的详细答案。html
下面的题目是各类公众号,朋友面试,整理出来的问题,答案是本身整理的,不免有纰漏。望指正vue
在一个文档中,每一个元素都被表示为一个矩形的盒子,不一样的浏览器显示不一样的盒子模型,node
盒模型是有两种标准的,react
一个是标准模型webpack
标准和模型中,宽高就是你本身的内容宽高css3
标准盒模型中,总width = 你设置的width+2margin+2padding+2bordernginx
一个块的总宽度= width + margin(左右) + padding(左右) + border(左右)es6
一个是IE模型(怪异盒模型)。web
IE模型中盒模型的宽高是内容(content)+填充(padding)+边框(border)的总宽高。
在IE的盒子模型中,width表示content+padding+border这三部分的宽度。
一个块的总宽度= width + margin(左右)(即width已经包含了padding和border值)
两个盒子展现图
css如何设置两种模型
这里用到了CSS3 的属性 box-sizing
/* 标准模型 */
box-sizing:content-box;
/*IE模型*/
box-sizing:border-box;
复制代码
好比说两个盒子并列排布,宽各为50%,可是此时有个新需求,盒子加上边框,用标准的你就会发现问题,会掉下来。若是用怪异的。由于border是计算在width里面的,不会撑开,完美解决。
第一种:transform:translate(-50%,-50%)
.parent{
position:relative
}
.son{
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
}
复制代码
第二种:弹性盒模型
.parent{
display:flex;
justify-content:center;定义项目在主轴(水平方向)上居中对齐
align-items:center;定义项目在交叉轴(垂直方向)上居中对齐
}
.son{
里面随便写。宽高爱写不写。都是居中的
}
复制代码
三栏布局,顾名思义就是两边固定,中间自适应。三栏布局在实际的开发十分常见,好比淘宝网的首页,就是个典型的三栏布局:即左边商品导航和右边导航固定宽度,中间的主要内容随浏览器宽度自适应。
第一种: 浮动布局
.left{
float: left;
width: 300px;
height: 100px;
background: #631D9F;
}
.right{
float: right;
width: 300px;
height: 100px;
background: red;
}
如今两侧的样式写好了,那么咱们来写中间的样式,
.center{
margin-left: 300px;
margin-right: 300px;
background-color: #4990E2;
}
清除一下浮动
.main::after{
content:'';
display: block;
clear: both;
}
复制代码
第二种:Position布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>position</title>
<style type="text/css"> *{ margin: 0; padding: 0; } </head> <body> <article class="main"> <div class="left">左</div> <div class="center">中 <h2>绝对定位</h2> </div> <div class="right">右</div> </article> </body> </html> <script> .left{ position: absolute; left: 0; width: 300px; background-color: red; } .center{ position: absolute; left: 300px; right: 300px; background-color: blue; } .right{ position: absolute; right: 0; width: 300px; background-color: #3A2CAC; } </script> 复制代码
第三种: flex弹性盒布局
.main {
display: flex;
justify-content:center;
align-items:center;
}
.left{
width: 400px;
background-color: red;
}
.center{
flex:1
background-color: blue;
word-break: break-word;
}
.right{
background-color: red;
width: 400px;
}
复制代码
选择器 | 栗子 |
---|---|
ID | #id |
class | .class |
标签 | p |
属性 | [type='text'] |
伪类 | :hover |
伪元素 | ::first-line |
相邻选择器、子代选择器 | > + |
浮动产生的缘由:
当一个内层元素是浮动的时候,若是没有关闭浮动,其父元素也就不会再包含这个浮动的内层元素,由于此时浮动元素已经脱离了文档流。也就是为何外层不能被撑开了
1、:after的3行代码
原理:利用:after和:before在元素内插入两个元素块(其实就是在节点自己构造出两个存在于Render Tree的节点),从而达到清除浮动的效果。
.box:after{
clear: both;
content: '';
display: block;
}
复制代码
2、添加新的节点,使用clear:both方法
原理:在父节点中插入元素块,清除浮动
<div class="box">
<div class="d1">1</div>
<div class="d2">2</div>
<div class="clc"></div>
</div>
<style> .clc{ clear: both; } </style>
复制代码
3、在父节点上设置一个新类名,而后在类名css中设置
原理:这样也能够清除浮动,其原理是使用overflow属性来清除浮动,overflow有三个值:auto| hidden | visible,
咱们可使用 hidden和 auto来清除浮动,
但绝不能够使用 visible 清除浮动,使用这个值达不到效果
<div class="box over-flow">
<div class="d1">1</div>
<div class="d2">2</div>
</div>
<style> .over-flow{ overflow: auto; } </style>
复制代码
flex是css3的弹性盒模型。在解决一个布局的时候,很是方便,
flex弹性盒主要是要区分什么是偶写在父元素,何时写在子元素。
分不清,父子元素写这些属性,就会很难用。或各类不生效
这一块就是各类属性多用。都尝试一遍,就知道属性怎么使用了。
BFC 全称为 块格式化上下文 (Block Formatting Context) 。
????
没有深刻研究的话谁一看都一脸懵逼,啥意思。
MDN讲那么多定义,最后仍是不知道干吗的
仅仅知道它是一种功能,针对于 HTML文档 起做用。
既然这样,就直接上例子。少说定义,就知道干吗的了
造成 BFC 的条件
一、浮动元素,float 除 none 之外的值;
二、绝对定位元素,position(absolute,fixed);
三、display 为如下其中之一的值 inline-blocks,table-cells,table-captions;
四、overflow 除了 visible 之外的值(hidden,auto,scroll)。
bfc的特性(功能)
(1).使 BFC 内部浮动元素不会处处乱跑;
父元素包含子元素,子元素若是浮动了,就脱离了文档流, 那么父元素就会高度塌陷。没法包含子元素,这时候把父元素造成一个块级别格式化一下,就能够了。父元素 ovelflow:hidden 就是这个原理
(2).和浮动元素产生边界
一半状况下,两个元素,其中一个元素浮动。另外一个元素,想要跟浮动元素,产生边距,须要设置margin-left:浮动元素宽度+margin值 可是把非浮动元素块级格式化一下,就不用这么麻烦了
(3)上代码
/*若是没有块级格式化产生边距20px*/
<body>
<div class="e"></div>
<div class="m"></div>
</body>
<style> .e{ border:1px solid #0f0; width:100px; height:100px; float:left; } .m{ border:1px solid #f00; width:100px; height:100px; margin-left:120px; 这块要设置成120px才能造成 } </style>
复制代码
/*若是有块级格式化产生边距20px*/
<body>
<div class="e"></div>
<div class="m"></div>
</body>
<style> .e{ border:1px solid #0f0; width:100px; height:100px; margin-right:20px; 本身设置边距就能够 float:left; } .m{ border:1px solid #f00; width:100px; height:100px; overflow:hidden; 把本身变成块级格式化元素 } </style>
复制代码
absolute: 生成绝对定位的元素,相对于 static 定位之外的第一个父元素进行定位。
元素的位置经过 "left", "top", "right" 以及 "bottom" 属性进行规定。
fixed:生成绝对定位的元素,相对于浏览器窗口进行定位。
元素的位置经过 "left", "top", "right" 以及 "bottom" 属性进行规定。
relative:生成相对定位的元素,相对于其正常位置进行定位。
所以,"left:20" 会向元素的 LEFT 位置添加 20 像素。
static:默认值。没有定位,元素出如今正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。
手写题是每一个公司都会有考试。,范围也比较固定,若是能大概准备一下,八九不离十就这几道题,固然基础选择题就忽略了
防抖是将屡次执行变为最后一次执行,节流是将屡次执行变为在规定时间内只执行一次 咱们在操做的js的时候,有不少根据窗口尺寸,或者滚动加载事件来执行方法的。可是这种操做会及其频繁的占用浏览器性能
因此咱们设置一下,避免频繁调用方法,增长浏览器的负担
(1)防抖
防抖是在单位时间内。只要触发事件,调用方法的时间就会从新计算延迟。只有单位时间再也不操做。才会调用一次方法。
好比 持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件。
简单的防抖函数实现
function debounce(fn, delay) {
let timer
return function() {
let _this = this
let args = arguments
// 防抖事件,只要定时器存在。还一直频繁操做事件,我就一直给你清空,直到你中止
if (timer !== null) {
clearTimeout(timer)
timer = setTimeout(()=> {
fn.apply(context, args)
},delay)
}
}
}
function handle() {
console.log(90909201902190)
}
window.addEventListener('scroll', debounce(handle, 1000))
复制代码
应用场景: 两个条件:
1,若是客户连续的操做会致使频繁的事件回调(可能引发页面卡顿).
2,客户只关心"最后一次"操做(也能够理解为中止连续操做后)所返回的结果.
例如:
输入搜索联想,用户在不断输入值时,用防抖来节约请求资源。
按钮点击:收藏,点赞,心标等
复制代码
(2) 节流
当用户操做持续出发事件的时候,保证必定时间内,只执行一次方法
// 定时器版本的节流
function throttle(fn, delay) {
let timer
return function () {
let args = arguments
let context = this
// 若是频繁操做事件,定时器还在,什么都不执行,若是定时器不在了,设置一个定时器。
if(!timer) {
timer = setTimeout(() => {
fn.apply(context,args)
timer = null
},delay)
}
}
}
复制代码
(3) 总结
函数防抖:将几回操做合并为一此操做进行。原理是维护一个计时器,规定在delay时间后触发函数,可是在delay时间内再次触发的话,就会取消以前的计时器而从新设置。这样一来,只有最后一次操做能被触发。
函数节流:使得必定时间内只触发一次函数。原理是经过判断是否到达必定时间来触发函数。
区别: 函数节流无论事件触发有多频繁,都会保证在规定时间内必定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 好比在页面的无限加载场景下,咱们须要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操做时才去请求数据。这样的场景,就适合用节流技术来实现。
1、ES6 set去重
function unique (arr) {
return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]
复制代码
2、利用indexOf去重
function unique(arr) {
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array .indexOf(arr[i]) === -1) {
array .push(arr[i])
}
}
return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
复制代码
3、利用filter
function unique(arr) {
return arr.filter(function(item, index, arr) {
//indexOf老是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,所以被filter滤掉了。
return arr.indexOf(item, 0) === index;
});
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]
复制代码
1、手写实现call
/*强调一下,通常来讲,对象调用了那个方法,这个方法的this就是指向这个对象的*/
/* 其实call方法就是在方法(Function)的原型上添加了一个方法(call)。 这个添加的方法(call)。有个参数(随便都行)就是你要指向的那个对象(foo1)。 方法里面的this(bar)就是你要改变指向的方法。原理就是: 在这个call方法里面把要改变方向的方法(bar)复制一份给被指向的对象(foo1), 而后被指向的对象调用这个方法。这不就是把bar方法的this指向foo1对象了啊 */
// 手动实现一个call
Function.prototype.call2 = function(context = window) {
context.fn = this
// 打印一下this,就是 方法bar
// context是传入的对象,this是指须要改变指向的那个函数,在目标对象上复制一个须要改变指向的方法
let args = [...arguments].slice(1) // //解析参数,第一个参数不要。由于是指向的对象。从第二个参数开始才是须要的
let result = context.fn(...args)// //目标对象上新复制的函数,执行一下,记得把处理的参数放进去。
delete context.fn // // //执行完。把添加的方法删了。要否则无缘无故就会多出一个函数。上面已经改变
return result // 把结果返回啊
}
var foo1 = {
value:5
}
function bar(name,age) {
console.log(name)
console.log(age)
console.log(this.value)
}
bar.call2(foo1,'back',12)
复制代码
1、手写实现apply
/*apply和call的惟一区别就是传的参数不一样。call是一个个传进去,apply是传一个数组进去。因此两个的原理写法是差很少的。区别就是处理参数的时候不一样*/
Function.prototype.apply1 = function(context=window) {
context.fn = this
let result
// 若是第二个传参了,apply 要求第二个参数是数组。就把数组一个个拓展开。
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
const bar = {
m:1,
c:2,
}
function test() {
console.log(this.m)
}
test.apply1(bar)
复制代码
3、手写实现bind
bind和apply的区别在于,bind是返回一个绑定好的函数,apply是直接调用.
就是返回一个函数,里面执行了apply上述的操做而已.
不过有一个须要判断的点,由于返回新的函数,要考虑到使用new去调用,
而且new的优先级比较高,因此须要判断new的调用,
/* 1.绑定this指向 2.返回一个绑定后的函数(高阶函数原理) 3.若是绑定的函数被new执行 ,当前函数的this就是当前的实例 new出来的结果能够找到原有类的原型 */
Function.prototype.myBind = function (context, ...args) {
// 传件来的须要改变指向的函数
const fn = this
// 传的参数是单个仍是数组
args = args ? args : []
// 返回一个新的函数
return function newFn(...newFnArgs) {
// 传件来的函数是是当前返回函数的实例。而且new出来的也要能够传参
if (this instanceof newFn) {
return new fn(...args, ...newFnArgs)
}
return fn.apply(context, [...args,...newFnArgs])
}
}
复制代码
图片懒加载技术主要经过监听图片资源容器是否出如今视口区域内,来决定图片资源是否被加载。 那么实现图片懒加载技术的核心就是如何判断元素处于视口区域以内。
之前
之前的懒加载核心技术原理就是经过js提供的elemenr.getBoundingClientRect()
注意使用的时候 scroll事件记得使用节流,避免一直触发操做事件,影响性能
如今
Web为开发者提供了 IntersectionObserver 接口它 能够异步监听目标元素与其祖先或视窗的交叉状态, 注意这个接口是异步的,它不随着目标元素的滚动同步触发,因此它并不会影响页面的滚动性能。
var io = new IntersectionObserver(callback, options)
// 用来指定交叉比例,决定何时触发回调函数,是一个数组,默认是[0]。
// 咱们指定了交叉比例为0,0.5,1,当观察元素img0%、50%、100%时候就会触发回调函数
const options = {
root: null,
threshold: [0, 0.5, 1]
}
var io = new IntersectionObserver(callback, options)
io.observe(document.querySelector('img'))
复制代码
1、柯里化的概念
在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
举例来讲,一个接收3个参数的普通函数,在进行柯里化后, 柯里化版本的函数接收一个参数并返回接收下一个参数的函数, 该函数返回一个接收第三个参数的函数。 最后一个函数在接收第三个参数后, 将以前接收到的三个参数应用于原普通函数中,并返回最终结果。
// 举个例子
// 数学和计算科学中的柯里化:
//一个接收三个参数的普通函数
function sum(a,b,c) {
console.log(a+b+c)
}
//用于将普通函数转化为柯里化版本的工具函数
function curry(fn) {
//...内部实现省略,返回一个新函数
}
//获取一个柯里化后的函数
let _sum = curry(sum);
//返回一个接收第二个参数的函数
let A = _sum(1);
//返回一个接收第三个参数的函数
let B = A(2);
//接收到最后一个参数,将以前全部的参数应用到原函数中,并运行
B(3) // print : 6
复制代码
这里大概就是讲一下什么是函数柯里化的概念,下面大概讲一下柯里化的应用
2、柯里化的用途
柯里化本质上是下降通用性,提升适用性。来看一个例子:
咱们开发工做中会遇到不少的校验工做,封装一个函数,传入正则验证参数,传入待验证参数进行验证,
function checkByRegExp(regExp,string) {
return regExp.test(string);
}
checkByRegExp(/^1\d{10}$/, '18642838455'); // 校验电话号码
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com'); // 校验邮箱
复制代码
若是验证少没问题,若是不少的话,就不行了,很繁琐。向下面这样
checkByRegExp(/^1\d{10}$/, '18642838455'); // 校验电话号码
checkByRegExp(/^1\d{10}$/, '13109840560'); // 校验电话号码
checkByRegExp(/^1\d{10}$/, '13204061212'); // 校验电话号码
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com'); // 校验邮箱
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@qq.com'); // 校验邮箱
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@gmail.com'); //
复制代码
极度繁琐,第一个验证参数,很长,不少,很不优雅
咱们经常使用的工具库loadsh就有这个功能,若是使用柯里化之后
//进行柯里化
let _check = curry(checkByRegExp);
//生成工具函数,验证电话号码
let checkCellPhone = _check(/^1\d{10}$/);
//生成工具函数,验证邮箱
let checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);
checkCellPhone('18642838455'); // 校验电话号码
checkCellPhone('13109840560'); // 校验电话号码
checkCellPhone('13204061212'); // 校验电话号码
checkEmail('test@163.com'); // 校验邮箱
checkEmail('test@qq.com'); // 校验邮箱
checkEmail('test@gmail.com'); // 校验邮箱
复制代码
就会很简洁优雅,
其实就是能够理解为:参数复用
es6 不用说了。面试必问。必问,并且你回答一下还不行,还会针对你的问题进行深层次问,大都会给一个业务场景,让你说。可是只要基础说好了。业务按着功能讲一下思路就行了
var 声明的变量能够挂载在window上,const let不行
var存在变量提高,const let没有
let const 存在块级做用域,var不存在
同一做用域下let和const不能声明同名变量,而var能够
const 声明的变量必须有值,不能为空,let能够
let,const 存在暂时性死区
箭头函数已是老生长谈了,所谓的箭头函数,写法不少种,在这里不作详细解释了,
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
复制代码
var a = ()=>{
return 1;
}
function b(){
return 2;
}
console.log(a.prototype); // undefined
console.log(b.prototype); // {constructor: ƒ}
复制代码
5.总结一下,箭头函数,任何方法没有办法修改指向的
咱们知道JavaScript是单线程语言,若是没有异步编程非得卡死。 这仨个具体的详细用法最好百度查找相关资料。这里对其详细用法不作阐述。 由于每个的用法,一篇长文章都不必定能够解释完。。。。
异步编程之前通用的是回调函数来解决,少许回调还好,若是不少层的话,解读要疯掉,很不直观,
promise
因此promise能够解决回调的地狱,将原来的用 回调函数 的 异步编程 方法转成用relsove和reject触发事件, 用 then 和 catch 捕获成功或者失败的状态执行相应代码的异步编程的方法
Generator
Generator是ES6的实现,最大的特色就是能够交出函数的执行权, 普通函数的写法,可是不会返回执行结果,须要手动执行next来执行结果,这样若是不少的话,你手动也不太能分清楚谁先执行,谁后执行
async await
这个方案就很厉害了,是Generator的语法糖,整合了里面的功能,不须要手动执行阻塞通行,执行执行完方法,自动执行接下来的方法,并且只须要一行代码。
在 await 的部分等待返回, 返回后自动执行下一步。并且相较于Promise,async 的优越性就是把每次异步返回的结果从 then 中拿到最外层的方法中,不须要链式调用,只要用同步的写法就能够了。
继承的目的是方法或者属性公用,不要重复建立使用,
核心是ES5是经过原型或者构造函数进行继承的,ES6是经过class关键字进行继承的
ES5继承
es5继承有不少方式,最经常使用的就是原型继承
function Parent(name) {
this.name = name;
this.n = 2222;
this.color = ["red", "blue"];
}
Parent.prototype.sayName = function() {
console.log('父类定义的一堆逻辑')
}
function child(name) {
console.log('子类方法')
}
child.prototype = new Parent(); // 核心地方
var s1 = new child();
s1.sayName(); //也能够调用父函数的方法
console.log(s1.color); //也能够调用父函数的属性 ["red","color"]
console.log(s1.n);// 222
// 此时能够继承父函数的属性
复制代码
实质上就是将子类的原型设置为父类的实例。
ES6继承
ES6继承是经过class丶extends 关键字来实现继承 Parent是父类,child 是子类 经过 class 新建子类 extends继承父类的方式实现继承,方式比ES5简单的多。
class Parent {
constructor(name) {
this.name = name;
this.color = ["red", "blue"];
}
sayName() {
alert(this.name);
}
}
class child extends Parent {
constructor(name, age) {
// 至关于SuperType.call(this, name);
super(name);
this.age = age;
}
}
var s1 = new child('ren', 19);
console.log(s1);
复制代码
ES5 :子类的原型设置为父类的实例。(es5继承有太多种了,须要那个搜一下,不用所有记住)
ES6:先创造父类的实例对象 this,子类调用super方法,而后再用子类的构造函数修改this
1、CommonJS
CommonJS主要用在Node开发上,每一个文件就是一个模块 CommonJS经过require()引入模块依赖,require函数能够引入Node的内置模块、自定义模块和npm等第三方模块。
require函数在加载模块是同步的
2、AMD和CMD
区别于CommonJS,AMD规范的被依赖模块是异步加载的
AMD 推崇依赖前置,CMD 推崇依赖就近。
CMD集成了CommonJS和AMD的的特色,支持同步和异步加载模块。CMD加载完某个依赖模块后并不执行,只是下载而已,在全部依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块
咱们在开发的时候,会常常遇到304缓存信息,这就涉及到浏览器的缓存问题了
浏览器分为强缓存和协商缓存:
1)浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存,强缓存若是命中,浏览器直接从本身的缓存中读取资源,不会发请求到服务器。好比某个css文件,若是浏览器在加载它所在的网页时,这个css文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个css,连请求都不会发送到网页所在服务器;
2)当强缓存没有命中的时候,浏览器必定会发送一个请求到服务器,经过服务器端依据资源的另一些http header验证这个资源是否命中协商缓存,若是协商缓存命中,服务器会将这个请求返回,可是不会返回这个资源的数据,而是告诉客户端能够直接从缓存中加载这个资源,因而浏览器就又会从本身的缓存中去加载这个资源;
3)强缓存与协商缓存的共同点是:若是命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器。
4)当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据。
强缓存
强缓存是利用Expires或者Cache-Control这两个http response header实现的,它们都用来表示资源在客户端缓存的有效期。
这两个header能够只启用一个,也能够同时启用,当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires:
弱缓存
弱缓存【ETag、If-None-Match】两个字段实现的
浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个惟一标识
Response Headers
date: Tue, 02 Jul 2019 09:47:48 GMT
ETag: "17df-adasdascae242vsd"
复制代码
浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match的header,这个header的值就是上一次请求时返回的ETag的值
Response Headers
date: Tue, 02 Jul 2019 09:47:48 GMT
If-None-Match: "17df-adasdascae242vsd"
复制代码
服务器再次收到资源请求时,根据浏览器传过来If-None-Match和资源生成一个新的ETag,若是这两个值相同就说明资源没有变化,不然就是有变化;若是没有变化则返回304 Not Modified,可是不会返回资源内容;若是有变化,就正常返回资源内容
若是资源已经被浏览器缓存下来,在缓存失效以前,再次请求时,默认会先检查是否命中强缓存,若是强缓存命中则直接读取缓存,若是强缓存没有命中则发请求到服务器检查是否命中协商缓存,若是协商缓存命中,则告诉浏览器仍是能够从缓存读取,不然才从服务器返回最新的资源。这是默认的处理方式,这个方式可能被浏览器的行为改变:
1)当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;
2)当f5刷新网页时,跳过强缓存,可是会检查协商缓存
1、jsonp
jsopn是由于script标签不受同源策略限制。 可是只能get 还不安全,可能被拦截,发送病毒
2、cors
cors跨域后端, 只能携带一个请求头或者* , 若是用* 就不能携带cookie(浏览器保证安全)
3、proxy代理
vue或者react框架的代理转发,原理是利用node的express模拟服务器请求目标服务器数据,服务器不存在跨域问题。
4、nginx配置
后端护着运维配置,由于服务器请求服务器不存在跨域,因此nginx代理服务器请求数据
这个不是特别大的重点,并非全部的公司都会询问。可是通常只要问了,总要说点什么吧
目前市场上,主流就是vue和react框架。这两个都不会的话,就很难混下去,此次就主要是vue的问题,重点问的,无非就是响应式原理,虚拟dom,nextTick的原理,通讯。。。。
(1)beforeCreate中拿不到任何数据,它在实例初始化以后,数据观测 (data observer) 和 event/watcher 事件配置以前被调用。
(2)created中已经能够拿到data中的数据了,可是dom尚未挂载。会判断有无el,若是没有el则中止后面的模板挂载。 在实例建立完成后被当即调用。在这一步,实例已完成如下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
(3)beforeMount 和 created 拿到的数据相同 在挂载开始以前被调用:相关的 render 函数首次被调用。
(4)mounted中el被建立dom已经更新,vue实例对象中有template参数选项,则将其做为模板编译成render函数,编译优先级render函数选项 > template选项
使用场景:经常使用于获取VNode信息和操做,ajax请求
复制代码
(5)因为beforeUpdate更新以前,和updated更新以后
(6)beforeDestroyed 和 destroyed 销毁以前 销毁以后
父beforeCreate->
父created->
父beforeMount->
子beforeCreate->
子created->
子beforeMount->
子mounted->
父mounted。
父子组件 props
非父子组件 eventBus
vuex
路由传参
1.路由设置两个对象,一个普通普通路由(没有任何权限),一个动态路由,
2.路由守卫,监听全部的路由走向,根据接口返回的路由name,遍历动态路由的全部信息
3.一旦信息能匹配上,就使用addRouter动态添加异步路由。实现不一样菜单权限展现
webpack必然会问,可是不会太深究,但是最基本的仍是要掌握的,好比什么提升性能了,打包分割,loader做用,之类的
webpack的loader和plugin有上万个,所有记住而且会使用是不可能的事情,因此就说说本身经常使用的几个
css-loader:负责解析 CSS 代码,主要是为了处理 CSS 中的依赖,例如 @import 和 url() 等引用外部文件的声明
style-loader 会将 css-loader 解析的结果转变成 JS 代码,运行时动态插入 style 标签来让 CSS 代码生效。
vue-loader 处理后缀vue文件,使其能够正常解析使用
先下后上,先右后左
每次面试都会问,你是怎么进行代码分割的,如今终于能够说出来了。
其实代码分割跟webpack 并无实质联系。只是webpack 如今内置的插件能够帮咱们进行代码分割,全部就绑在一块了。
webpack的代码分割就是自带的属性optimization中的splitChunks各类属性配置
Code Splitting 的核心是把很大的文件,分离成更小的块,让浏览器进行并行加载。
假如打包一个2m的文件。用户加载就是2m的资源,若是是两个1m的,浏览器能够并行加载,速度就会很快
通常分割有三种:
手动进行分割:例如项目若是用到lodash,则把lodash单独打包成一个文件。
同步导入的代码:使用 Webpack 配置进行代码分割。
异步导入的代码:经过模块中的内联函数调用来分割代码。
复制代码
1.手动分割
module.exports = { entry: { main: './src/index.js', lodash: 'lodash' }
复制代码
本质就是多入口打包,
它输出了两个模块,也能在必定程度上进行代码分割,不过这种分割是十分脆弱的,若是两个模块共同引用了第三个模块,那么第三个模块会被同时打包进这两个入口文件中,而不是分离出来。(别用这个)
2.同步分割
有时候的代码是同步方法,不存在异步方法的话,使用这个,可是谁的代码里面没有异步的啊,这个用的也不是不少
module.exports = {
// 其它配置
optimization: {
splitChunks: {
chunks: 'initial'
}
}
}
复制代码
3.异步分割
若是咱们只须要针对异步代码进行代码分割的话,咱们只须要进行异步导入,Webpack会自动帮咱们进行代码分割,异步代码分割它的配置以下:
module.exports = {
// 其它配置
optimization: {
splitChunks: {
chunks: 'async'
}
}
}
复制代码
最后讲解一下splitChunks属性的用法
module.exports = {
// 其它配置项
optimization: {
splitChunks: {
chunks: 'async', // 异步仍是同步分割代码
minSize: 30000, // 若是模块大于30k就开始分割。不然就不分割
minChunks: 1, // 改模块必须引用一次以上才分割
maxAsyncRequests: 5, // 默认就好了
maxInitialRequests: 3,// 默认就好了
automaticNameDelimiter: '~',// 默认就好了
name: true,
cacheGroups: {
vendors: { //分组,若是模块知足在module包里面,就打包成vender.js形式
test: /[\\/]node_modules[\\/]/,
priority: -10// 值越大。越服从谁,好比一个loadsh的包,符合第一个组,也符合默认,就看priority的值,越大就打包到哪一个组
},
default: { //分组,若是模块不在module包里面,打包成default.js形式
minChunks: 2,
priority: -20,
reuseExistingChunk: true // 若是一个模块已经被打包了,在遇到的时候,就忽略掉,直接使用之前的包
}
}
}
}
};
复制代码
1.什么是sourcemap, 当你文件里面的代码出错了,若是不配置sourceMap,会把报错的信息体如今,压缩完的代码里,这样就很很差,找不到错在哪里了。 可是配置之后,会提示你错在哪一个文件夹的哪一行,方便快速找到错误,映射错误文件
2.在module.exports里面直接加上devtools:'sourceMap',能够开启这个功能,若是配置了sourcemap.打包的速度会变慢的。
3.使用sourcemap之后,你会发现,打包好的文件里面,有个.js.map的映射文件
4.官方文档 配置 里面, 有个选项 devtool.里面有很详细的使用方法,
(1)sourceMap.打包出一个xx.js.map的文件
(2)inline-source-map,会把map的文件取消,转换成一个base64的行字符串加在打包的js文件里面.
(3)inline-cheap-source-map,上面的两个会把哪一行,那一列错的位置告诉咱们,可是这个会把那一列去到,提升性能。
(4)cheap-module-eval-source-map,开发使用这个最好,全面,速度还快一点 开发环境
(5)cheap-module-source-map,生产使用这个比较好,mode:producton 生产环境
loader 用于加载某些资源文件。
由于 webpack 只能理解 JavaScript 和 JSON 文件,对于其余资源例如 css,图片,或者其余的语法集,好比 jsx, coffee,是没有办法加载的。 这就须要对应的loader将资源转化,加载进来。从字面意思也能看出,loader是用于加载的,它做用于一个个文件上。
plugin 用于扩展webpack的功能。
它直接做用于 webpack,扩展了它的功能。固然loader也是变相的扩展了 webpack ,可是它只专一于转化文件(transform)这一个领域。而plugin的功能更加的丰富,而不只局限于资源的加载。
开发中有这种问题,我在一个模块里面创建了是几个方法,在其余模块里面引入了其中一个方法,打包的时候咱们会发现模块里面的方法所有都在打包文件里面,这样没有必要,
tree shaking 翻译成汉语的意思是 摇树的意思,意思是把我不须要引入的模块去掉或者吧不须要模块里面的其余方法去掉
webpack里面写入就能使用
optimization: {
usedExports: true
},
复制代码
可是会有一些问题,
例如import './style.css' 时,会起到反作用,css整个不引入
这个时候在package.json里面设置,过滤掉全部的css。不被摇掉,polyfill也不被摇掉
"sideEffects": [ ".css", "@babel/polyfill" ],
复制代码