前端知识点

332 阅读38分钟

一. 基础

1. html

块级标签: div dl dd dt h1-h6 p ul ol
行级标签: span a em sub sup br
行内块标签: img select option input textarea
html5
语义标签(更好的seo):section article header footer nav aside figure time progress
多媒体:audio video autoplay自动播放 loop循环 controls控制条 preload预加载 poster优先展示图片(video独有)
拖拽元素: 事件ondrag全程调用 ondragstart ondragleave ondragend
历史记录: history

2. css

2.1权重

内连样式 1000 > id 100 > class | 伪类 | 属性 10 > 标签 | 伪元素 1 > 子代 | 兄弟 | *

2.2不可继承属性

  1. display
  2. 文本(vertical-align text-shadow white-space)
  3. 盒子模型(width height margin border padding)
  4. 背景属性(background background-color background-image background-repeat background-position background-attachment)
  5. 定位属性 (float、clear、position、top、right、bottom、left)

2.3可继承属性

  1. 字体属性: font-size font-family font-weight font-style
  2. 文本属性:text-align text-indent line-height word-spacing color
  3. 元素可见性:visibility
  4. 光标属性: cursor

2.4隐藏元素方法

  1. display: none 不占位置 不响应事件
  2. visibility: hidden 占位置 不响应事件
  3. opacity: 0 占位置 响应事件
  4. z-index: -1 用其他元素遮挡
  5. position: absolute 移除可视区域
  6. transform: scale(0,0) 占位置 不响应事件

2.5盒模型

  1. 标准盒模型(box-sizing:content-box): content = width/height
  2. ie盒模型(box-sizing:border-box):content + padding + border = width/height

2.6使用translate和poaition:absolute移动元素区别

参考链接:整体原因 浏览器渲染流程
浏览器渲染流程:

  1. html-->dom tree
  2. css-->style tree
  3. dom + style-->render tree
  4. render tree layout
  5. 合成图层(利用gpu渲染layout)

硬件加速原理
每个渲染元素会被分配到一个图层中,每个图层加载到gpu形成如渲染纹理,使用合成线程进行渲染,不会触发repaint

3d和2d translate区别
3d在动画渲染前创建独立的复合图层,2d在运行期间创建;因此2d多两次repaint(创建+删除)

gpu加速缺点

  1. 大量使用gpu加速,会渲染大量纹理,导致内存问题
  2. 影响字体的抗锯齿效果,硬件加速停止,文本还在动画期导致

总结:

  1. translate使用gpu加速,性能更好;absolute使用cpu,导致repaint,性能差
  2. translate整个动画过程占据空间,absolute整个动画过程不占据空间
  3. translate 2d比3d多两次repaint
  4. 注意使用gpu加速的缺点
  5. 理解强制触发硬件加速技巧(例如rotate 360deg)
  6. 触发gpu加速的css属性:transform opacity filter

2.7布局

两栏布局 三栏布局 水平居中 水平垂直居中

2.8flex详解

参考链接

容器属性

  1. flex-direction: 主轴方向 row / column
  2. flex-wrap: 换行
  3. flex-flow: flex-direction + flex-wrap 简写
  4. justify-content: 主轴上项目对齐方式 flex-start/end center space-between/around
  5. align-items: 交叉轴对齐方式 flex-start/end center baseline(第一行文字基线对齐) stretch(默认,每一项上没设置高度,自动填满父元素)
  6. align-content: 多根主轴对齐方式

项目属性

  1. flex-grow: 每一项放大比例, 默认0
  2. flex-shrink: 每一项缩小比例,默认1,空间不足,项目缩小; 0 不收缩
    注意:flex-shrink > 1 当子元素宽度和大于父元素宽度时,flex-shrink 将会按照一定的比例进行收缩,即将子元素宽度之和与父元素宽度的差值按照子元素flex-shrink值来分配给各个子元素,指定各个子元素收缩多少,每个子元素原本宽度减去按比例分配的值,其剩余值为收缩完的实际宽度。
    flex-shrink < 1 只收缩溢出空间部分
  3. flex-basis: 分配多余空间之前,项目占据的主轴空间; 默认auto,项目本身大小 干掉width
  4. flex: 1 flex-grow/shrink/basis 1/1/0 自适应

2.9浮动

浮动脱离文档流,导致高度塌陷
与浮动元素同级的非浮动元素会跟随其后
父元素的高度无法被撑开,影响与父元素同级的元素

清除浮动带来的影响

  1. 给父级height
  2. 给最后一个浮动的元素添加空div,并且增加clear:both
  3. 给包含浮动的父级元素包含overflow:hidden
  4. 使用after伪元素:.clear::after{ content:''; display: block; clear:both;}

2.10 BFC

块级格式化上下文,css布局中生成的盒子区域

创建bfc

  1. body
  2. position:absolute/fixed
  3. float: none以外的值
  4. overflow:hidden/auto/scroll

作用

  1. 解决margin塌陷(将两个元素变成两个bfc, bfc独立渲染互不影响)
  2. 解决高度塌陷

2.11 css性能问题

3.js

参考链接:

3.1 js数据类型:

number string boolean null undefined symbol object array

null和undefined区别:null表示没有,undefined有对象但是没值

3.2 js判断数据类型方法:

  1. typeof: 判断出来number string boolean undefined function 无法判断null array object 均为 object
  2. instanceof:检测构造函数(右边)的 prototype 属性是否出现在某个实例对象(左边)的原型链上。因此只能判断复杂类型
  3. object.property.tostring.call():不能检测非原生构造函数的构造函数名。其余都可以
  4. constructor:不能判断null和undefined。不安全的。因为constructor指向可以改变。

