一. 基础
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不可继承属性
- display
- 文本(vertical-align text-shadow white-space)
- 盒子模型(width height margin border padding)
- 背景属性(background background-color background-image background-repeat background-position background-attachment)
- 定位属性 (float、clear、position、top、right、bottom、left)
2.3可继承属性
- 字体属性: font-size font-family font-weight font-style
- 文本属性:text-align text-indent line-height word-spacing color
- 元素可见性:visibility
- 光标属性: cursor
2.4隐藏元素方法
- display: none 不占位置 不响应事件
- visibility: hidden 占位置 不响应事件
- opacity: 0 占位置 响应事件
- z-index: -1 用其他元素遮挡
- position: absolute 移除可视区域
- transform: scale(0,0) 占位置 不响应事件
2.5盒模型
- 标准盒模型(box-sizing:content-box): content = width/height
- ie盒模型(box-sizing:border-box):content + padding + border = width/height
2.6使用translate和poaition:absolute移动元素区别
- html-->dom tree
- css-->style tree
- dom + style-->render tree
- render tree layout
- 合成图层(利用gpu渲染layout)
硬件加速原理:
每个渲染元素会被分配到一个图层中,每个图层加载到gpu形成如渲染纹理,使用合成线程进行渲染,不会触发repaint
3d和2d translate区别:
3d在动画渲染前创建独立的复合图层,2d在运行期间创建;因此2d多两次repaint(创建+删除)
gpu加速缺点:
- 大量使用gpu加速,会渲染大量纹理,导致内存问题
- 影响字体的抗锯齿效果,硬件加速停止,文本还在动画期导致
总结:
- translate使用gpu加速,性能更好;absolute使用cpu,导致repaint,性能差
- translate整个动画过程占据空间,absolute整个动画过程不占据空间
- translate 2d比3d多两次repaint
- 注意使用gpu加速的缺点
- 理解强制触发硬件加速技巧(例如rotate 360deg)
- 触发gpu加速的css属性:transform opacity filter
2.7布局
2.8flex详解
容器属性:
- flex-direction: 主轴方向 row / column
- flex-wrap: 换行
- flex-flow: flex-direction + flex-wrap 简写
- justify-content: 主轴上项目对齐方式 flex-start/end center space-between/around
- align-items: 交叉轴对齐方式 flex-start/end center baseline(第一行文字基线对齐) stretch(默认,每一项上没设置高度,自动填满父元素)
- align-content: 多根主轴对齐方式
项目属性:
- flex-grow: 每一项放大比例, 默认0
- flex-shrink: 每一项缩小比例,默认1,空间不足,项目缩小; 0 不收缩
注意:flex-shrink > 1 当子元素宽度和大于父元素宽度时,flex-shrink 将会按照一定的比例进行收缩,即将子元素宽度之和与父元素宽度的差值按照子元素flex-shrink值来分配给各个子元素,指定各个子元素收缩多少,每个子元素原本宽度减去按比例分配的值,其剩余值为收缩完的实际宽度。
flex-shrink < 1 只收缩溢出空间部分 - flex-basis: 分配多余空间之前,项目占据的主轴空间; 默认auto,项目本身大小 干掉width
- flex: 1 flex-grow/shrink/basis 1/1/0 自适应
2.9浮动
浮动脱离文档流,导致高度塌陷
与浮动元素同级的非浮动元素会跟随其后
父元素的高度无法被撑开,影响与父元素同级的元素
清除浮动带来的影响:
- 给父级height
- 给最后一个浮动的元素添加空div,并且增加clear:both
- 给包含浮动的父级元素包含overflow:hidden
- 使用after伪元素:.clear::after{ content:''; display: block; clear:both;}
2.10 BFC
块级格式化上下文,css布局中生成的盒子区域
创建bfc:
- body
- position:absolute/fixed
- float: none以外的值
- overflow:hidden/auto/scroll
作用:
- 解决margin塌陷(将两个元素变成两个bfc, bfc独立渲染互不影响)
- 解决高度塌陷
2.11 css性能问题
3.js
3.1 js数据类型:
number string boolean null undefined symbol object array
null和undefined区别:null表示没有,undefined有对象但是没值
3.2 js判断数据类型方法:
- typeof: 判断出来number string boolean undefined function 无法判断null array object 均为 object
- instanceof:检测构造函数(右边)的 prototype 属性是否出现在某个实例对象(左边)的原型链上。因此只能判断复杂类型
- object.property.tostring.call():不能检测非原生构造函数的构造函数名。其余都可以
- constructor:不能判断null和undefined。不安全的。因为constructor指向可以改变。
3.3字符串方法:
由于字符串的不可变性,所有方法都不会改变原字符串
查找字符串
- indexof/lastIndexOf: 查找想要的字符串,返回索引值,否则返回-1
- search:查找字符串,返回索引值
- includes: 字符串中是否包含指定的内容,返回布尔值
- startswith:字符串是否指定的内容开头,返回布尔值
- endswith:字符串是否指定的内容结尾,返回布尔值
获取指定位置的字符
- charAt
- charCodeAt
字符串截取
- slice:左闭右开,可以传负值,从后开始,返回新字符串
- substring:左闭右开,不能为负数,返回新字符串
- substr:开始索引,截取的长度;返回新字符串
- split:返回数组;split(',')以逗号分隔
- trim:去掉前后空格
3.4数组方法
不会改变原数组
- slice:左闭右开,可以传负值,从后开始,返回新数组
- concat:合并数组,返回新数组
- join:数组转为字符串,返回转换后的字符串
- indexOf/lastIndexOf/includes
- find:找到第一个满足条件返回true的元素
- findIndex
- every/some: 全满足返回true/只要有一个满足返回true
- map:映射,对每一项进行加工,返回新数组
- filter:返回为true的项,组成新数组
改变原来数组
- push:尾部添加,返回新数组长度
- pop:尾部删除,返回被删除元素
- unshift:头部添加,返回新数组长度
- shift:头部删除,返回被删除元素
- splice:删除指定元素,返回被删除元素
- fill:填充数组,返回新数组
- reverse:反转数组
- sort:从小到大排列
- reduce
3.5 对象方法
- defineProperty(obj, property, detail): detail包含value configable writable enumerable
- assign:浅copy,enumerable为false不能copy
- create:使用指定对象原型创建一个新的对象
- keys:可枚举属性的数组,与for...in区别在于得到原型链上的属性
- freeze:浅冻结一个对象
- is:比较两个对象是否相等 Object.is(NaN,NaN)-->true, Object.is(+0,-0)-->false, ===结果相反
3.6 预编译
JS运行:语法分析、预编译、解释执行
- 页面产生便创建GO全局对象(Global Object)window对象
- 第一个脚本文件加载
- 语法分析
- 开始预编译,查找变量声明作为GO属性,值为undefined
- 查找函数声明,值为函数体(作为GO属性)
执行函数预编译
- 创建AO对象(active object)执行期上下文
- 查找形参和变量声明,值为undefined
- 实参值赋给形参
- 查找函数声明,值为函数体
函数声明 > 形参 > 变量声明
3.7 this apply call bind
this函数执行期的上下文对象,根据函数调用方式不同,this指向不同的对象
- 以构造函数的形式调用时,this指向实例对象
- 使用call和apply、bind调用时,this指向指定的对象
- 以方法的形式调用时,this指向调用方法的那个对象
- 函数的形式调用时,this指向window
- 事件绑定函数的调用时,this指向绑定事件的对象
apply、call、bind比较
call:修改this指向,调用函数。可以实现继承。参数:this指向,实参1,2...
apply:修改this指向,调用函数。参数:this指向,[实参数组]
bind: 修改this指向,不调用函数。参数:this指向,实参1,2...
3.8 原型和原型链
prototype为原型对象、__proto__对象原型
3.9 通过new方法创建对象的过程
- 首先创建一个新对象
- 将新对象的__proto__指向构造函数的原型
它将新生成的对象的__proto__属性赋值为构造函数的prototype属性,使得通过构造函数创建的所有对象可以共享相同的原型。这意味着同一个构造函数创建的所有对象都继承自一个相同的对象,因此它们都是同一个类的对象。 - 改变this指向,指向空对象
- 对构造函数的返回值做判断,返回相应值
返回值是对象类型则直接返回,基本类型则返回空对象
3.10 闭包
有权访问另一个函数作用域中变量的函数
闭包的作用:
- 延伸变量的作用范围
- 私有化变量
- 模拟块级作用域
- 创建模块
function fn1() {
let a = 10;
function fn2() {
console.log(a);
}
return fn2;
}
const res = fn1();
res();
缺点:fn1执行后,变量a不会立即销毁,因为fn2中还要继续调用变量a;导致变量一直在内存中,过多的闭包导致内存泄漏
3.11 拷贝
浅copy 指向同一个地址
- Object.assign(obj1, obj2)
- 展开运算符 ...
- concat
- 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操作
- 创建节点:createElement
- 插入节点:parentNode.appendChild parentNode.insertBefore
- 删除节点:parentNode.removeChild
- 复制节点:cloneNode
节点属性
- 获取节点属性:ElementNode.getAttribute(key)
- 设置节点属性:ElementNode.setAttribute(key, value)
- 删除节点属性:ElementNode.removeAttribute(value)
js动画
- offset元素尺寸
- scroll滚动
- client可视区大小
- scrollheight - scrolltop = clientheight
3.13 BOM
对象
- window
- Navigator:当前浏览器信息,识别不同浏览器
- Location:浏览器地址栏信息
- History:浏览器历史记录
- Screen:屏幕信息
常用方法
- history.length
- history.back()
- history.forward()
- history.go()
- location.href
- location.reload
3.14 let const
let:定义变量
const:定义常量(定义后,不可修改)
特点:
- 不属于顶层对象
- 不允许重复声明
- 不存在变量提升
- 暂时性死区(TDZ,仅针对当前作用域有效)
- 支持块级作用域
3.15 箭头函数
特点
- 写法简单
- this,本身无this,内部this就是外部代码的this,因此也无法使用call修改this指向
- 函数声明和箭头函数都有参数默认值
剩余参数和arguments比较
- 剩余参数普通函数和箭头函数都可以使用,arguments只能普通函数使用
- 剩余参数包含没有对应形参的实参,arguments包含所有参数
- 剩余参数是真实数组,arguments是类数组对象
- 剩余参数更加灵活
3.16 set map
set
- 类似数组,允许存储任意数据类型,但是不能重复
- set常用方法:get size set delete has clear
map
- 用于存储映射关系
- 与之前用于存储映射关系的对象相比,map可以将对象作为key存储
- 数组、字符串、map、set可以使用for...of遍历,普通对象不能
备注:是否可以使用for...of遍历,要看原型上是否存在Symbol.iterator方法,普通对象没有这个方法
3.17 Promise
异步编程方案
- 解决回掉地狱
- 管理异步操作更加容易
Promise状态
- 初始化 pending --> new Promise
- 成功 fullfilled --> resolve()
- 失败 rejected --> reject()
Promise实例方法
- then
- catch
- finally
Promise的静态方法
- Promise.resolve()
- Promise.reject();
- Promise.all([]) 并发处理多个异步任务,都成功resolve 一个成功 reject
- Promise.race() 并发处理多个异步任务 返回第一个处理的Promise,且状态与第一个状态一致
- Promise.allSettled() 多个异步,返回所有结果,适用于多个彼此不依赖的异步任务
3.18 事件循环补充
异步操作
- 事件监听
- 定时器 settimeout setinterval ajax
- Promise Generator async/await
异步任务
- 宏任务
- 微任务:process.nextTick Promise.then/catch/finally Mutationobserver()dom节点发生变化时调用
第一个宏任务是script
event loop
- 在宏任务队列中,按照入队顺序,找到第一个执行的宏任务,放入调用栈,执行
- 执行完该宏任务队列下所有同步任务后,即调用栈清空,该宏任务被推出宏任务队列,然后微任务队列按照入队顺序执行,直到清空
- 清空微任务队列,一个事件循环结束
- 在宏任务队列中,找到下一个宏任务,开始执行下一个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常用拦截方法
- get --> get(target, propkey, [recevier]) recevier操作所针对的对象
- set --> set(target, propkey, value, [recevier])
- has --> has(target, propkey) target是否存在propkey
Reflect
- 对某些返回方法的结果进行修改
- 使用函数的方式实现了object的命令式操作
Reflect方法
- get(target, name, recevier) 当target中存在name,this绑定recevier
- set(target, name, value, recevier)
- has(obj, name) 是否存在
- deleteProperty(obj, property) 删除obj的property属性
- getPropertyof(obj)
- defineProperty(target,name,attr)
4. ts(待)
5. node
node.JS 是 js在服务端的运行环境
5.1 常见模块
- buffer缓冲区,用于存储二进制数据,操作方法与数组类似
- fs文件系统模块,主要使用异步,文件较大时多次读取
- path路径模块
- http模块,创建web服务
5.2 模块化
- commonJS
- AMD
- CMD
- ES6
5.3 require import区别
- require(AMD)--> module.exports/exports(commonJS)--> 运行时调用
- 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+)(待)
useEffect & useCallback & useMemo
1.1 概念
- 函数式组件:调用函数
- 类式组件: 通过对象实例调用原型上的render
1.2 核心属性
- state
- props
- refs
1.3 事件处理
1.4 生命周期
1.5 路由传参
1.6 组件通信
1.7 Redux
1.8 React Hooks
- useState
- useEffect
- useMemo
- useContext
1.9 react-fiber
2. Vue(Vue2 + Vue3)
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 指令
- v-bind 单向数据绑定
- v-model 双向数据绑定
- v-if 条件渲染
- v-show 条件渲染
v-if 和 v-show区别
v-if 删除和创建元素控制隐藏 切换开销大 适合条件改变较少
v-show 通过display:none控制 初始开销大 适合频繁切换
- v-for条件渲染
key的作用:虚拟dom的标识,提高渲染效率
旧虚拟dom和新虚拟dom的key相同:
- 内容没变,直接复用真实dom
- 内容改变,新替换旧
2.4 computed methods watch
computed:计算属性。通过已经有的属性计算得到的
原理就是使用了object.defineProperty()的getter和setter
methods: 方法
computed和methods区别:
- computed是响应式,methods是非响应式
- computed像属性访问一样,methods以函数形式调用
- computed有缓存机制,效率高
watch:监听器,当需要数据变化时,异步或者开销大的操作时,使用watch。不要使用箭头函数,this指向不对。 immediate立即执行,deep深度监听
2.5 生命周期
- beforeCreate:无法通过vm访问到data中的数据,methods中的方法
- created:可以通过vm访问到data,methods,模版解析未完成
- beforeMount:页面是未编译的dom,dom操作无效
- Mounted:编译后的dom,开定时器,ajax,订阅消息,绑定事件
- beforeCreate:数据新,页面旧,未同步
- Created:数据新,页面新,同步
- beforeDestory:data可用,在此阶段关闭定时器,取消订阅等
- Destoryed
2.6 组件通信
- props:父--> data v-on绑定 子--> props接收
$emit:利用$emit传值 父--> v-on绑定相应事件,通过e传值- 事件总线: 在main.js中进行配置,Vue.prototype.
$bus= this。 通过this.$bus.$emit()传递事件。在Mounted中通过this.$bus.$on接收数据。在beforeDestory中解绑数据this.$bus.$off() - 消息订阅:利用插件pubsub等
- provide/inject 适用于祖孙通信
- ref:适用于父子间双向通信
- Vuex状态管理
2.7 Vuex
概念:
- state:统一定义公共数据
- mutations:修改数据
- getters: 计算属性
- actions:发起异步请求
- modules:拆分复杂业务
2.8 路由传参
- query参数:传递 :to =
?id=${id}接收:this.$route.query.id - params参数:动态路由 path:'/:id' 接收:this.$route.params.id
- props参数:在router/index.js中,props(route) {return {id: route.query.id}} 接收:props:[id]
2.9 路由守卫
对路由权限进行控制 例如:about --> home
- about --> beforeRouteLeave
- beforeEach
- home --> beforeEnter
- home --> beforeRouteEnter
- afterEach
2.10 $nextTick
在下次dom更新循环结束之后延迟回掉,在修改数据后使用$nextTick,则可以在回掉中获得更新后的dom
2.11 Vue3对比Vue2变化
- 响应式原理不同,Vue2 defineProperty;Vue3 proxy reflect
- Vue2深度监听在初始化时旧一次递归监听就好了;Vue3是在获取值的时候才对其监听(按需)
- Vue2无法追踪到对象增加新值,删除属性;Vue3可以
- Vue2数组使用包装的API;Vue3可以直接使用数组方法
3. diff算法
三. 实习
1. Next.js
1.1 Next.js渲染模式
概念
- MPA:多页面应用 首屏速度快/ 切换页面慢/ SEO友好
- SPA:单页面应用 与 MPA 相反
CSR:客户端渲染 React/Vue默认开发模式, 前后端分离
渲染流程:
- 浏览器请求页面
- 前端服务器返回html页面
- 浏览器请求js脚本
- 前端服务器返回js脚本
- 浏览器执行js脚本
- js脚本请求数据
- 后端服务器查询数据库
- 数据库返回数据直到渲染到页面
SSR:服务器端渲染(请求时) 输出完整的html,整个渲染过程都在服务器端进行
渲染流程:
- 浏览器请求页面
- 前端服务器向后端服务器请求数据
- 后端服务器查询数据库
- 数据库返回数据给前端服务器
- 在前端服务器中数据与html组合返回
- 渲染页面
- 浏览器请求js脚本
- 前端服务器返回js脚本
- 浏览器执行js脚本,绑定事件
- 请求数据,动态渲染页面
备注:这里的SSR是指,首页返回完整的HTML,浏览器通过hydrate操作,成为React/Vue应用, 后续请求不会向服务端请求html,是以类似SPA方式进行
同构直出
在服务端渲染中,有两种页面渲染的方式:
- 后端服务器获取数据并生成 HTML 返回给浏览器解析渲染页面
- 浏览器在交互过程中,请求新的数据并动态更新渲染页面
这两种渲染方式的不同点在于运行环境的不同。同一份代码可以在客户端和服务器端运行,两个环境的渲染结果应该保持一致。因此,我们需要实现客户端和服务器端的路由、页面模板和数据共享。
整体渲染流程
dehydrate: 脱水,静态的html片段,无法交互,适合网络传输
hydrate: 注水,将dehydrate的html片段加入数据和交互,构建完整应用
SSG:静态生成 构建时生成html
渲染流程:
- 构建阶段:SSG将html和css结合,生成静态页面
- 预渲染:SSG在构建过程中自动执行预渲染。这意味着 SSG 会根据预定义的路由和数据源,在构建时生成静态页面的多个实例。例如,对于一个博客,每篇文章都可以在构建过程中生成一个独立的静态页面。
- 静态输出:构建完成,SSG将生成的静态页面输出到目标文件中,包含html,css,js, 静态资源等
- 部署:生成的静态页面可以直接部署到任何支持静态文件的Web服务器上。无需动态生成,可以缓存到CDN中
- 用户访问:首屏直接解析 html 生成 dom。接着和 SSR 一样通过 hydrate 将整个应用转变成为 React 或 Vue 应用,使用户在交互时与单页应用无异。
ISR:增量静态生成 将部分静态页面在构建时生成,并在用户访问时进行增量更新。
需要在getstaticprops中设置revalidate
渲染流程:
- 构建阶段:在构建过程中,使用SSG(静态站点生成器)生成静态页面,并将这些页面上传到 CDN。
- 实时内容使用 SSR 来动态生成这些内容,并在后续的请求中进行增量更新,从而保持页面的实时性。
渲染模式对比
SSR
优点:SEO友好,首屏时间短
缺点:占用服务器资源,代码复杂性增加
CSR
与SSR相反
SSG
优点:充分利用缓存性能高,SEO友好,方便部署(不用依赖node),安全性好(服务器不需要运行程序,因此服务器漏洞仅限于操作系统本身,维护也更加方便)
缺点:静态,不适合总改的数据
ISR
优点:SSR和SSG之间的平衡点
缺点:访问到之前被预渲染过,但已经过期且未更新的页面,会得到过期的缓存响应。用户刷新一次,才能看到新的数据。
1.2 getstaticprops中fallback不同值的区别
- false:只会在build的时候构建制定的页面,如果没有指定则返回404
- true:访问没有定义的页面时会议客户端渲染(csr)的方式进行渲染,同时,提供router.isFallBack来判断数据是否从getstaticprops返回,显示或者隐藏自己的页面,如果使用了next.js但是某一个页面不想采用服务端渲染,采取这样的方式是可行的
- block: 生成ssr再返回,这种方式可能会很慢,但是只生成一次,生成后next会把它添加到预渲染列表中
1.3 Next.js性能优化
最直观的就是通过performance与lighthouse来评判 参考链接
- 通过 ssh登陆远程服务器开启 nginx 的gzip。 gzip on
- npm run build命令 查看 First Load JS. 用较小的等效库替换较大的库,并删除不相关的库 或者改为自定义库
- 针对非首屏组件基于 dynamic 动态加载。import dynamic from 'next/dynamic' const Modal = dynamic(() => import('../components/mModal'));
- next/image 优化图片资源。next/image 可帮助我们对图片进行压缩(尺寸 or 质量),且支持图片懒加载
- next/link 预加载,将 prefetch 属性添加到其中并将其设置为 false, 当用户 hover 到 Link 标签时,对即将跳转的页面资源进行预加载,进一步防止页面卡顿
- 开启 SWC 编译
- 资源上 CDN,减少Waiting for server response的性能损耗
说明:Next.js图片优化 cls:累计位移布局 lcp:最大内容绘制
- 对于缩略图:NextJs采用配置型loader实现不同云端缩略图请求的能力
- 对于cls:只要在图片显示之前知道图片的宽高,或者人为定义显示区域的宽高,这样无论图片宽高是多少,都会被限制在这个范围内,从而无法影响布局,NextJS Image的做法是在外层套一个span元素,在使用组件时传入宽高,再将span元素的宽高设置上去,撑开容器即可。
- 分层次加载:视窗中的图片首先加载无可厚非,剩下的视窗外的,肯定是越接近视窗的先加载,但不排除距离较远元素优先级更高从而先加载的场景,这里浏览器提供了相关的api。intersectionObserver当被观察元素与祖先元素相交值到达指定阈值时触发回调。这样就可以在图片元素快到达视窗时设置其src,从而进行加载。指定以何种策略加载图片
eager:立即加载图像,不管它是否在可视视口(visible viewport)之外(默认值)。
lazy:延迟加载图像,直到它和视口接近到一个计算得到的距离,由浏览器定义
设置rel=preload将使浏览器对资源进行优先加载
- 对于保证滚动时动画尽可能的流畅 通过对图片进行decode和append分离,在decode未完成之前,只渲染外层容器。
2. 登陆鉴权 (待)
2.1 session-cookie鉴权
- cookie:http协议是无状态的,服务器为了区分不同的客户端,通过cookie去实现
- session:会话,是无状态协议通信过程中,为了实现中断/继续操作,将用户和服务器之间的交互进行的一种抽象
认证步骤:
- 客户端:向服务器发送登陆信息用户名/密码等,请求校验
- 服务端:校验通过后,生成session,并根据session生成sessionId,并在响应头set-cookie中设置sessionId
- 客户端:解析响应头,保存在cookie中,下次请求时携带sessionID
- 服务端:接收到请求后,解析sessionId,判断请求是否合法
缺点:
- 依赖cookie
- 保存在cookie中不安全
- session存储在服务端,增大开销
- 移动端支持不友好
2.2 Token鉴权
token组成:uid + 时间戳 + 签名(token前几位哈希算法压缩成的16进制字符串)
认证步骤:
- 客户端:向服务器发送登陆信息用户名/密码等,请求校验
- 服务端:
2.3 JWT
2.4 单点登录
2.5 联合登录和信任登录
2.6 唯一登录
2.7 扫码登录
2.8 一键登录
四. 计算机网路
1. http、https
HTTPS 的加密过程可以简单描述如下:
- 客户端发起 HTTPS 请求,连接到服务器的安全端口(一般是 443 端口)。
- 服务器将自己的 SSL/TLS 证书发送给客户端。
- 客户端验证服务器的证书是否有效、是否由受信任的证书颁发机构签发,并检查证书中的域名与实际访问的域名是否匹配。
- 如果验证通过,客户端生成一个随机的对称加密密钥,并使用服务器的公钥进行加密,然后发送给服务器。
- 服务器使用自己的私钥解密客户端发送的加密密钥。
- 客户端和服务器都使用这个对称密钥进行加密和解密数据,确保通信过程中的数据保密性和完整性。
安全通信的四大原则:机密性、完整性、身份认证、不可否性
http明文传输,会存在窃听风险、篡改风险、冒充风险
https = http + ssl/tls 使得https不直接与tcp对话
https非对称加密:解决客户端到服务端传输的安全
- 客户端存储公钥
- 服务端存私钥
https数字证书:解决服务端到客户端传输公钥的信任问题(第三方验证真实性)
- 服务器向CA申请证书,证书中包含公钥主机名host等信息
- 服务器申请证书后,向客户端发送的就是含有公钥的证书
数字证书加密方法:
- 证书信息,无加密
- 数字签名,是经过CA上的私钥加密过,只有客户端用一开始就存在于操作系统内部存储的公钥才可以解密。
数字签名加密:
- 对证书的各种信息进行随机摘要,通过MD5算法生成摘要秘文
- 使用CA私钥对摘要进行加密,这样的话客户端使用CA公钥进行解密。对比摘要是否相同
- 防止整个调包,客户端还会对证书上的域名和请求域名是否一致。
备注:公钥,是被操作系统信任,内置在客户端的操作系统上的CA证书上的。所以无需传输,因此也不会被劫取。这意味着中间人是没有这个公钥的。
2. get post
定义:http协议中两种发送请求对方法
GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
get:浏览器会把http header和data一起发送,返回200ok post:浏览器先发http header,服务器响应100 continue;然后发送data,服务器响应200,返回数据
- GET与POST都有自己的语义,不能随便混用。
- 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
- 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
3. 跨域
简单请求和非简单请求
- 简单请求:get post head
- Head头信息:Accept Accept-Language Content-Language Last-Event-ID Content-Type:application/x-www-form-urlencoded text-plain
- 非简单请求:需要先发送option预检
正向代理:代理客户端 反向代理:代理服务端
同源策略(协议、域名、端口)
- JSONP: 利用script标签没有跨域限制,通过script的src属性。发送带有callback参数的GET请求。服务端将接口返回数据拼凑到callback函数中,返回给浏览器。浏览器解析执行,前端从callback中拿到返回到数据。JSONP只能发送GET请求。
- 跨域资源共享(CORS):普通跨域请求只需要服务端设置Access-Control-Allow-Origin;若要带有cookie跨域请求,前端增加withCredentials = true。这里的cookie是接口所在的cookie并非当前页的cookie。当前页的cookie使用需要Nginx和NodeJS中间件实现。
- Nginx代理:通过Nginx配置一个代理服务器域名与目标url相同,端口不同做跳板机。反向代理访问另一个接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域访问。domain设置proxy_cookie_domain
- NodeJS中间件代理跨域:大多数情况下使用http-proxy-middleware进行代理,实现数据转发。通过设置cookieDomainRewrite: { '*': ''},可以实现当前域中cookie写入。
- postMessage:解决跨域,多窗口通信,嵌套iframe通信
4. websocket
暂时忽略
5. 输入url浏览器加载流程
- 浏览器通过进程间通信将url传给网络进程
- 网络进程收到url后,先查找缓存,有缓存,返回;无缓存,发送网络请求
- 发送网络请求,获取IP,从host中获取IP,建立tcp链接;host中无IP,将网址提交给DNS进行域名到IP映射
- 利用IP地址和服务器简历tcp链接(3次握手)
- 建立链接后,浏览器构建数据包(请求头...),向服务器发送请求
- 服务器根据请求信息,构建响应数据,发回网络进程
- 网络进程收到响应数据进行解析,判断state code,3xx重定向 200ok
- 传输完成,tcp四次挥手断开链接,如果有keep-alive,不断开
- 解析数据,根据content-type判断data type
- 如果是text/html --> 渲染 renderTree = domtree + style tree + layout
6. 网络模型
- 应用层(HTTP DNS FTP SMTP)
- 传输层(TCP UDP)
- 网络层(IP)
- 数据链路层
- 物理层
五. 其他
1. 前端性能优化措施/指标/工具等
从前端体系考虑性能
指标:加载
- FP:白屏时间
- FCP:首屏加载时间
- LCP:最大内容绘制时间
- TTI:可交互时间
- TTFB:资源从请求到响应,有一个等待时间
交互:
- 页面响应时间要短
- 页面滚动距离
- 异步请求接口完成时间要短
刷新率:屏幕参数 帧率:图像参数
RAIL模型:
- response: < 50ms 处理事件输入
- Animation:> 60桢 1000/60 = 16.6ms
- Idle:最大限度增加空闲时间
- Load:加载时间越短越好
前端性能监控performance
测量和分析网页性能指标,用于前端性能监控
- performance.navigation 页面导航相关性能指标
- performance.timing 页面资源加载和处理的性能指标
- performance.memory 内存
- performance.mark/measure 插入标记点,测量特定代码块时间
2. 重排和重绘
重排:发生在布局阶段,计算元素在设备可视区的大小
触发重排:
- display:none
- dom元素的增删改查
- 修改样式和大小
- 移动元素
触发重绘:
- dom改动
- css改动
如何减少重排重绘
- 在dom tree的末端改变元素css
- 动画效果尽量应用到脱离文档流的元素上
- 多用visibility:hidden
- 一次性加载dom等
3. 缓存(http缓存)
强制缓存与协商缓存共同使用,保证效率
强制缓存
基于cache-control实现强制缓存,单位为秒。 200读取缓存
cache-control属性
- max-age: 决定客户端资源被缓存多久
- s-maxage:决定代理服务器缓存时长
- no-cache:强制进行协商缓存
- no-store:禁止任何缓存策略
- public:浏览器或者代理服务器缓存
- private:浏览器缓存
协商缓存
基于last-modified的协商缓存方式,对比时间戳
- 服务端读取文件修改时间
- 服务端将修改时间给last-modified
- 服务端设置cache-control:no-cache
- 客户端读取到last-modified时,在下一次请求时会带上If-Modified-Since,且值为last-modified的值
- 服务端收到请求后,对比If-Modified-Since和last-modified,是否读取缓存,304协商缓存,200不读缓存
基于E-tag的协商缓存方式,对比文件指纹(哈希值)
- 服务端读取文件并计算文件指纹,并将指纹哈希值放到字段etag中
- 客户端第二次请求时,将etag值赋值给if-None-Match
- 服务端收到请求后进行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. 垃圾回收机制
浏览器根据数据存储方式分为栈垃圾和堆垃圾
- 栈垃圾回收: 当函数执行完后,js引擎通过向下移动指针,来销毁该函数保存在栈中的执行上下文;先进后出原则。
- 堆垃圾回收:堆垃圾的饮用被清除了,但是堆垃圾还存在。标记清除算法/引用记数算法
V8引擎也是基于标记清除算法进行优化,将内存分为新生代和老生代
- 新生代:存留时间短的对象--> scavenge算法
- 老生代:经历新生代垃圾回收后还存在的对象--> 标记清除+整理算法
scavenge算法:正在被使用的区域是使用区,未被使用的是空闲区
- 新加入的对象会被放在使用区,当使用区快写满时副垃圾收集器就要进行一次清理操作
- 进入垃圾回收阶段,新生代回收器会对使用区内的对象进行标记
- 标记完成后,需要对使用区内的活动对象进行copy到空闲区并进行排序
- 垃圾回收阶段,将使用区内非活动对象进行清理
- 将空闲区和使用区进行翻转
标记清除算法:老生代区域中对象占据内存较大,如果采用scavenge算法copy消耗较大
- 垃圾收集器运行时会给内存中所有变量进行标记
- 从各个根对象开始遍历,把还在引用的对象标记清除
- 清理所有带标记的变量,销毁并回收他们占据的空间
标记整理算法:标记清除后剩余对象内存位置是不变的,产生大量碎片化空间。
- 从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素标记为活动对象。
- 所有存活的对象都向一端移动
- 清理掉端边界以外的内存
增量标记算法:
V8 是使用副垃圾回收器和主垃圾回收器处理垃圾回收的,不过由于 JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。我们把这种行为叫做全停顿。 为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成。
7. xss/csrf
8. webpack
查看参考链接即可
常见loader
- sass-loader
- less-loader
- i18n-loader
- cache-loader
常见plugin
- define-plugin
- ignore-plugin
- web-webpack-plugin
- clean-webpack-plugin
9. webpack与vite对比
核心理念:bundle与否
- webpack:使用node的打包器从入口开始构建依赖图,将项目中所需要的模块组合成一个或者多个bundle文件,逐层识别依赖,构建知识图谱。
- vite:无需bundle,源文件之间的关系通过浏览器对es module的支持进行解析,将依赖中的模块区分为依赖和源码两部分。依赖:使用esbuild进行构建,esbuild使用go编写,因此,比使用node编写的打包器速度更快;源码:在浏览器请求时按需转换,并以原生的es module方式提供源码,让浏览器接管打包程序的部分工作。
首屏、懒加载性能
- webpack:使用bundle,使用了完整的模块依赖包,不存在首屏和懒加载性能问题
- vite:未使用bundle,出现以下问题;由于未对源文件进行bundle,存在大量的http请求;动态加载的文件需要对源文件进行转换操作:load、transform、parse;预构建、二次构建操作也会阻塞首屏请求;但是由于缓存,后续请求会加快
服务启动速递
- webpack:使用bundle,建立所有模块的依赖关系,速度较慢
- vite:无需bundle,应用中的模块区分为 依赖(node_modules) 和 源码(项目代码) 两类,进行预构建,速度会快很多;依赖:使用esbuild进行构建,esbuild使用go编写,因此,比使用node编写的打包器速度更快;源码:在浏览器请求时按需转换,并以原生的es module方式提供源码,让浏览器接管打包程序的部分工作。
热更新速度
- webpack:动态模块热重载HRM,对其余部分没影响,提升速度;在实际使用中,效果一般,随着项目体积变大,HRM效果也会下降
- vite:HRM是在原生的es module中执行,当编辑一个文件时,只需要精确地使已编辑的模块与其最近的HMR边界之间的链失效(大多数时候只需要模块本身),使HMR更新始终快速,与应用大小无关。并且,vite中源码会使用304进行协商缓存;依赖会进行强制缓存,提升速度
prod环境打包区别
- webpack:在生产环境的构建方面更加成熟,bundle整体形成完善的依赖关系,也有非常多的loader或者plugin可供选择;
- vite: Rollup。因为存在大量的http请求,最好的方式还是代码进行tree-shaking、懒加载、和 chunk 分隔等;
生态成熟度: webpack > vite
10. git
常用命令:
- git fetch featureName 获取远程仓库分支更新 --all 所有分支更新
- git pull origin 从远程仓库拉取代码到本地 = git fetch + merge
- git pull -rebase origin 拉取后使用rebase进行合并,减少冲突,使用merge多一次merge记录
- git branch 查看本地分支 git branch branchname 创建branchname本地分支
- git checkout 切换分支 -b 切换并且创建分支
- git add 将修改文件添加到暂存区
- git commit 将暂存区文件提交到本地仓库
- git push 将暂存区文件提交到远程仓库 -f 强制提交
- 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
- 那么当 dev 要合并 feature-a 的内容时,使用 git merge feature-a
- 反过来当 feature-a 要更新 dev 的内容时,使用 git rebase dev
代码回退
- git checkout -- filename 撤回工作区文件的修改
- git reset HEAD -- filename 撤回暂存区文件的修改
- git reset --hard commit-id 回退到其他commit版本 影响 工作、暂存、本地仓库
- git revert 代码回退
git reset和revert区别
- reset是根据来移动HEAD指针,在该次提交点后面的提交记录会丢失。
- revert会产生新的提交,来抵消选中的该次提交的修改内容,可以理解为“反做”,不会丢失中间的提交记录。
- 公共分支回退使用revert,自己分支回退按需使用