CSS
介绍一下盒模型?
①盒模型:内容(content)、填充(padding)、边界(margin)、 边框(border)
②类型: IE 盒子模型、标准 W3C 盒子模型;
③两种盒模型的主要区别是:标准盒模型的宽高是值内容宽高(content)
④而IE盒模型的宽高是指content+padding+border。
⑤设置盒模型的方式是:设置box-sizing, box-sizing:content-box 标准盒模型, box-sizing:border-box IE盒模型
圣杯布局
三栏布局,左右定宽,中间自适应,中间内容优先加载,以前都是通过负边距、左浮动、相对定位、内边距实现的,现在通过flex轻松搞定
请写出你所知道的元素水平垂直居中?
①绝对定位(盒子可以没有宽高)水平垂直居中
position: absolute;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
②③绝对定位(盒子必须设置宽高)水平垂直居中
width:400px;
height:200px;
top: 50%;
left: 50%;
margin: -100px 0 0 -200px;
或:
transform: translate(-50%, -50%);
④ flex 布局居中
width: 100px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
什么是BFC?
Block Fromatting Context 块级格式化上下文 是一块独立的渲染区域,规定了内部块级元素的布局规则
创建一个BFC
- float为 left|right
- overflow为 hidden|auto|scroll
- display为 table-cell|table-caption|inline-block|inline-flex|flex
- position为 absolute|fixed
- 根元素
同一BFC内的相邻块级元素/父子元素会发生margin重叠;
可以实现自适应两栏布局(BFC的区域不会与float box重叠)
父盒子没有设置高度且子元素全部float的话,会发生高度塌陷问题,(清除浮动)给父组件overflow:hidden解决 或者 after伪类清浮动
#fatherContainer::after {
content: "";
display: block;
clear: both;
}
介绍一下重绘Repaint和回流Reflow?
重绘改变颜色,重排改变大小和位置,重排一定会引发重绘
会触发重绘或回流/重排的操作
①添加、删除元素(回流+重绘)
②隐藏元素,display:none(回流+重绘),visibility:hidden(只重绘,不回流)
③移动元素,如改变top、left或移动元素到另外1个父元素中(重绘+回流)
④改变浏览器大小(回流+重绘)
⑤改变浏览器的字体大小(回流+重绘)
⑥改变元素的padding、border、margin(回流+重绘)
⑦改变浏览器的字体颜色(只重绘,不回流)
⑧改变元素的背景颜色(只重绘,不回流)
需要怎么优化?
①用transform 代替 top,left ,margin-top, margin-left... 这些位移属性
② opacity 加上 transform: translateZ/3d 这个属性之后便不会发生回流和重绘了
③不要使用 js 代码对dom 元素设置多条样式,选择用一个 className 代替之。
④如果确实需要用 js 对 dom 设置多条样式那么可以将这个dom 先隐藏,然后再对其设置
⑤不要使用table 布局,因为table 的每一个行甚至每一个单元格的样式更新都会导致整个table 重新布局
⑥对于频繁变化的元素应该为其加一个 transform 属性,对于视频使用video 标签
link @import导入css
- link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。
- link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
- link无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
- link支持使用Javascript控制DOM去改变样式;而@import不支持。
JS
js事件传播机制
捕获阶段 -> 目标阶段 -> 冒泡阶段
从外层向内一级一级查找的事件操作源,触发事件源绑定的行为或方法,然后自内而外地相关DOM元素依次触发
类型判断
Object.prototype.toString.call()
简单的数组求和?
eval([1,2,3].join("+"))或者arr.reduce();
reduce接受两个参数, 一个回调, 一个初始值initialValue .
回调函数接受四个参数 previousValue, currentValue, currentIndex, array,回调函数第一次执行时,如果提供了初始值,则previousValue等于initialValue,如果没有提供初始值,则前一个值就是数组的第一个值
简单的数组去重?
[...new Set([...])]或Array.from(new Set(array))(set返回的结构不是数组类型)
将下面的数组进行扁平展开
`let arr = [2,3,1,4,[3,[5,8]]]`,从而得到一个一维数组
var arr = [1,2,3,[5,6,[7,5]]];
const flatten = arr => arr.reduce((prev, next) => prev.concat(Array.isArray(next) ? flatten(next) : next), []);
console.log(flatten(arr))
计算数组中每个元素出现的次数
let names = ['peter', 'tom', 'mary', 'bob', 'tom','peter'];
let nameNum = names.reduce((pre,cur)=>{ if(cur in pre){ pre[cur]++ }else{ pre[cur] = 1 } return pre },{})
console.log(nameNum); //{ peter: 2, tom: 2, mary: 1, bob: 1 }
列出对数组产生副作用和没有副作用的方法
产生副作用的方法(改变了原来的数组):pop()后删、push()后加 、shift()前删、unshift()前加、sort()、reverse()、splice(i 索引值, n 个数): 删除从i开始之后的n个元素
没有副作用的方法(原来数组不变):concat()、join()、split()、slice(start,end):切去索引值start到索引值end的数组,不包含end索引的值,返回值是切出来的数组、toString()、toLocaleString()、valueOf()
深拷贝的方法有哪些?
深复制是在堆中重新开辟内存空间,将原来的对象进行复制,以保证新对象不再有任何属性指向原对象
数组解构是浅拷贝 [...arr]
JSON.parse(JSON.stringfy())
非数组对象转数组的方法
- **Array.prototype.slice.call(obj)**简洁写法
[].slice.call(obj): 可以将类数组对象转换为数组 - Array.from(obj): 可以将类数组对象和可迭代对象转换为数组
- [...obj]: 展开运算符可以将可迭代对象转换为数组
- Object.values(obj): 返回对象自身可枚举属性值的集合
Map和Set的区别?
Set 对象类似于数组,且成员的值都是唯一的。
Map 对象是键值对集合,和 JSON 对象类似,但是 key 不仅可以是字符串还可以是对象
let,var和const有什么区别?
const定义的变量不可以修改,而且必须初始化
var定义的变量可以修改,如果不初始化会输出undefined,不会报错。
let是块级作用域,函数内部使用let定义后,对函数外部无影响,不会发生变量提升,存在暂时性死区
怎么理解for循环中用let声明的迭代变量每次是新的变量?
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
相当于:
var a = [];
{ let k;
for (k = 0; k < 10; k++) {
let i = k; //注意这里,每次循环都会创建一个新的i变量,这一步是内部进行转换的
a[i] = function () {
console.log(i);
};
}
}
a[6](); // 6
闭包
Object属性
ES5
引入了属性描述符的概念:
数据描述符(其中属性为:enumerable,configurable,value,writable)
存取描述符(其中属性为enumerable,configurable,set(),get())
之间是有互斥关系的。在定义了set()和get()之后,描述符会认为存取操作已被 定义了,再定义value和writable会引起错误。
value——当试图获取属性时所返回的值。
writable——该属性是否可写。
enumerable——该属性在for in循环中是否会被枚举
configurable——该属性是否可被删除。
set()——该属性的更新操作所调用的函数。
get()——获取属性值时所调用的函数。
盘点ES7、ES8、ES9、ES10新特性
箭头函数和普通函数有什么区别?
①普通函数的this 指向调用它的那个对象,例如 obj.func ,那么func中的this就是obj
②箭头函数不能作为构造函数,不能使用new,没有this,arguments,super、new
.target,箭头函数的this永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()(或者说箭头函数中的this指向的是定义时的this,而不是执行时的this)
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
console.log(this.name); // My Object
return function(){
return this.name; // The Window
};
},
b: () => {
return this.name; // the window
},
c: function() {
return () => {
return this.name; //My Object
}
}}
怎么理解Event Loop
javascript是一门单线程语言,事件循环是js实现异步的一种方法,也是js的执行机制
js任务分为:
- 同步任务
- 异步任务:macro-task宏任务(setTimeout,setInterval),micro-task微任务(Promise,process.nextTick)
当执行一段代码的时候,同步任务直接进入主线程执行,异步任务进入Event Table注册回调函数,回调函数进入对应的Event Queue,当主线程内的任务执行完毕为空,会去Event Queue(分宏任务队列和微任务队列)读取对应的函数,先取出微任务队列里面的函数执行所有的微任务(PS: 此处有人理解script是宏任务,从代码开始执行就进入循环)
事件循环开始了:从宏任务队列里取出一个任务进入主线程执行,执行完毕后检查微任务队列,执行微任务队列中的所有任务后,开始下一轮循环......
MVC
所有通信都是单向的,用户对View的操作交给了Controller处理,在Controller中响应View的事件调用Model的接口对数据进行操作,一旦Model发生变化便通知相关视图进行更新
- View 传送指令到 Controller
- Controller 完成业务逻辑后,要求 Model 改变状态
- Model 将新的数据发送到 View,用户得到反馈
MVVM
View 与 Model 不发生联系,都通过ViewModel传递,ViewModel负责把Model的数据同步到view显出来,还负责把view修改同步到Model,各部分之间的通信,都是双向的
ViewModel做了两件事:数据的绑定(DataBindings)和Dom事件监听(DomListeners),从而实现双向数据绑定,View和Model之间的同步工作完全是自动的,开发者只需要关注业务逻辑,做到了视图和数据的解耦,利于前端业务组件化开发,提高了可复用性
Vue
生命周期
- beforeCreate 组件实例刚被创建、组件属性尚未绑定
- created 组件实例创建完成,属性已绑定,但DOM还未生成$el属性还不存在
- beforeMount 组件挂载前
- mounted 组件挂载后
- beforeUpdate 组件更新前
- updated 组件更新后
- activated keep-alive 组件被激活
- deactivated keep-alive 组件被移除
- beforeDestory 组件销毁前
- destoryed 组件销毁后
组件通信
父传子用props,父用子用ref 子调父用$emit 无关系用eventBus
Vue.set(object, key, value)方法做了什么?
Vue就是利用Object.defineProperty劫持对象的访问器,在属性值发生变化时可以获取变化,从而进行进一步操作。当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
但是Vue不能检测以下变动的数组:
- 通过length修改数组的长度时,例如: vm.items.length = newLength
- 对于初始化不存在的索引属性进行赋值时,例如:通过数组下标添加元素时:vm.items[indexOfItem] = newValue
这是由于Object.defineProperty不能检测到通过改变length而增加的数组元素,而且初始化时已定义的数组索引被删除后再赋值也不能被检测到(这对于对象的属性也是一样的)
数组的length属性,被初始化为:
enumberable: false
configurable: false
writable: true
configurable为false,意味着试图去删除和修改(并非赋值)length属性都会报错,无法重写length属性的set()方法
而且数组几个内置的方法在操作数组时,都会改变length的值,如:push,pop,unshift,shift他们在操作数组时就可能会出现Object.defineProperty不能检测的情况 🌰举个栗子 所以Vue为了监测数组的变化重写了数组的原型方法
Vue.set(object, key, value)方法是为了解决在对象创建之后添加新的属性到对象上,不会触发视图更新的问题
vuex中的成员?对应的作用?
state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步
modules => 模块化Vuex
简述Vuex和Redux是干什么的?有什么异同?
vue和redux都是一种状态管理机制。 他们都有自己的state、和修改state的方法, 修改state的方法涉及到同步和异步,vuex的处理方式是同步通过mutation,异步通过action;redux的同步通过reducer ,异步更多的是用户自己去通过中间件的方式去实现的。
Vue首页白屏是什么问题引起的?如何优化?
解释一下为什么Redux的reducer里不能有异步操作?
- 先从Redux的设计层面来解释为什么Reducer必须是纯函数
如果你经常用React+Redux开发,那么就应该了解Redux的设计初衷。Redux的设计参考了Flux的模式,作者希望以此来实现时间旅行,保存应用的历史状态,实现应用状态的可预测。所以整个Redux都是函数式编程的范式,要求reducer是纯函数也是自然而然的事情,使用纯函数才能保证相同的输入得到相同的输入,保证状态的可预测。所以Redux有三大原则:
- 单一数据源,也就是state
- state 是只读,Redux并没有暴露出直接修改state的接口,必须通过action来触发修改
- 使用纯函数来修改state,reducer必须是纯函数
- 下面在从代码层面来解释为什么reducer必须是纯函数
那么reducer到底干了件什么事,在Redux的源码中只用了一行来表示:
currentState = currentReducer(currentState, action)
这一行简单粗暴的在代码层面解释了为什么currentReducer必须是纯函数。reducer是用来计算state的,所以它的返回值必须是state,也就是我们整个应用的状态,而不能是promise之类的。要在reducer中加入异步的操作,如果你只是单纯想执行异步操作,不会等待异步的返回,那么在reducer中执行的意义是什么。如果想把异步操作的结果反应在state中,首先整个应用的状态将变的不可预测,违背Redux的设计原则,其次,此时的currentState将会是promise之类而不是我们想要的应用状态,根本是行不通的。
[为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作
React
React 事件机制
基本理解: react自身实现了一套自己的事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,虽然和原生的是两码事,但也是基于浏览器的事件机制下完成的。
我们都知道react 的所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发。
浏览器
跨域是什么原因引起?怎么解决?
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
① jsonp函数
在HTML DOM中,script标签本身就可以访问其它域的资源,不受浏览器同源策略的限制,可以通过在页面动态创建script标签。
var script = document.createElement('script');
script.src = "http://aa.xx.com/js/*.js";
document.body.appendChild(script);
② iframe实现跨域
基于iframe实现的跨域要求两个域具有aa.xx.com,bb.xx.com这种特点,也就是两个页面必须属于同一个顶级基础域(例如都是xxx.com,或是xxx.com.cn),使用同一协议(例如都是 http)和同一端口(例如都是80),这样在两个页面中同时添加document.domain,就可以实现父页面调用子页面的函数
③ 跨域资源共享(CORS)
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
④ 使用HTML5的window.postMessage方法跨域
window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。
⑤nginx反向代理
HTTP常见状态码:
2开头 (请求成功)表示成功处理了请求的状态代码。
- 200:服务器已成功处理了请求
- 204:无内容服务器成功处理了请求但没有返回任何内容
3开头 (请求被重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向
- 301:(永久性重定向) 请求的网页已永久移动到新位置
- 302:(临时重定向)
- 304:资源未修改,走缓存,不返回
4开头 (请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理
- 400:请求发生错误
- 401:(未授权) 请求要求身份验证
- 403:服务器拒绝请求,没权限
- 404:(未找到) 服务器找不到请求的网页
- 405: (方法禁用) 请求方法错误
- 413: 请求实体过大
5开头(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错
- 500:服务器内部错误
- 502: 网关错误
- 504 :网关超时
HTTP状态码301和302的区别是什么?
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取
- 301 redirect: 301 代表永久性重定向(Permanently Redirect):搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的网址
- 302 redirect: 302 代表暂时性重定向(Temporarily Redirect ):索引擎会抓取新的内容也会保存旧的网址
osi模型
七层结构:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层 tcp udp属于传输层;http属于应用层
http2.0 http1
- HTTP2.0的基本单位为二进制帧
- HTTP2.0中帧具有优先级
- HTTP2.0的多路复用( 1次连接)
- HTTP2.0压缩消息头
- HTTP2.0服务端推送
- HTTP2.0只适用于HTTPS的场景
如何解决移动端Retina屏1px像素问题?
用户从输入url到页面渲染完成发生了什么过程
- DNS解析(DNS缓存:浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存),获取到域名对应的服务器的ip地址
- TCP建立连接(三次握手)
- 发送http请求(浏览器缓存: 强缓存、协商缓存),服务器响应请求,返回数据
- 断开TCP连接(四次挥手)
- 页面渲染(DOM树,CSS规则树,渲染树,浏览器开始布局渲染树并将其绘制到屏幕上,这个过程比较复杂,涉及到reflow(回流)和repain(重绘))
XSS: 跨站脚本攻击:
是Web攻击中最常见的攻击方法之一,它是通过对网页注入可执行代码且成功地被浏览器 执行,达到攻击的目的
CSRF (Cross Site Request Forgery)攻击:跨站请求伪造
- 用户C登录受信任站点 A,并在本地生成cookie;
- 在不登出站点A(清除站点A的cookie)的情况下,访问恶意站点B,攻击者就能够完成CSRF攻击。
和XSS攻击对比,CSRF 攻击并不需要将恶意代码注入用户当前页面的html文档中,而是跳转到新的页面,利用服务器的验证漏洞和用户之前的登录状态来模拟用户进行操作。
CSRF的防御:
- 增加csrf_token验证
- 设置Cookie的SameSite属性为Strict/Lax:当前网页的 URL 与请求目标一致,才会带上 Cookie
- 将cookie设置为HttpOnly: js脚本无法获取cookie
- 尽量使用POST,限制GET
- 通过Referer识别(在HTTP头中有一个Referer字段,它记录了该HTTP请求的来源地址)
扩展
webpack要知道的常识
HMR是什么
HMR即Hot Module Replacement是指当你对代码修改并保存后,webpack将会对代码进行重新打包,并将改动的模块发送到浏览器端,浏览器用新的模块替换掉旧的模块,去实现局部更新页面而非整体刷新页面,可以保存应用的状态,提高开发效率
借助webpack-dev-server热模块更新功能:内部实现主要使用了webpack、express、websocket。
-
使用
express启动本地服务,当浏览器访问资源时对此做响应。 -
服务端和客户端使用
websocket实现长连接 -
webpack监听源文件的变化,即当开发者保存文件时触发webpack的重新编译。- 每次编译都会生成
hash值、已改动模块的json文件、已改动模块代码的js文件 - 编译完成后通过
socket向客户端推送当前编译的hash戳
- 每次编译都会生成
-
客户端的
websocket监听到有文件改动推送过来的hash戳,会和上一次对比- 一致则走缓存
- 不一致则通过
ajax获取已改动模块和jsonp获取已改动模块代码向服务端获取最新资源
-
使用
内存文件系统去替换有修改的内容实现局部刷新
打包速度太慢怎么办?
设置超时
function newFetch(fetch, timeout) {
return Promise.race([fetch, new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('已超时')), timeout)
})])
}