3.3字符串方法:

由于字符串的不可变性,所有方法都不会改变原字符串

查找字符串

  1. indexof/lastIndexOf: 查找想要的字符串,返回索引值,否则返回-1
  2. search:查找字符串,返回索引值
  3. includes: 字符串中是否包含指定的内容,返回布尔值
  4. startswith:字符串是否指定的内容开头,返回布尔值
  5. endswith:字符串是否指定的内容结尾,返回布尔值

获取指定位置的字符

  1. charAt
  2. charCodeAt

字符串截取

  1. slice:左闭右开,可以传负值,从后开始,返回新字符串
  2. substring:左闭右开,不能为负数,返回新字符串
  3. substr:开始索引,截取的长度;返回新字符串
  4. split:返回数组;split(',')以逗号分隔
  5. trim:去掉前后空格

3.4数组方法

不会改变原数组

  1. slice:左闭右开,可以传负值,从后开始,返回新数组
  2. concat:合并数组,返回新数组
  3. join:数组转为字符串,返回转换后的字符串
  4. indexOf/lastIndexOf/includes
  5. find:找到第一个满足条件返回true的元素
  6. findIndex
  7. every/some: 全满足返回true/只要有一个满足返回true
  8. map:映射,对每一项进行加工,返回新数组
  9. filter:返回为true的项,组成新数组

改变原来数组

  1. push:尾部添加,返回新数组长度
  2. pop:尾部删除,返回被删除元素
  3. unshift:头部添加,返回新数组长度
  4. shift:头部删除,返回被删除元素
  5. splice:删除指定元素,返回被删除元素
  6. fill:填充数组,返回新数组
  7. reverse:反转数组
  8. sort:从小到大排列
  9. reduce

3.5 对象方法

  1. defineProperty(obj, property, detail): detail包含value configable writable enumerable
  2. assign:浅copy,enumerable为false不能copy
  3. create:使用指定对象原型创建一个新的对象
  4. keys:可枚举属性的数组,与for...in区别在于得到原型链上的属性
  5. freeze:浅冻结一个对象
  6. is:比较两个对象是否相等 Object.is(NaN,NaN)-->true, Object.is(+0,-0)-->false, ===结果相反

3.6 预编译

JS运行:语法分析、预编译、解释执行

  1. 页面产生便创建GO全局对象(Global Object)window对象
  2. 第一个脚本文件加载
  3. 语法分析
  4. 开始预编译,查找变量声明作为GO属性,值为undefined
  5. 查找函数声明,值为函数体(作为GO属性)

执行函数预编译

  1. 创建AO对象(active object)执行期上下文
  2. 查找形参和变量声明,值为undefined
  3. 实参值赋给形参
  4. 查找函数声明,值为函数体

函数声明 > 形参 > 变量声明

3.7 this apply call bind

this函数执行期的上下文对象,根据函数调用方式不同,this指向不同的对象

  1. 以构造函数的形式调用时,this指向实例对象
  2. 使用call和apply、bind调用时,this指向指定的对象
  3. 以方法的形式调用时,this指向调用方法的那个对象
  4. 函数的形式调用时,this指向window
  5. 事件绑定函数的调用时,this指向绑定事件的对象

apply、call、bind比较

call:修改this指向,调用函数。可以实现继承。参数:this指向,实参1,2...
apply:修改this指向,调用函数。参数:this指向,[实参数组]
bind: 修改this指向,不调用函数。参数:this指向,实参1,2...

3.8 原型和原型链

prototype为原型对象、__proto__对象原型

原型和原型链.png

3.9 通过new方法创建对象的过程

参考链接

  1. 首先创建一个新对象
  2. 将新对象的__proto__指向构造函数的原型
    它将新生成的对象的__proto__属性赋值为构造函数的prototype属性,使得通过构造函数创建的所有对象可以共享相同的原型。这意味着同一个构造函数创建的所有对象都继承自一个相同的对象,因此它们都是同一个类的对象。
  3. 改变this指向,指向空对象
  4. 对构造函数的返回值做判断,返回相应值
    返回值是对象类型则直接返回,基本类型则返回空对象

3.10 闭包

有权访问另一个函数作用域中变量的函数

闭包的作用

  1. 延伸变量的作用范围
  2. 私有化变量
  3. 模拟块级作用域
  4. 创建模块
function fn1() {
    let a = 10;
    function fn2() {
        console.log(a);
    }
    return fn2;
}
const res = fn1();
res();

缺点:fn1执行后,变量a不会立即销毁,因为fn2中还要继续调用变量a;导致变量一直在内存中,过多的闭包导致内存泄漏

3.11 拷贝

浅copy 指向同一个地址

  1. Object.assign(obj1, obj2)
  2. 展开运算符 ...
  3. concat
  4. slice

deepClone

function deepClone(newobj, obj) {
  for (let key in obj) {
    if (obj[key] instanceof Array) {
      newobj[key] = [];
      deepClone(newobj[key], obj[key])
    }else if (obj[key] instanceof Object) {
      newobj[key] = {};
      deepClone(newobj[key], obj[key])
    }else {
      newobj[key] = obj[key];
    }
  }
}

3.11 继承

参考链接

3.12 DOM

DOM操作

  1. 创建节点:createElement
  2. 插入节点:parentNode.appendChild parentNode.insertBefore
  3. 删除节点:parentNode.removeChild
  4. 复制节点:cloneNode

节点属性

  1. 获取节点属性:ElementNode.getAttribute(key)
  2. 设置节点属性:ElementNode.setAttribute(key, value)
  3. 删除节点属性:ElementNode.removeAttribute(value)

