face
html&css 部分
BFC和IFC
FC是页面中的一块渲染区域,并且有一套渲染规则,他决定了子元素如何定位,以及和其他元素的关系和相互作用。BFC和IFC都是常见的FC
1.BFC 块及格式上下文的 规则:
- 内部的盒子在垂直方向,一个个的放置。
- 盒子的垂直方向的距离由margin决定,属于同一个BFC的两个相邻box的上下margin会发生层叠。
- 每个元素的左边,与包含的盒子的左边相接触,即使存在浮动也是如此。
- BFC 的区域不会和float 重叠。
- BFC在页面上是一个隔离的独立容器,容器内的子元素不会影响到外面的元素,反之亦然。
- 计算BFC的高度时,浮动元素也参加计算;哪些元素会产生BFC:
- 根元素
- float 的属性部位none。
- position 属性为fixed 或 absolute。
- dispaly 的属性为inline-block table-cell table-caption flex
- overflow属性不为visible
- IFC行级格式上下文 规则 :
- 内部的盒子会在水平方向上, 一个个的放置
- IFC 的高度, 由里面最高盒子的高度决定
- 当一行不够放置的时候会自动切换到下一行
CSS清楚浮动
1.父元素定高 手动撑开;
2.浮动元素末尾添加空标签,设置clear:both
3.父元素设置overflow:hidden
4.父元素添加伪类after和zoom,常用的方法是双伪元素法清楚浮动防塌陷
.clearfix:before,
.clearfix:after{
content :''
display:block
clear :both
}
.clearfix{
zoom:1
}
盒模型
盒模型包括: content, padding, border, margin 盒模型分为: IE 盒模型(怪异盒模型 border-box) 和 w3c 盒模型(标准盒模型 content-box) border-box: 盒子宽度由 content, padding, border 组成 content-box: 盒子宽度由 content 组成 其余类似
css选择器优先级
!important > 内联样式(style) > (ID)选择器>类/属性/伪类>元素> 关系
CSS实现三列布局(左右固定,中间自适应)
- float : div左边 float:left 右边 float:right, 中间 div 设置 margin-left 和 margin-right
- position : div左边left:0 右边right:0 , 中间div 设置 margin-left 和 margin-right
- flex : 子元素div 水平居中: justify-content: center; 垂直居中align-items: center;
H5 自适应方案
rem 布局 vw vh 和 calc 函数
JS 部分
DOM 节点的创建和常用API
- 创建节点 :
- createElement
- createTextNode
- createDocumentFragment(临时节点)
- 修改节点
- appendChild(后面追加)
- insertBefore(前面插入)
- removeChild(删除)
- replaceChild(替换)
Call / bind / apply 的作用和区别
他们都可以改变作用域 :
- call和apply 可以直接执行该函数,而bind不会立即执行
- call /apply 类似,都可以改变指针和执行函数,区别在于传参,call 传入单个参数,apply 通过数组传参
谈一谈EventLoop (事件循环)
js是一门单线程语言,同一时间只能执行一个任务,即代码执行时同步并且阻塞的只能同步执行肯定有问题, 索引引入异步函数, EventLoop就是为了确保异步代码可以在同步代码执行后继续执行
- 队列(Queue): 队列是一个FIFO 的数据结构 特点是先进先出
- 栈(Stack):栈是一种LIFO的数据结构 ,特点是先进后出
- 调用栈(Call Stack ): 本质上也是个栈 ,里面是一个个待执行的函数
function loginfo(){
console.log('this is something……'
}
function do(){
console.log('start~')
loginfo()
}
以上代码在调用栈中的运行顺序如下:
1.do()
2.do(), console.log('start!')
3.do()
4.do(), logInofo()
5.do(), logInofo(), console.log('this is someting...')
6.do(), logInofo()
7.do()
Empty, 执行完毕
以上都是同步代码,代码在运行时,只用调用栈解释就可以了如果是一个request请求,或者定时器,该如何执行呢?因此引出 事件表格(Event table)和事件队列(Event Queue)
- Event table : 可以理解为一张 事件-> 回调函数 的对应表
- Event Queue : 可以理解为回调函数队列 也叫callbackQueue
event loop 流程图:略- 1 开始,任务进入 call stack
- 2 同步任务直接在栈中等待被执行,异步任务从 call stack 中移入 event table 注册
- 3当对应的事件触发(或延迟到指定时间),Event Table 会将事件回调函数移入 Event Queue 等待
- 4当 Call Stack 中没有任务,就从 Event Queue 中拿出一个任务放入 Call Stack
Event Loop 不停检查 Call Stack 中是否有任务(也叫栈帧)需要执行,如果没有,就检查 Event Queue,从中弹出一个任务,放入 Call Stack 中,如此往复循环
宏任务和微任务
- 宏任务:参与了事件队列的异步函数
- 微任务 : 没有参与事件队列的异步函数
- 宏任务和微任务的优先级 : 微任务优先执行
- 浏览器环境下常见的宏任务和微任务 :
- 宏任务 : script标签里的代码 , setTimeout setInterval I/O UI render
- 微任务 : process.nextTick,promise,Object.observe(废弃), MutationObserve
setTimeout(()=>{
console.log('1')
})
new Promise((reslove)=>{
console.log('2')
reslove()
}).then(()=>{
console.log('3')
})
console.log("4")
// log 2 4 3 1
/**
过程:
1. 遇到setTimeout, 将其回调函数注册后分发到宏任务队列等待执行
2.接下来遇到Promise, new Promise 立即执行, 遇到console.log('2')打印 2, then函数分发到微任务队列等待执行
3.接下来遇到console.log('4'), 理解执行, 打印 4
4.此时 Event Loop 会去检查微任务队列是否有微任务需要执行, 有, 立即执行, 打印 3
5.到此, 整体代码作为第一轮宏任务执行结束, 打印了 2 4 3
6.开始第二轮宏任务, 发现console.log('1'), 打印 1
7.全部执行完毕, 打印 2 4 3 1
node 环境更为复杂, 暂不做讨论....
*/
get 和 post 的区别
- 大小限制: 其实都没有大小限制,get 一般为2k-8k ,受限制主要是因为浏览器
- 安全方面: get 通过url 明文传输。post 会通过body 传输,本身都不安全
- 浏览器记录: get请求会有浏览器记录,post不会
- 浏览器后退: get 无害 ,post会再次提交
- 浏览器收藏 : get 可以收藏,post不可以
- 浏览器缓存 : get 可以缓存,post 不会
- 编码方式 : get 通过url 编码,post 支持多种编码
- TCP 数据包: get 产生一个数据包,post 产生两个数据包
- 使用方式(习惯):get 主要拉取数据 ,post主要提交保存数据
Jsonp
- 简单实现 服务端设置:
const express = require('express')
const router = require ('express').Router()
const app = express()
router.get('/getData',getDataHander)
app.listen(31001,()=>{
console.log('http://localhost:31001'
})
getDataHander((req,res)=>{
let callback = req.callback
let data={
content:{
name:'zs'
age :18
}
}
res.send(`callback(${Json.stringify(data)})`)
})
//前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script src="localhost:31001?callback=getData"></script>
<script>
function getData(data) {
// ...
}
</script>
</body>
</html>
- jsonp的缺点: 只支持get 请求 返回咩有状态码 安全性不好
防抖与节流
防抖和截流都是希望在同一时间内,不要重复处理请求,一般场景用在搜索和网页滚动事件中
- 区别:防抖主要是在规定的时间内只触发一次,如果再次调用,事件重新计算,只有任务触发的间隔超过指定间隔的时候,任务才会执行;节流主要是在固定的时间内只触发一次,比如每间隔1秒触发一次,截流函数不管事件触发有多频繁,都会保证在规定的时间内一定会执行一次真正的事件处理函数
- 简单实现 :
// 1. 防抖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script src="localhost:31001?callback=getData"></script>
<script>
function getData(data) {
// ...
}
</script>
</body>
</html>
//2 节流
//01) 时间戳版本
function throttle(func, delay) {
let prev = Date.now();
return function () {
const context = this;
const args = arguments;
const now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
};
}
// 02) 定时器版本
fucntion throttle(func, delay){
let timer = null;
return funtion(){
let context = this;
let args = arguments;
if(!timer){
timer = setTimeout(function(){
func.apply(context, args);
timer = null;
}, delay);
}
}
}
// 03) 时间戳定时器综合版本
function throttle(func, delay) {
let timer = null;
let startTime = Date.now();
return function () {
let curTime = Date.now();
let remaining = delay - (curTime - startTime);
const context = this;
const args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(func, remaining);
}
};
}
常用的设计模式
- 单例模式
- 工厂模式
- 观察者模式
- 适配器模式
遍历的方法
- 常规for 循环
- while 循环
- do while 循环
- for in
- forEach
- map
- jQuery each
js 执行上下文
function fn(){
console.log(a) //undefined
var a = 1;
}
fn()
// 为什么打印出来是undefined 呢?
/**
概念 : 当代码运行时,会产生一个对应的执行环境,在这个环境中,所有变量会被事先提出来(变量提升),有的直接赋值,有的为默认值undefind,代码从上往下开始执行,就叫做执行上下文。
在javascipt 中,运行环境有三种,分别是:
1.全局环境: 代码首先进入的环境(在浏览器环境中 指向window)
2.函数环境 : 函数被调用时执行的环境(函数被调用时创建的)
3.eval函数(不常用)
特点:
1.单线程,在主线程上运行
2.同步执行,从上往下顺序执行
3.全局上下文之后一个,浏览器关闭时弹出栈
4.函数的执行上下文没有数目限制
5.函数每被调用一次,都会产生一个新的执行上下文环境(同一个函数被多次调用,都会创建一个新的上下文)
执行上下文的三个阶段
1.创建阶段(1)生成变量对象(2)建立作用域链(3)确定this 指向
2.执行阶段(1)变量赋值(2)函数引用(3)执行其他代码
3.销毁阶段 执行完毕出栈 等待回收被销毁
**/
作用域链
- 回顾: js执行先会创建上下文环境,在创建阶段会建立作用域链
- 简单理解 : 从当前环境向父级一层层查找变量的过程称之为作用域链。
var name ='你好 前端'
function hello(){
console.log(name)
}
- 解释 : 当我们在执行hello()打印name 时 ,会现在hello 函数中寻找name ,找不到在向父级作用域中查找,直到最外层
原形链
概念 : 每个函数都有一个prototype 属性,每个实例对象都有一个proto属性 ,而这个属性指向函数的prototype. 当我们访问实例对象的属性或者方法时,会先从自身构造函数中查找,如果没有,就通过proto去原型中查找,这个查找的过程就是原型链(和作用域链有点像)
function Animal()/**定义动物-父类*/{
this.age =10;
this.say = function(){
return 'hello tom'
}
}
function Cat()/**定义动物-子类*/{
this.name = 'tom'
}
Cat.prototype = new Animal()//通过原型继承动物类
const cat = new Cat()//实例化一个cat 对象
cat._proto_ === Cat.prototype;//打印返回true
console.log(cat.age)// 打印age 回先查找cat 在查找Animal
闭包
概念: 函数中嵌套函数,闭包是一个函数,能够访问其他函数作用域中的变量叫做闭包
//正常访问
var lan = 'zh'
function hello(){
let name = 'jack'
}
console.log(name) // 明显访问不到,返回undefined
//闭包
function hello(){
let name ="jsom";
return function demo(){
console.log(name) // jsom
}
}
继承有哪些方法?
- 1 原型继承
function Person(){
this.name = '人类'
}
function Student(){}
Student.prototype = new Person()
let s = new Student()
console.log(s.name)//人类
- 2 构造继承
function Person(){
this.name = name ;
}
function Doctor(){
Person.call(this,'jones');
this.age = 18
}
let d = new Doctor()
console.log(d.name) // jones
console.log(d.age) //18
- 3 实例继承
function Person(name){
this.name = name || '屈原'
this.write = function(){
console.log('床前明月光,疑是地上霜')
}
}
function Writer(){
let instance = new Person()
instance.name = name || '李白'
return instance
}
let w = new Writer()
console.log(w.name) // '李白'
w.write() //床前明月光,疑是地上霜
console.log(w instanceof Person) // true
console.log(w instanceof Writer) //false
- 4 call /apply 继承(组合继承)
function Person(name){
this.name = name;
}
function Doctor(name){
Person.call(this);
this.name = name || 'Tom';
}
Doctor.prototype = new Person()
let d = new Doctor()
console.log(d.name) // Tom
console.log(d instanceof Person) // true
console.log(d instanceof Doctor) // true
- 5es6使用class extends继承
class Person {
name = ''
constructor(name){
this.name = name || '文';
}
}
class Doctor extends Person{
constructor (name){
super(name)
this.name = name || '姜'
}
}
let d = new Doctor('法拉利')
console.log(d.name) // 法拉利
console.log(d instanceof Person) //true
console.log(d instanceof Doctor) // true
深/浅拷贝
理解 : JS数据类型分别为:基本数据类型和引用数据类型,基本数据类型保存的是值,引用类型保存的是引用地址(this指针). 浅拷贝公用一个引用地址,深拷贝会创建一个新的内存地址
- 浅拷贝
- Object.assgin(目标对象,源对象),返回目标对象,key一样则被覆盖
const target = {a:1,b:2}
const source = {b:4,c:5}
const returntarget = Object.assgin(target,source);
console.log(target) //{a:1,b:4,c:5}
console.log(returntarget) // {a:1,b:4,c:5}
- 复制
function copy(source,target={}){
for(let key in source){
target[key] = source[key]
}
}
const source = {
a:1,
b:2
}
const target = {
b:4,
c:5
}
copy(source,target)
console.log(target) //{b: 2, c: 5, a: 1}
- 深拷贝
- JSON api
let source = {a:1,b:2,c:10}
let traget = JSON.parse(JSON.stringify(source));
console.log(traget)//{a:1,b:2,c:10}
let source1 = {a:1,b:2,c:10,d:undefined}
let traget1 = JSON.parse(JSON.stringify(source1));
console.log(traget1) //{a:1,b:2,c:10}
2 . 手动实现深拷贝
function _deepclone(obj){
if(!obj && typeof obj !=='object'){
throw Error('error arguments')
}
const targetObj = Array.isArray(obj) ? [] :{}
console.log(targetObj)
for(let key in obj){
if(obj.hasOwnProperty(key)){
if(obj[key] && typeof obj[key]==='object'){
targetObj[key] = _deepclone(obj[key])
}else {
targetObj[key] = obj[key]
}
}
}
return targetObj
}
console.log(_deepclone({})) // {}
console.log(_deepclone([])) //[]
2.1 手动实现深拷贝 理解版
function _deepcopy(target){
let result ;
//判断target 的数据类型
if(typeof target ==='object'){
if(Array.isArray(target)){
result = [];
for (let key in target){
result.push(_deepcopy(target[key]))
}
}else if(target ===null ){
result = null
}else if (target.constructor===RegExp ){
result = target
}else{
result ={}
for(let key in target){
result[key] = _deepcopy(target[key])
}
}
}else {
result = target
}
return result
}
let obj1 = {
a: {
c: /a/,
d: undefined,
b: null
},
b: function () {
console.log(this.a)
},
c: [
{
a: 'c',
b: /b/,
c: undefined
},
'a',
3
]
}
console.log(_deepcopy(obj1))
image.png
逻辑或(||) 逻辑与(&&)
// 逻辑或:有true返回true ,同为false 返回false ;
// 逻辑与 : 有false返回false ,同为true 返回true;
异步函数的发展史
- 1 回调函数callback
function callback(){}
window.addEventlistener('click',callback);
- 2Promise
function getData(){
return new Promise((reslove,reject)=>{
axios.get(url,{})
.then((data)=>{})
.catch(error=>{})
})
}
- 3Generator函数
function* doSomething() {
yield console.log(1);
yield console.log(2);
yield console.log(3);
return true;
}
let d = doSomething(); // 返回的其实是一个迭代器
d.next(); // 1
d.next(); // 2
d.next(); // 3
d.next(); // { value: true, done: true }
- 4Async / await (语法糖,原理是promise和generator函数)
function delay(val,timer){
return new Promise((res,rej)=>{
setTimeout(()=>{
res(val)
},timer)
})
}
async function dosometing(){
let val1 = await delay(1,2000)
console.log(val1) // 2秒打印 1
let val2 = await delay(2,1000)
console.log(val2) // 3秒 打印2
}
dosometing() //总共用了3秒
如何解决跨域?生产环境和开发环境都要写出来
- 使用脚手架自带的proxy 配置代理(dev环境)
- 配置谷歌浏览器启动项,开启跨域模式(dev环境)
- 使用浏览器跨域插件,cors(dev环境)
- 使用http-proxy-middleware插件,配置代理(dev环境,pro 环境)
- 找后端开发配置设置响应头(dev环境,prod环境)
- 配置nginx 代理转发(dev环境,prod环境常用!)
- 使用iframe+(document.dimain/location.hash/window.name)跨域(dev环境和prod 环境)
- postMessage跨域解决方案(dev环境和prod 环境)
- WebSocket协议跨域
- 注意 :
- postMessage 这个api 还可以实现两个毫无干系的网页之间的通信(是除localStorage的另外的网页通信方式)
- WebSocket 同上,也可以实现两个网页之间的通信
- 注意 :
Vue相关
mvvm 框架是什么?
- mvvm 是model-view-viewModel 简写,即视图-模型-视图
- 模型:后端传递的数据
- 视图 : 展示的页面
- 视图模型: mvvm 的核心,连接view和model 的桥梁。他有两个方向:一是将【模型】转化为【视图】,实现方式是数据绑定;二是将【视图】转化成【模型】,将看到页面转化成后端的数据,实现方式是DOM 事件监听。这两个方法我们称之为数据的双向绑定
- 总结 : 在mvvm 框架下model和view 是不能直接通信的,它们通过viewModel来通信。viewModel通常要实现一个observer观察者, 当数据发生变化, 观察者检测到数据变化, 并通知视图更新; 而当用户操作页面时, viewModel也能监听到视图的变化, 然后通知数据做变动
vue的优点是什么?
- 官方文档齐全,简单易学
- 轻量级框架
- 双向数据绑定
- 组件化 视图 数据和结构的分离
- 虚拟dom ,运行速度快
vue两大核心点
- 响应式编程
- 组件化
三大框架的优缺点(vue react angular)
- 1react 和 vue 都在使用虚拟dom
- 2react 和 vue 都提供了响应式编程和组件化
- 3react 和 vue ,vue 上手更容易(vue文档齐全)
- 4vue 的优化比 react好
- 5 react 语法更简单,拓展更强大,支持高阶组件
- 6vue 会在组件销毁的同时自动清除事件绑定,而react 不会 需要手动清除
- 7vue中的观察者(数据双向绑定的数据)如果过多,会导致页面卡顿,react 不会(可能vue只适合中小型项目的原因之一)
渐进式框架的理解
- vue 是一个渐进式框架,正式因为它的渐进式,没有强主张,在原有的系统基础上,可以使用vue 也可以使用jquery ;也可以使用整个全家桶框架。vue 只是一个轻量视图,他只做了自己该做的事情,没有做不该做的事情
单页面应用和多页面应用区别及优缺点?
- 单页面的优点:
- 1用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
- 2前后端分离
- 3 页面效果比较炫酷(比如切换页面内容时的专场特效)
- 单页面的缺点 :
- 1不利于seo
- 2导航不可用,如果一定要导航需要自行实现前进、后退。(由于单页面不能使用浏览器的前进后退功能,所以需要自己建立堆栈管理)
- 3 初次加载时耗时多
- 4页面复杂度提高很多
- 单页面适用于高要求的体验度,追求页面流畅的应用
- 多页面适用于 追求高度支持搜索引擎的应用
SPA首屏加载慢的解决办法!
1.组件懒加载 babel-plugin-syntax-dynamic-import
2.不要把js打包到一个文件 配置webpack output 使用chunkname hash等
3.公共资源可以使用 CDN配置vue理由, 使用 import 方式引入组件, 指定输出js文件名
4.首页使用 loading 图, 不留白屏
5.使用骨架屏
组件通信
- 爷子孙组件通信props & Vue.children 、Vue.attr 、 Vue.$listeners
- 非父子组件通信(兄弟组件)公共eventbus 、 on
- 跨组件通信vuex
react 相关
什么是react ?
- react 是一个简单的javascript UI库,用于共建高校快速的用户界面,它遵循组件设计模式,声明式编程范式和函数式编程概念,使前端应用程序更为高效,它使用虚拟dom来操作dom,它遵循从高阶组件到低阶组件的单向数据流
声明式编程?
- 声明式编程是一种编程范式,它关注的是你要做什么,而不是如何做,它表达的逻辑而不是显式的定义步骤
- 声明式编程 VS 命令式编程:
//声明式编程
const number = [1,2,3,4,5]
const dobulenumber = number.map(number=>number*2)
console.log(dobulenumber) //[2, 4, 6, 8, 10]
//命令式编程
const number1 = [1,2,3,4,5]
let dobulenumber2=[]
for(let i =0 ;len = number1.length,i<len;i++){
let targetnumber = number1[i]*2
dobulenumber2.push(targetnumber)
}
console.log(dobulenumber2) //[2, 4, 6, 8, 10]
函数式编程?
- 函数式编程式声明式编程的一部分。javascript 中函数式第一类公民,这意味着函数是数据,你可以像保存变量一样在应用程序中保存、检索和传递这些函数。函数式编程有些核心的概念如下:
- 不可变性:不可变性意味着不可以改变,在函数式编程中,你无法更改数据,也不能改,如果要改变或者改变数据,必须复制数据副本来更改
- 纯函数 : 纯函数式始终接收一个或多个参数并计算着参数并返回数据或函数的函数。它没有副作用,它接受参数,基于参数计算,返回一个新对象而不修改参数
- 数据转换 : 数据不可变,生成原始数据的转换副本,而不是直接更改数据 比如join()、filter() 、map()等函数
- 高阶函数: 高阶函数式函数作为参数或返回函数的函数,或者有时他们都在。比如map() 、filter()、 reduce() 等函数
- 递归:递归是一种函数在满足一定条件之前调用自身的技术,只要可能,最好使用递归替代循环
- 组合:在react中,我们将功能划分为小型可重用的纯函数,我们必须将所有这些可以重用的函数放在一起,最终使其成为产品,这称之为组合;
什么是虚拟DOM?
- 浏览器遵循HTML指令来构造文档对象模型(DOM)。当浏览器加载HTML并呈现用户界面时,HTML文档中的所有元素都变成DOM元素。
- 虚拟DOM只不过是真实DOM的javascript对象表示, 与更新真实DOM相比, 更新javascript对象比较容易
- react 将整个DOM副本保存为虚拟DOM, 每当发生更新时, 它会生成一个更新后的虚拟DOM, 和更新前的虚拟DOM, 再对比两个虚拟DOM, 找出变化的地方, 并将变化的地方更新到实际的DOM, 一旦真正DOM更新, UI也会重新更新
什么是JSX?
- JSX 是javascript的语法扩展,它就像一个javascript全部功能的模版语言,它生成react元素,简而言之,jsx结合了html和javascript
函数/无状态/展示组件&类/有状态组件
- 函数/无状态/展示组件 =>function 声明的组件
- 类/有状态组件 => class 声明的组件 可以使用setState
组件生命周期钩子
redux
Hooks
- 我们不能在函数组件中使用state,因为它们不是类组件,hooks 让我们在函数组件中可以使用state和其他功能
vuex或者redux 刷新页面,怎么保留状态管理中的数据?
- 使用 loaclStorage / sessionStorage
在浏览器输入url到页面渲染处理,中间发生了什么?
- 1DNS域名解析(如果输入的url是域名而非服务器ip)
- 1.1 在浏览器的dns缓存中搜索域名对应的ip地址
- 1.2 在操作系统的dns缓存中搜索
- 1.3 读取系统的hosts文件
- 1.4 向本地的dns服务器发起请求
- 1.5 向远程的dns服务器发起请求
- 2建立tcp连接
- 2.1 三次握手
- 3发送http请求
- 4服务器处理请求
- 4.1 如果是静态资源, 服务器直接返回对应的静态资源
- 4.2 如果是其他比如接口, 需要服务器处理好结果之后再返回
- 5返回响应结果
- 5.1 状态码:
- 1xx 请求正在处理
- 2xx 请求处理成功
- 3xx 重定向 需要进行附加操作完成请求
- 4xx 客户端错误码 服务器无法处理请求
- 5xx 服务器错误码 服务器处理错误
- 5.1 状态码:
- 6关闭tcp连接
- 6.1四次挥手
- 7浏览器解析dom
- 7.1 将html文件解析成dom树
- 7.2 将css文件解析成规则树
- 7.3 将dom树和规则树结合成为渲染树
- 8布局渲染
- 8.1 布局 (回流发生在这里)
- 8.2 绘制 (重绘)
前端性能优化
- 1减少http 请求
- 1.1 尽量合并压缩js文件,减少请求的次数(利用打包工具比如webpack)
- 1.2 尽量使用字体图标/svg/base64类型的图片,来替代传统的png图片
- 1.3使用精灵图
- 1.4图片的懒加载和预加载,懒加载减少页面一打开时的http请求;预加载,预加载大图,防止空白的情况
- 1.5 动画使用css动画,避免使用js动画
- 1.6第三方库的按需加载,只加载自己要用到功能
- 1.7 避免使用iframe,极消耗性能
-1.8 使用本地json,减少发送请求
- 2代码优化
- 2.1在js中尽量避免使用闭包和全局变量,防止内存泄漏
- 2.2 减少dom操作,主要是减少dom的回流和重绘对样式进行修改最好统一处理不要单个修改,最好使用修改className 的方式修改样式,不要使用js直接修改样式
- 2.3 减少代码的事件的复杂度和空间复杂度,不要写嵌套循环和死循环
- 2.4将css 放在body上方引入,js 放在body下方
- 2.5尽量动画元素设置为单独的图层,触发bfc,减少重绘和回流的大小
- 2.6 js高内聚低耦合
- 2.7css导入尽量少使用@import ,因为@import是同步操作 ,link 是异步操作
- 2.8使用window.requestAnimation替代传统的定时器动画
- 2.9尽量减少递归,避免死递归
- 2.10 多使用事件委托,减少循环给dom绑定事件处理函数
- 2.11减少flash 的使用
- 3存储
- 3.1使用浏览器缓存技术
- 3.2使用localStorage和sessionStorage
- 4其他
- 4.1节流函数和防抖函数
- 4.2webpack打包压缩代码
- 4.3UI库的按需加载
- 4.4路由文件配置组件懒加载
- 4.5使用cdn缓存公共js库
- 4.6使用骨架屏
- 4.7nginx 配置gizp加速
前端安全
- 1xss跨站脚本攻击
- 攻击者网web页面里插入恶意可执行脚本,用户浏览网页时会执行脚本,盗取用户信息,侵犯隐私等
- domxss前端js讲不可信的内容插在网页中,如使用innerHTML appenChild等方法时要注意
- 反射型xss非持久性xss攻击者会在页面中按钮或者链接中注入非法链接用户点了之后会将信息提交到攻击者的服务器上并保存
- 存储行xss非持久性xss想后台提交数据时,前后端都未对数据做过过滤,恶意数据会永久的保存在数据库中,当下次查询时,会造成xss,常见场景 留言板
- 防止xss的方法:
- i.过滤特殊字符
- ii.设置cookie httponly ,禁止js 访问设置该属性的cookie
- iii.在服务端设置Content-Security-Policy,或者在前端html中的meta 标签中设置
- 2CSRF跨站请求伪造
- 利用用户在A网站的权限/cookie等,在B网站拼接A网站的请求
- 同源策略是最早用于防止csrf攻击的一种方式,但是由于同源策略是针对浏览器的,设置启动项配置代理等方式可以绕过
- 所以使用以下的方式预防:
- i.在请求头中设置自定义属性验证
- ii.检查csrf token
- iii.cookie中加入hash随机数
- 3SQL注入
- sql注入是网站常用的一种注入方式,比如攻击者在登录时密码框输入恶意拼接的数据库sql语句,可能会导致数据库数据丢失,插入数据,篡改密码,盗取数据等等问题
- 防御方法:
- i.给数据库表名字段名加前缀,防止被猜到
- ii.隐藏接口表报错信息,避免被看到
- iii.过滤可以拼接的sql关键字符
- iv.对用户输入进行转义
- v.验证用户输入的类型,防止关键字
- 4crypto加密传输
- 常用数据按照一定的规则加密后传递给后端,后端解密,常用的AES256 md5 加密方式
- 5TSL/SSL网络传输协议
- 作用 :
- i.认证用户和服务器,确保数据发送到了正确的客户端和服务器上
- ii.加密传输数据,防止在传输过程中被劫持
- iii.维护数据的完整性,确保在传输过程中,数据不被改变
- 作用 :
webpack相关
- 配置文件如下
const path = require('path')
const HtmlWebPackPlugin = require('html-webpack-plugin') // html模板插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 打包时清除dist目录
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 抽离js中的css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // css压缩
const webpackPlugins = {
entry: {
main: path.resolve(__dirname, 'src', 'index.js')
},
output: {
publicPath: './',
path: path.resolve(__dirname, 'dist'),
filename: './static/js/[name].[hash:10].js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css/,
exclude: /node_modules/,
use: [
{ loader: 'style-loader' },
{ loader: MiniCssExtractPlugin.loader, options: { publicPath: './static/css' } },
{ loader: 'css-loader' },
{ loader: 'postcss-loader' }
]
},
{
test: /\.(png|svg|jpg|gif)$/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'static/images',
publicPath: '../images'
// esModule: false,
},
}
]
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'url-loader',
options: {
// 把较小的图片转换成base64的字符串内嵌在生成的js文件里
limit: 1024 * 1, //10000
// 路径和生产环境下的不同,要与修改后的publickPath相结合
// name: 'images/[name].[ext]?[hash:7]',
name: '[name].[ext]',
publicPath: '../images', //会在打包后图片路径拼上该设置
outputPath: '/static/images'//输出的图片会生成并放在当前设置的文件夹下
}
}
]
},
resolve: {
alias: {
"@": path.resolve(__dirname)
}
},
plugins: [
new CleanWebpackPlugin({
dry: false,
cleanOnceBeforeBuildPatterns: [
path.resolve(__dirname, 'dist'),
],
}),
new HtmlWebPackPlugin({
title: 'App',
minify: { // 压缩 html
removeComments: true, // 移除注释
collapseWhitespace: true, // 删除空白符和换行
minifyCSS: true, // 压缩内敛 css
},
template: path.resolve(__dirname, 'public', 'index.html'), //模板
filename: 'index.html',
inject: true, // true || body script位于body底部
favicon: path.resolve(__dirname, 'public', 'favicon.ico'),
chunks: ['main'] // 多入口 对应 entry里的
}),
new MiniCssExtractPlugin({ filename: './static/css/[name].[hash:10].css' }),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
// cssProcessorOptions: cssnanoOptions,
cssProcessorPluginOptions: {
preset: ['default', {
discardComments: {
removeAll: true,
},
normalizeUnicode: false
}]
},
canPrint: true
})
]
}
TCP 三次握手/四次挥手
- 握手
- 第一次握手:客户端A将标志位SYN置为1,随机产生一个值为seq=J(J的取值范围为=1234567)的数据包到服务器,客户端A进入SYN_SENT状态,等待服务端B确认
- 第二次握手:服务端B收到数据包后由标志位SYN=1知道客户端A请求建立连接,服务端B将标志位AYN和ACK都置为1,ack= J+1,随机产生一个值为seq=K,并将该数据包发送给客户端A以确认连接请求,服务端B进入AYN_RCVD状态。
- 第三次握手: 客户端A收到确认后,检查ack是否为了J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发动给服务端B,服务端B检查ack是否为K+1,ACK是否为1,如果正确则表示连接建立成功,客户端A和服务端B进入ESTABLISHED状态,完成三次握手,随后客户端A与服务器B之间可以开始传输数据了
- 挥手
- 第一次挥手: Client发送一个FIN,用来关闭Client到Server 的数据传送,Client到进入FIN_WAIT_1状态。
- 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与-SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
- 第三次挥手 :Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
- 第四次挥手:Client 收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成第四次挥手。
http的发展史
- http 1.0
- 无状态:服务器不跟踪不记录请求过的状态
- 无连接:浏览器每次请求都要建立tcp链接
- 无状态可以通过cookie和session来做身份认证和状态记录
- 无连接的缺陷:
- i.无法复用连接:每发送一次请求都要进行tcp连接,也就是三次握手四次挥手,网 络利用率极低
- ii.队头阻塞:http1.0规定前一个请求响应完成后下一个请求才能发送,如果前一个请求挂了,后续所有请求将阻塞
- http 1.1
- 长连接:新增Connection字段,可以设置keep-alive,保持连接不断开
- 管道化:不必等前一个请求响应,可以连续发起多个请求,响应的顺序还是按照请求的顺序响应
- 缓存处理:新增cache-control字段,浏览器请求资源时,会先看有没有缓存有的话则直接取缓存
- 断点传输: 当上传下载资源过大时,分割成多个部分分别上传下载;当因网络故障可以从之前的地方继续上传下载. 客户端在请求头中设置Range,服务器在响应头设置Content-Range
- 管道化的缺陷: 虽然可以同时发起多个请求,但是响应的顺序还是按照请求的顺序,依然无法解决队头阻塞的问题
- http 2.0
- 二进制分帧:将所有传输的信息分割为更小的帧,采用二进制格式编码
- 多路复用:同时发送请求和响应
- 头部压缩
- 服务器推送:服务器可以额外的向客户端推送资源,而无需客户端请求
https怎么加密传输, https中间人攻击
- 客户端使用https的url访问web服务器,要求与服务器建立ssl连接
- web服务器收到请求后,将网站的证书包含公钥发给客户端
- 客户端收到证书后,会检查证书的颁发机构和过期时间,没问题的话就会产生一个密钥
- 客户端利用公钥将密钥加密并传递给服务器,服务器通过私钥将密钥解密
- 服务器和客户端通过这个密钥进行加密传输
公众号后台开发简介
- access_token 读写, 调用wx各种API,
- 通过access_token 获取 ticket, 调用 JS-SDK, 开发微信web页面
- 获取 ticket 的crypto加密算法
node.js相关
- node自带模块只使用过fs, qs模块
- 使用express.js做后台开发
- 使用koa.js做后台开发
根据时间戳 转换成日期格式
function format(timer)/**new Date().getTime() 获取当天时间转换成时间戳*/ {
if (!timer || typeof timer != "number") return "";
const date = new Date(timer);
return `${date.getMonth() + 1}/${date.getDate()}`;
}
console.log(format(1595411812239))// 7/22
时间戳之差 实现js 倒计时
function NextTime(next, cb) {
var t = null ;
(function ft(){
var dif = (next.getTime() - (new Date()).getTime()) / 1000; //秒 new Date()).getTime() 获取当前时间时间戳
if(dif > 0){
t = setTimeout(ft, 1000);
if(cb)
cb(Math.floor(dif/60/60/24),Math.floor(dif/60/60%24), Math.floor(dif/ 60 % 60), Math.floor(dif % 60));
} else {
clearTimeout(t);
}
})();
return function(){
clearTimeout(t);
};
}
function lpad(num, n) {
var len = num.toString().length;
while(len < n) {
num = "0" + num;
len++;
}
return num;
}
var next = new Date('2020/07/27 12:00:00'); // 过期时间
new NextTime(next, function(day,hour, minute, second){
console.log(lpad(day, 1)+'天'+lpad(hour, 2) + ':' + lpad(minute, 2) + ':' + lpad(second, 2))
});
根据时间戳 转换成年月日时分秒
function formatDate(timer) {
const date = new Date(timer)
let year = date.getFullYear(),
month = date.getMonth() + 1,
day = date.getDate(),
hours = date.getHours(),
minutes = date.getMinutes(),
seconds = date.getSeconds()
return `${year}-${morethanTen(month)}-${morethanTen(day)} ${morethanTen(hours)}:${morethanTen(minutes)}:${morethanTen(seconds)}`
}
function morethanTen(params) {
return params < 10 ? '0' + params : params
}
console.log(formatDate(1545230489000))//2018-12-19 22:41:29
封装判断javascript类型的函数
function getType(value){
if(typeof value ===null ){
return value+''
}
if(typeof value==='object'){
let valClass = Object.prototype.toString.call(value),
type = type.split(' ')[1].split('');
type.pop();
return type.join('').toLowerCase()
}else{
return typeof value
}
}
let a1='string'
console.log(getType(a1)) // string
[about js 掘金]:https://juejin.im/post/5f1412ad6fb9a07e944eff6b?utm_source=gold_browser_extension#heading-22