js动画

  1. offset元素尺寸
  2. scroll滚动
  3. client可视区大小
  4. scrollheight - scrolltop = clientheight

3.13 BOM

对象

  1. window
  2. Navigator:当前浏览器信息,识别不同浏览器
  3. Location:浏览器地址栏信息
  4. History:浏览器历史记录
  5. Screen:屏幕信息

常用方法

  1. history.length
  2. history.back()
  3. history.forward()
  4. history.go()
  5. location.href
  6. location.reload

3.14 let const

let:定义变量
const:定义常量(定义后,不可修改)

特点

  1. 不属于顶层对象
  2. 不允许重复声明
  3. 不存在变量提升
  4. 暂时性死区(TDZ,仅针对当前作用域有效)
  5. 支持块级作用域

3.15 箭头函数

特点

  1. 写法简单
  2. this,本身无this,内部this就是外部代码的this,因此也无法使用call修改this指向
  3. 函数声明和箭头函数都有参数默认值

剩余参数和arguments比较

  1. 剩余参数普通函数和箭头函数都可以使用,arguments只能普通函数使用
  2. 剩余参数包含没有对应形参的实参,arguments包含所有参数
  3. 剩余参数是真实数组,arguments是类数组对象
  4. 剩余参数更加灵活

3.16 set map

set

  1. 类似数组,允许存储任意数据类型,但是不能重复
  2. set常用方法:get size set delete has clear

map

  1. 用于存储映射关系
  2. 与之前用于存储映射关系的对象相比,map可以将对象作为key存储
  3. 数组、字符串、map、set可以使用for...of遍历,普通对象不能
    备注:是否可以使用for...of遍历,要看原型上是否存在Symbol.iterator方法,普通对象没有这个方法

3.17 Promise

异步编程方案

  1. 解决回掉地狱
  2. 管理异步操作更加容易

Promise状态

  1. 初始化 pending --> new Promise
  2. 成功 fullfilled --> resolve()
  3. 失败 rejected --> reject()

Promise实例方法

  1. then
  2. catch
  3. finally

Promise的静态方法

  1. Promise.resolve()
  2. Promise.reject();
  3. Promise.all([]) 并发处理多个异步任务,都成功resolve 一个成功 reject
  4. Promise.race() 并发处理多个异步任务 返回第一个处理的Promise,且状态与第一个状态一致
  5. Promise.allSettled() 多个异步,返回所有结果,适用于多个彼此不依赖的异步任务

手写实现Promise静态方法

3.18 事件循环补充

异步操作

  1. 事件监听
  2. 定时器 settimeout setinterval ajax
  3. Promise Generator async/await

异步任务

  1. 宏任务
  2. 微任务:process.nextTick Promise.then/catch/finally Mutationobserver()dom节点发生变化时调用

第一个宏任务是script

event loop

  1. 在宏任务队列中,按照入队顺序,找到第一个执行的宏任务,放入调用栈,执行
  2. 执行完该宏任务队列下所有同步任务后,即调用栈清空,该宏任务被推出宏任务队列,然后微任务队列按照入队顺序执行,直到清空
  3. 清空微任务队列,一个事件循环结束
  4. 在宏任务队列中,找到下一个宏任务,开始执行下一个event loop,直到宏任务队列清空

3.19 Proxy Reflect

Proxy可以对目标对象的读取,函数调用等操作进行拦截,操作代理对象

let target = {
    name: 'tom',
    age: 18
}

let handler = {
    get: function(target, key) {
        console.log('get' + key);
        return target[key];
    },
    set: function(target, key) {
        console.log('set', + key);
        return target[key];
    }
}
let proxy = new Proxy(target, handler);
console.log(proxy.name);
console.log(proxy.age);

proxy常用拦截方法

  1. get --> get(target, propkey, [recevier]) recevier操作所针对的对象
  2. set --> set(target, propkey, value, [recevier])
  3. has --> has(target, propkey) target是否存在propkey

Reflect

  1. 对某些返回方法的结果进行修改
  2. 使用函数的方式实现了object的命令式操作

Reflect方法

  1. get(target, name, recevier) 当target中存在name,this绑定recevier
  2. set(target, name, value, recevier)
  3. has(obj, name) 是否存在
  4. deleteProperty(obj, property) 删除obj的property属性
  5. getPropertyof(obj)
  6. defineProperty(target,name,attr)

4. ts(待)

5. node

node.JS 是 js在服务端的运行环境

5.1 常见模块

  1. buffer缓冲区,用于存储二进制数据,操作方法与数组类似
  2. fs文件系统模块,主要使用异步,文件较大时多次读取
  3. path路径模块
  4. http模块,创建web服务

5.2 模块化

参考链接

  1. commonJS
  2. AMD
  3. CMD
  4. ES6

5.3 require import区别

  1. require(AMD)--> module.exports/exports(commonJS)--> 运行时调用
  2. import(ES6)--> export(ES6)--> 编译时调用

module.exports/exports 输出值的copy export输出值的引用

export = {} module.exports = xxx exports.xxx = {}

5.4 module.exports/exports

module.exports 和 exports都是对外暴露成员的

exports本质是变量,它是module.exports的地址引用

module.exports和exports指向的是同一个对象,但是以module.exports的暴露结果为准,若使用exports = obj形式,意味着exports指向新对象,但是module.exports没有挂载时,会导出空对象

二. 框架

1. React(React16.8+)(待)

参考链接1 参考链接2

useState & useRef

useEffect & useCallback & useMemo

1.1 概念

  1. 函数式组件:调用函数
  2. 类式组件: 通过对象实例调用原型上的render

1.2 核心属性

  1. state
  2. props
  3. refs

1.3 事件处理

1.4 生命周期

1.5 路由传参

1.6 组件通信

1.7 Redux

1.8 React Hooks

  1. useState
  2. useEffect
  3. useMemo
  4. useContext

image.png

1.9 react-fiber

2. Vue(Vue2 + Vue3)

参考链接1 参考链接2

2.1 数据代理

通过一个对象对另一个对象数据的访问

Vue中的数据代理:Vue中通过vm实例对象,代理对data对象属性的操作

通过Vue的实例对象可以直接访问data中的数据,data中的数据存放在vm._data上,vm._data = data;通过object.defineProperty()给vm添加属性,并且指定getter和setter,通过getter和setter访问和修改data

2.2 数据监测

Vue把每一组key value都加工成了get和set写法,方便数据修改访问

监测对象原理:使用递归的方式监测对象的每一个属性,如果新赋值的value为对象,object.defineProperty()无法监测到,使用的是Obverse()。object.defineProperty()只能监测已经声明的属性

监测数组原理:Vue将被监听的数组的变更方法进行了包裹,数组更新将触发试图更新。只对改变原数组的方法生效,push、pop、shift、unshift、splice、sort、reverse

2.3 指令

  1. v-bind 单向数据绑定
  2. v-model 双向数据绑定
  3. v-if 条件渲染
  4. v-show 条件渲染

v-if 和 v-show区别

v-if 删除和创建元素控制隐藏 切换开销大 适合条件改变较少

v-show 通过display:none控制 初始开销大 适合频繁切换

  1. v-for条件渲染

key的作用:虚拟dom的标识,提高渲染效率

旧虚拟dom和新虚拟dom的key相同:

  1. 内容没变,直接复用真实dom
  2. 内容改变,新替换旧

2.4 computed methods watch

computed:计算属性。通过已经有的属性计算得到的

原理就是使用了object.defineProperty()的getter和setter

methods: 方法

computed和methods区别:

  1. computed是响应式,methods是非响应式
  2. computed像属性访问一样,methods以函数形式调用
  3. computed有缓存机制,效率高

watch:监听器,当需要数据变化时,异步或者开销大的操作时,使用watch。不要使用箭头函数,this指向不对。 immediate立即执行,deep深度监听

2.5 生命周期

  1. beforeCreate:无法通过vm访问到data中的数据,methods中的方法
  2. created:可以通过vm访问到data,methods,模版解析未完成
  3. beforeMount:页面是未编译的dom,dom操作无效
  4. Mounted:编译后的dom,开定时器,ajax,订阅消息,绑定事件
  5. beforeCreate:数据新,页面旧,未同步
  6. Created:数据新,页面新,同步
  7. beforeDestory:data可用,在此阶段关闭定时器,取消订阅等
  8. Destoryed

2.6 组件通信

  1. props:父--> data v-on绑定 子--> props接收
  2. $emit:利用$emit传值 父--> v-on绑定相应事件,通过e传值
  3. 事件总线: 在main.js中进行配置,Vue.prototype.$bus = this。 通过this.$bus.$emit()传递事件。在Mounted中通过this.$bus.$on接收数据。在beforeDestory中解绑数据this.$bus.$off()
  4. 消息订阅:利用插件pubsub等
  5. provide/inject 适用于祖孙通信
  6. ref:适用于父子间双向通信
  7. Vuex状态管理

2.7 Vuex

概念:

  1. state:统一定义公共数据
  2. mutations:修改数据
  3. getters: 计算属性
  4. actions:发起异步请求
  5. modules:拆分复杂业务

WechatIMG124.jpg

WechatIMG125.jpg

2.8 路由传参

  1. query参数:传递 :to = ?id = ${id} 接收:this.$route.query.id
  2. params参数:动态路由 path:'/:id' 接收:this.$route.params.id
  3. props参数:在router/index.js中,props(route) {return {id: route.query.id}} 接收:props:[id]

2.9 路由守卫

对路由权限进行控制 例如:about --> home

  1. about --> beforeRouteLeave
  2. beforeEach
  3. home --> beforeEnter
  4. home --> beforeRouteEnter
  5. afterEach

2.10 $nextTick

在下次dom更新循环结束之后延迟回掉,在修改数据后使用$nextTick,则可以在回掉中获得更新后的dom

2.11 Vue3对比Vue2变化

  1. 响应式原理不同,Vue2 defineProperty;Vue3 proxy reflect
  2. Vue2深度监听在初始化时旧一次递归监听就好了;Vue3是在获取值的时候才对其监听(按需)
  3. Vue2无法追踪到对象增加新值,删除属性;Vue3可以
  4. Vue2数组使用包装的API;Vue3可以直接使用数组方法

WechatIMG126.jpg

3. diff算法

WechatIMG122.jpg

WechatIMG123.jpg

三. 实习

1. Next.js

1.1 Next.js渲染模式

参考链接

概念

  1. MPA:多页面应用 首屏速度快/ 切换页面慢/ SEO友好
  2. SPA:单页面应用 与 MPA 相反

CSR:客户端渲染 React/Vue默认开发模式, 前后端分离

渲染流程:

  1. 浏览器请求页面
  2. 前端服务器返回html页面
  3. 浏览器请求js脚本
  4. 前端服务器返回js脚本
  5. 浏览器执行js脚本
  6. js脚本请求数据
  7. 后端服务器查询数据库
  8. 数据库返回数据直到渲染到页面

SSR:服务器端渲染(请求时) 输出完整的html,整个渲染过程都在服务器端进行

渲染流程:

  1. 浏览器请求页面
  2. 前端服务器向后端服务器请求数据
  3. 后端服务器查询数据库
  4. 数据库返回数据给前端服务器
  5. 在前端服务器中数据与html组合返回
  6. 渲染页面
  7. 浏览器请求js脚本
  8. 前端服务器返回js脚本
  9. 浏览器执行js脚本,绑定事件
  10. 请求数据,动态渲染页面

备注:这里的SSR是指,首页返回完整的HTML,浏览器通过hydrate操作,成为React/Vue应用, 后续请求不会向服务端请求html,是以类似SPA方式进行

同构直出

在服务端渲染中,有两种页面渲染的方式:

  1. 后端服务器获取数据并生成 HTML 返回给浏览器解析渲染页面
  2. 浏览器在交互过程中,请求新的数据并动态更新渲染页面

这两种渲染方式的不同点在于运行环境的不同。同一份代码可以在客户端和服务器端运行,两个环境的渲染结果应该保持一致。因此,我们需要实现客户端和服务器端的路由、页面模板和数据共享。

整体渲染流程

START.png dehydrate: 脱水,静态的html片段,无法交互,适合网络传输
hydrate: 注水,将dehydrate的html片段加入数据和交互,构建完整应用

SSG:静态生成 构建时生成html

渲染流程:

  1. 构建阶段:SSG将html和css结合,生成静态页面
  2. 预渲染:SSG在构建过程中自动执行预渲染。这意味着 SSG 会根据预定义的路由和数据源,在构建时生成静态页面的多个实例。例如,对于一个博客,每篇文章都可以在构建过程中生成一个独立的静态页面。
  3. 静态输出:构建完成,SSG将生成的静态页面输出到目标文件中,包含html,css,js, 静态资源等
  4. 部署:生成的静态页面可以直接部署到任何支持静态文件的Web服务器上。无需动态生成,可以缓存到CDN中
  5. 用户访问:首屏直接解析 html 生成 dom。接着和 SSR 一样通过 hydrate 将整个应用转变成为 React 或 Vue 应用,使用户在交互时与单页应用无异。

ISR:增量静态生成 将部分静态页面在构建时生成,并在用户访问时进行增量更新。

需要在getstaticprops中设置revalidate

渲染流程:

  1. 构建阶段:在构建过程中,使用SSG(静态站点生成器)生成静态页面,并将这些页面上传到 CDN。
  2. 实时内容使用 SSR 来动态生成这些内容,并在后续的请求中进行增量更新,从而保持页面的实时性。

渲染模式对比

SSR
优点:SEO友好,首屏时间短
缺点:占用服务器资源,代码复杂性增加

CSR
与SSR相反

SSG
优点:充分利用缓存性能高,SEO友好,方便部署(不用依赖node),安全性好(服务器不需要运行程序,因此服务器漏洞仅限于操作系统本身,维护也更加方便)
缺点:静态,不适合总改的数据

ISR
优点:SSR和SSG之间的平衡点
缺点:访问到之前被预渲染过,但已经过期且未更新的页面,会得到过期的缓存响应。用户刷新一次,才能看到新的数据。

1.2 getstaticprops中fallback不同值的区别

  1. false:只会在build的时候构建制定的页面,如果没有指定则返回404
  2. true:访问没有定义的页面时会议客户端渲染(csr)的方式进行渲染,同时,提供router.isFallBack来判断数据是否从getstaticprops返回,显示或者隐藏自己的页面,如果使用了next.js但是某一个页面不想采用服务端渲染,采取这样的方式是可行的
  3. block: 生成ssr再返回,这种方式可能会很慢,但是只生成一次,生成后next会把它添加到预渲染列表中

1.3 Next.js性能优化

最直观的就是通过performance与lighthouse来评判 参考链接

  1. 通过 ssh登陆远程服务器开启 nginx 的gzip。 gzip on
  2. npm run build命令 查看 First Load JS. 用较小的等效库替换较大的库,并删除不相关的库 或者改为自定义库
  3. 针对非首屏组件基于 dynamic 动态加载。import dynamic from 'next/dynamic' const Modal = dynamic(() => import('../components/mModal'));
  4. next/image 优化图片资源。next/image 可帮助我们对图片进行压缩(尺寸 or 质量),且支持图片懒加载
  5. next/link 预加载,将 prefetch 属性添加到其中并将其设置为 false, 当用户 hover 到 Link 标签时,对即将跳转的页面资源进行预加载,进一步防止页面卡顿
  6. 开启 SWC 编译
  7. 资源上 CDN,减少Waiting for server response的性能损耗

WechatIMG20.png

说明:Next.js图片优化 cls:累计位移布局 lcp:最大内容绘制

  1. 对于缩略图:NextJs采用配置型loader实现不同云端缩略图请求的能力
  2. 对于cls:只要在图片显示之前知道图片的宽高,或者人为定义显示区域的宽高,这样无论图片宽高是多少,都会被限制在这个范围内,从而无法影响布局,NextJS Image的做法是在外层套一个span元素,在使用组件时传入宽高,再将span元素的宽高设置上去,撑开容器即可。
  3. 分层次加载:视窗中的图片首先加载无可厚非,剩下的视窗外的,肯定是越接近视窗的先加载,但不排除距离较远元素优先级更高从而先加载的场景,这里浏览器提供了相关的api。intersectionObserver当被观察元素与祖先元素相交值到达指定阈值时触发回调。这样就可以在图片元素快到达视窗时设置其src,从而进行加载。指定以何种策略加载图片

eager:立即加载图像,不管它是否在可视视口(visible viewport)之外(默认值)。

lazy:延迟加载图像,直到它和视口接近到一个计算得到的距离,由浏览器定义

设置rel=preload将使浏览器对资源进行优先加载

  1. 对于保证滚动时动画尽可能的流畅 通过对图片进行decode和append分离,在decode未完成之前,只渲染外层容器。

2. 登陆鉴权 (待)

参考链接

2.1 session-cookie鉴权

  1. cookie:http协议是无状态的,服务器为了区分不同的客户端,通过cookie去实现
  2. session:会话,是无状态协议通信过程中,为了实现中断/继续操作,将用户和服务器之间的交互进行的一种抽象

认证步骤:

  1. 客户端:向服务器发送登陆信息用户名/密码等,请求校验
  2. 服务端:校验通过后,生成session,并根据session生成sessionId,并在响应头set-cookie中设置sessionId
  3. 客户端:解析响应头,保存在cookie中,下次请求时携带sessionID
  4. 服务端:接收到请求后,解析sessionId,判断请求是否合法

缺点:

  1. 依赖cookie
  2. 保存在cookie中不安全
  3. session存储在服务端,增大开销
  4. 移动端支持不友好

2.2 Token鉴权

token组成:uid + 时间戳 + 签名(token前几位哈希算法压缩成的16进制字符串)

认证步骤:

  1. 客户端:向服务器发送登陆信息用户名/密码等,请求校验
  2. 服务端:

2.3 JWT

2.4 单点登录

2.5 联合登录和信任登录

2.6 唯一登录

2.7 扫码登录

2.8 一键登录

四. 计算机网路

1. http、https

参考链接

HTTPS 的加密过程可以简单描述如下

  1. 客户端发起 HTTPS 请求,连接到服务器的安全端口(一般是 443 端口)。
  2. 服务器将自己的 SSL/TLS 证书发送给客户端。
  3. 客户端验证服务器的证书是否有效、是否由受信任的证书颁发机构签发,并检查证书中的域名与实际访问的域名是否匹配。
  4. 如果验证通过,客户端生成一个随机的对称加密密钥,并使用服务器的公钥进行加密,然后发送给服务器。
  5. 服务器使用自己的私钥解密客户端发送的加密密钥。
  6. 客户端和服务器都使用这个对称密钥进行加密和解密数据,确保通信过程中的数据保密性和完整性。

安全通信的四大原则:机密性、完整性、身份认证、不可否性

http明文传输,会存在窃听风险、篡改风险、冒充风险

https = http + ssl/tls 使得https不直接与tcp对话

https非对称加密:解决客户端到服务端传输的安全

  1. 客户端存储公钥
  2. 服务端存私钥

https数字证书:解决服务端到客户端传输公钥的信任问题(第三方验证真实性)

  1. 服务器向CA申请证书,证书中包含公钥主机名host等信息
  2. 服务器申请证书后,向客户端发送的就是含有公钥的证书

数字证书加密方法

  1. 证书信息,无加密
  2. 数字签名,是经过CA上的私钥加密过,只有客户端用一开始就存在于操作系统内部存储的公钥才可以解密。

数字签名加密

  1. 对证书的各种信息进行随机摘要,通过MD5算法生成摘要秘文
  2. 使用CA私钥对摘要进行加密,这样的话客户端使用CA公钥进行解密。对比摘要是否相同
  3. 防止整个调包,客户端还会对证书上的域名和请求域名是否一致。

备注:公钥,是被操作系统信任,内置在客户端的操作系统上的CA证书上的。所以无需传输,因此也不会被劫取。这意味着中间人是没有这个公钥的。

2. get post

参考链接

定义:http协议中两种发送请求对方法

GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

get:浏览器会把http header和data一起发送,返回200ok post:浏览器先发http header,服务器响应100 continue;然后发送data,服务器响应200,返回数据

  1. GET与POST都有自己的语义,不能随便混用。
  2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
  3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

3. 跨域

参考链接

简单请求和非简单请求

  1. 简单请求:get post head
  2. Head头信息:Accept Accept-Language Content-Language Last-Event-ID Content-Type:application/x-www-form-urlencoded text-plain
  3. 非简单请求:需要先发送option预检

正向代理:代理客户端 反向代理:代理服务端

同源策略(协议、域名、端口)

  1. JSONP: 利用script标签没有跨域限制,通过script的src属性。发送带有callback参数的GET请求。服务端将接口返回数据拼凑到callback函数中,返回给浏览器。浏览器解析执行,前端从callback中拿到返回到数据。JSONP只能发送GET请求。
  2. 跨域资源共享(CORS):普通跨域请求只需要服务端设置Access-Control-Allow-Origin;若要带有cookie跨域请求,前端增加withCredentials = true。这里的cookie是接口所在的cookie并非当前页的cookie。当前页的cookie使用需要Nginx和NodeJS中间件实现。
  3. Nginx代理:通过Nginx配置一个代理服务器域名与目标url相同,端口不同做跳板机。反向代理访问另一个接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域访问。domain设置proxy_cookie_domain
  4. NodeJS中间件代理跨域:大多数情况下使用http-proxy-middleware进行代理,实现数据转发。通过设置cookieDomainRewrite: { '*': ''},可以实现当前域中cookie写入。
  5. postMessage:解决跨域,多窗口通信,嵌套iframe通信

4. websocket

暂时忽略

5. 输入url浏览器加载流程

  1. 浏览器通过进程间通信将url传给网络进程
  2. 网络进程收到url后,先查找缓存,有缓存,返回;无缓存,发送网络请求
  3. 发送网络请求,获取IP,从host中获取IP,建立tcp链接;host中无IP,将网址提交给DNS进行域名到IP映射
  4. 利用IP地址和服务器简历tcp链接(3次握手)
  5. 建立链接后,浏览器构建数据包(请求头...),向服务器发送请求
  6. 服务器根据请求信息,构建响应数据,发回网络进程
  7. 网络进程收到响应数据进行解析,判断state code,3xx重定向 200ok
  8. 传输完成,tcp四次挥手断开链接,如果有keep-alive,不断开
  9. 解析数据,根据content-type判断data type
  10. 如果是text/html --> 渲染 renderTree = domtree + style tree + layout

6. 网络模型

  1. 应用层(HTTP DNS FTP SMTP)
  2. 传输层(TCP UDP)
  3. 网络层(IP)
  4. 数据链路层
  5. 物理层

TCP UDP

五. 其他

1. 前端性能优化措施/指标/工具等

参考链接

从前端体系考虑性能

指标:加载

  1. FP:白屏时间
  2. FCP:首屏加载时间
  3. LCP:最大内容绘制时间
  4. TTI:可交互时间
  5. TTFB:资源从请求到响应,有一个等待时间

交互:

  1. 页面响应时间要短
  2. 页面滚动距离
  3. 异步请求接口完成时间要短

刷新率:屏幕参数 帧率:图像参数

RAIL模型:

  1. response: < 50ms 处理事件输入
  2. Animation:> 60桢 1000/60 = 16.6ms
  3. Idle:最大限度增加空闲时间
  4. Load:加载时间越短越好

前端性能监控performance

测量和分析网页性能指标,用于前端性能监控

  1. performance.navigation 页面导航相关性能指标
  2. performance.timing 页面资源加载和处理的性能指标
  3. performance.memory 内存
  4. performance.mark/measure 插入标记点,测量特定代码块时间

2. 重排和重绘

重排:发生在布局阶段,计算元素在设备可视区的大小

触发重排:

  1. display:none
  2. dom元素的增删改查
  3. 修改样式和大小
  4. 移动元素

触发重绘:

  1. dom改动
  2. css改动

如何减少重排重绘

  1. 在dom tree的末端改变元素css
  2. 动画效果尽量应用到脱离文档流的元素上
  3. 多用visibility:hidden
  4. 一次性加载dom等

3. 缓存(http缓存)

参考链接

WechatIMG108.jpg

强制缓存与协商缓存共同使用,保证效率

强制缓存

基于cache-control实现强制缓存,单位为秒。 200读取缓存

cache-control属性

  1. max-age: 决定客户端资源被缓存多久
  2. s-maxage:决定代理服务器缓存时长
  3. no-cache:强制进行协商缓存
  4. no-store:禁止任何缓存策略
  5. public:浏览器或者代理服务器缓存
  6. private:浏览器缓存

协商缓存

基于last-modified的协商缓存方式,对比时间戳

  1. 服务端读取文件修改时间
  2. 服务端将修改时间给last-modified
  3. 服务端设置cache-control:no-cache
  4. 客户端读取到last-modified时,在下一次请求时会带上If-Modified-Since,且值为last-modified的值
  5. 服务端收到请求后,对比If-Modified-Since和last-modified,是否读取缓存,304协商缓存,200不读缓存

基于E-tag的协商缓存方式,对比文件指纹(哈希值)

  1. 服务端读取文件并计算文件指纹,并将指纹哈希值放到字段etag中
  2. 客户端第二次请求时,将etag值赋值给if-None-Match
  3. 服务端收到请求后进行if-None-Match对比,是否读取缓存,304协商缓存,200不读缓存

4. 防抖、节流

防抖:n秒后触发,在n秒内重复触发,重新计时【搜索联想,改变窗口尺寸】

节流:n秒内只运行一次,n秒内重复触发,只执行一次【滚动加载等】

function ajax(content) {
    console.log('模拟ajax请求'+ content)
}
function debounce(func, wait) {
    let timeout;
    return function(args) {
        let context = this;
        let _args = args;
        cleaeTimeout(timeout);
        timeout = setTimeout(function() {
            func.call(context, _args)
        }, wait);
    }
}

let inputb = document.getElementById('debounce') 

let debounceAjax = debounce(ajax, 500)

inputb.addEventListener('keyup', function (e) {
    debounceAjax(e.target.value)
})

function throttle(fun, delay) {
    let last, deferTimer
    return function (args) {
        let that = this
        let _args = arguments
        let now = +new Date()
        if (last && now < last + delay) {
            clearTimeout(deferTimer)
            deferTimer = setTimeout(function () {
                last = now
                fun.apply(that, _args)
            }, delay)
        }else {
            last = now
            fun.apply(that,_args)
        }
    }
}

let throttleAjax = throttle(ajax, 1000)

let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
    throttleAjax(e.target.value)
})

5. 懒加载、预加载

懒加载:可缓解服务器压力 首先将页面上的src属性设置为空。图片的真实路径设置在data-origin中,当页面滚动时监听scroll事件,在scroll的回调中,判断图片是否进入可视区,将进入可视区的src属性设置为data-origin的值

预加载:将资源提前加载到本地,用到的时候可以从缓存中取。

6. 垃圾回收机制

参考链接

浏览器根据数据存储方式分为栈垃圾和堆垃圾

  1. 栈垃圾回收: 当函数执行完后,js引擎通过向下移动指针,来销毁该函数保存在栈中的执行上下文;先进后出原则。
  2. 堆垃圾回收:堆垃圾的饮用被清除了,但是堆垃圾还存在。标记清除算法/引用记数算法

V8引擎也是基于标记清除算法进行优化,将内存分为新生代和老生代

  1. 新生代:存留时间短的对象--> scavenge算法
  2. 老生代:经历新生代垃圾回收后还存在的对象--> 标记清除+整理算法

scavenge算法:正在被使用的区域是使用区,未被使用的是空闲区

  1. 新加入的对象会被放在使用区,当使用区快写满时副垃圾收集器就要进行一次清理操作
  2. 进入垃圾回收阶段,新生代回收器会对使用区内的对象进行标记
  3. 标记完成后,需要对使用区内的活动对象进行copy到空闲区并进行排序
  4. 垃圾回收阶段,将使用区内非活动对象进行清理
  5. 将空闲区和使用区进行翻转

标记清除算法:老生代区域中对象占据内存较大,如果采用scavenge算法copy消耗较大

  1. 垃圾收集器运行时会给内存中所有变量进行标记
  2. 从各个根对象开始遍历,把还在引用的对象标记清除
  3. 清理所有带标记的变量,销毁并回收他们占据的空间

标记整理算法:标记清除后剩余对象内存位置是不变的,产生大量碎片化空间。

  1. 从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素标记为活动对象。
  2. 所有存活的对象都向一端移动
  3. 清理掉端边界以外的内存

增量标记算法
V8 是使用副垃圾回收器和主垃圾回收器处理垃圾回收的,不过由于 JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。我们把这种行为叫做全停顿。 为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成。

7. xss/csrf

参考链接

8. webpack

参考链接

查看参考链接即可

常见loader

  1. sass-loader
  2. less-loader
  3. i18n-loader
  4. cache-loader

常见plugin

  1. define-plugin
  2. ignore-plugin
  3. web-webpack-plugin
  4. clean-webpack-plugin

9. webpack与vite对比

参考链接

核心理念:bundle与否

  1. webpack:使用node的打包器从入口开始构建依赖图,将项目中所需要的模块组合成一个或者多个bundle文件,逐层识别依赖,构建知识图谱。
  2. vite:无需bundle,源文件之间的关系通过浏览器对es module的支持进行解析,将依赖中的模块区分为依赖和源码两部分。依赖:使用esbuild进行构建,esbuild使用go编写,因此,比使用node编写的打包器速度更快;源码:在浏览器请求时按需转换,并以原生的es module方式提供源码,让浏览器接管打包程序的部分工作。

首屏、懒加载性能

  1. webpack:使用bundle,使用了完整的模块依赖包,不存在首屏和懒加载性能问题
  2. vite:未使用bundle,出现以下问题;由于未对源文件进行bundle,存在大量的http请求;动态加载的文件需要对源文件进行转换操作:load、transform、parse;预构建、二次构建操作也会阻塞首屏请求;但是由于缓存,后续请求会加快

服务启动速递

  1. webpack:使用bundle,建立所有模块的依赖关系,速度较慢
  2. vite:无需bundle,应用中的模块区分为 依赖(node_modules) 和 源码(项目代码) 两类,进行预构建,速度会快很多;依赖:使用esbuild进行构建,esbuild使用go编写,因此,比使用node编写的打包器速度更快;源码:在浏览器请求时按需转换,并以原生的es module方式提供源码,让浏览器接管打包程序的部分工作。

热更新速度

  1. webpack:动态模块热重载HRM,对其余部分没影响,提升速度;在实际使用中,效果一般,随着项目体积变大,HRM效果也会下降
  2. vite:HRM是在原生的es module中执行,当编辑一个文件时,只需要精确地使已编辑的模块与其最近的HMR边界之间的链失效(大多数时候只需要模块本身),使HMR更新始终快速,与应用大小无关。并且,vite中源码会使用304进行协商缓存;依赖会进行强制缓存,提升速度

prod环境打包区别

  1. webpack:在生产环境的构建方面更加成熟,bundle整体形成完善的依赖关系,也有非常多的loader或者plugin可供选择;
  2. vite: Rollup。因为存在大量的http请求,最好的方式还是代码进行tree-shaking、懒加载、和 chunk 分隔等;

生态成熟度: webpack > vite

10. git

参考链接

常用命令

  1. git fetch featureName 获取远程仓库分支更新 --all 所有分支更新
  2. git pull origin 从远程仓库拉取代码到本地 = git fetch + merge
  3. git pull -rebase origin 拉取后使用rebase进行合并,减少冲突,使用merge多一次merge记录
  4. git branch 查看本地分支 git branch branchname 创建branchname本地分支
  5. git checkout 切换分支 -b 切换并且创建分支
  6. git add 将修改文件添加到暂存区
  7. git commit 将暂存区文件提交到本地仓库
  8. git push 将暂存区文件提交到远程仓库 -f 强制提交
  9. git tag 查看已打的标签

git merge 和 git rebase 区别

merge:保留两个分支的commit信息,且是ff模式,即commit信息按照时间顺序展示;多用于自己的dev分支合并进master分支
rebase:改变分支的起始位置,在dev 上 git rebase master,将dev的多次commit一起拉到要master最新提交的后面(时间最新),变成一条线,多用于整理自己的dev提交历史,然后把master最新代码合进来。
假设场景:从 dev 拉出分支 feature-a

  1. 那么当 dev 要合并 feature-a 的内容时,使用 git merge feature-a
  2. 反过来当 feature-a 要更新 dev 的内容时,使用 git rebase dev

代码回退

  1. git checkout -- filename 撤回工作区文件的修改
  2. git reset HEAD -- filename 撤回暂存区文件的修改
  3. git reset --hard commit-id 回退到其他commit版本 影响 工作、暂存、本地仓库
  4. git revert 代码回退

git reset和revert区别

  1. reset是根据来移动HEAD指针,在该次提交点后面的提交记录会丢失。
  2. revert会产生新的提交,来抵消选中的该次提交的修改内容,可以理解为“反做”,不会丢失中间的提交记录。
  3. 公共分支回退使用revert,自己分支回退按需使用

11. 前端存储

参考链接