HTTP3
QUIC(想用udp替代tcp。)
特点
- 零RTT建立连接
- 拥塞控制
- 流量控制
- 多路复用
V8垃圾回收
v8内存结构
垃圾回收主要集中在新生代和老生代
新生代:由两个半空间组成,采用Scavenge(牺牲空间换取时间的算法)
1、激活空间称为from,未激活称为to。程序被分配到from空间,
2、进行垃圾回收时,若from空间有存活对象将其复制到to。其余垃圾回收,角色互换。
3、对象晋升:当一个对象多次复制依旧存活则将其放入老生代。
老生代:管理大量存活对象,采用标记清楚和标记整理
标记整理:为了防止大量的内存碎片,因此将活得移动到一侧,清除另一侧的所有。
React的实现粗划分为两部分:
reconciliation(diff):阶段将 vdom 转换成 fiber,确定节点操作,并创建用到的 DOM
commit(操作DOM): 阶段执行实际 DOM 操作
reconciliation是一个自顶向下的递归算法,一旦开始就会持续占用线程。采用fiber异步渲染。
react-fiber
实现了一个基于优先级和requestIdleCallback的循环任务调度算法。Fiber核心是更换了reconciliation阶段的运作。
-
reconciliation阶段可以把任务拆分成多个小任务
-
reconciliation阶段可随时中止或恢复任务
-
可以根据优先级不同来选择优先执行任务
FIBER运行实例
Fiber是把render/update分片,拆解成多个小任务来执行,每次只检查树上部分节点,做完此部分后,若当前一帧(16ms)内还有足够的时间就继续做下一个小任务,时间不够就停止操作,等主线程空闲时再恢复。
这种停止/恢复操作,需要记录上下文信息。而当前只记录单一dom节点的vDom tree 是无法完成的, Fiber引入了fiber tree,是用来记录上下文的vDom tree。
Fiber是根据一个fiber节点(VDOM节点)来拆分,以fiber node为一个任务单元,一 个组件实例都是一个任务单元。任务循环中,每处理完一个fiber node,可以中断/挂起/恢复。
基于requestIdleCallback和优先级任务调度
浏览器提供的requestIdleCallback API中的Cooperative Scheduling可以让浏览器在空闲时间执行回调(开发者传入的方法),在回调参数中可以获取到当前帧(16ms)剩余的时间。利用这个信息可以合理的安排当前帧需要做的事情,如果时间足够,那继续做下一个任务,如果时间不够就歇一歇,调用requestIdleCallback来获知主线程不忙的时候,再继续做任务。
调用requestIdleCallback获取所剩时间,若执行时间超过了deathLine,或者突然插入更高优先级的任务,则执行中断,保存当前结果,修改tag标记一下,设置为pending状态,迅速收尾并再调用一个requestIdleCallback,等主线程释放出来再继续2.恢复任务执行时,检查tag是被中断的任务,会接着继续做任务或者重做
HOOK不能再循环中
react用链表来严格保证hooks的顺序
useState
返回一个数组,数组的值为当前state和更新state的函数u。useState的参数时变量,兑现或者函数。变量或者对象会作为state的初始值,函数的返回值会作为初始值。
useState的初始值,只在第一次有效
let [count,setCount] = useState(0)
与在类中使用 setState
的异同点:
- 相同点:在一次渲染周期中调用多次
setState
,数据只改变一次。 - 不同点:类中的
setState
是合并,而函数组件中的setState
是替换。
useEffect
第一个参数是回调函数,第二个参数是数组。数组的内容是依赖项deps。依赖项改变后执行回调函数
😄1.只在第一次使用的componentDidMount,可以用来请求异步数据...、
useEffect最后,加了[]就表示只第一次执行
useEffect(()=>{
const users = 获取全国人民的信息()
},[])
😄2.用来替代willUpdate等每次渲染都会执行的生命函数
useEffect最后,不加[]就表示每一次渲染都执行
useEffect(()=>{
const users = 每次都获取全国人民的信息()
})
😄3.每次渲染都执行感觉有点费,所以:
useEffect最后,加[],并且[]里面加的字段就表示,这个字段更改了,我这个effect才执行
useEffect(() => {
const users = (name改变了我才获取全国人民的信息())
},[name])
😄4.如果我想要分别name和age呢:
可以写多个useEffect
useEffect(() => {
const users = (name改变了我才获取全国人民的name信息())
},[name])
useEffect(() => {
const users = (name改变了我才获取全国人民的age信息())
},[age])
😄5.如果我们之前订阅了什么,最后在willUnMount这个生命周期里面要取消订阅,这可咋用useEffect实现啊:
在effect的return里面可以做取消订阅的事
useEffect(() => {
const subscription = 订阅全国人民吃饭的情报!
return () => {
取消订阅全国人民吃饭的情报!
}
},[])
useMemo
跟shouldComponentUpdate类似
接受回调函数(返回值是jsx)和一个依赖项数组,依赖项改变才会重新渲染函数的返回值
useMemo(() => {
return <Childone />
}, [render])
根据[name]
里面的name值判断一下,因为useMemo
作为一个有着暂存能力的,暂存了上一次的name结果。
useCallback
跟useMemo类似,第一个回调的里边不是值是函数
useRef
const refContainer = useRef(initialValue);
JSBridge
核心是 构建 Native 和非 Native 间消息通信的通道,而且是 双向通信的通道。
JS 向 Native 发送消息 : 调用相关功能、通知 Native 当前 JS 的相关状态等。
Native 向 JS 发送消息 : 回溯调用结果、消息推送、通知 JS 当前 Native 的状态等。
实现原理
-
JavaScript 调用 Native 方法
- native向webview的context(window)注入一个暴露指定native的方法(android)或者接受javascript消息(iOS)注入
- 拦截webview内的额特定URL Schema,根据URL来执行对应的Native方法 拦截
- 拦截Javascript的console.log alert prompt并执行对应的Native方法
-
Native 调用 JavaScript 方法(调用)
- 用过URL执行javaScript语句
- 通过Android和iOS的方法evaluateJavaScript来执行JavaScript语句
Fetch,Ajax,Axios
- Ajax
- Ajax 是一个技术统称,是一个概念模型,它囊括了很多技术,并不特指某一技术,它很重要的特性之一就是让页面实现局部刷新。
- 局部刷新页面,无需重载整个页面。
-
function ajax(url) { const xhr = new XMLHttpRequest(); xhr.open("get", url, false); xhr.onreadystatechange = function () { // 异步回调函数 if (xhr.readyState === 4) { if (xhr.status === 200) { console.info("响应结果", xhr.response) } } } xhr.send(null); } ajax('https://smallpig.site/api/category/getCategory')
2、fetch
使用了 ES6 提出的 promise 对象。它是 XMLHttpRequest 的替代品。
很多小伙伴会把它与 Ajax 作比较,其实这是不对的,我们通常所说的 Ajax 是指使用 XMLHttpRequest 实现的 Ajax,所以真正应该和 XMLHttpRequest 作比较。
Fetch 是一个 API,它是真实存在的,它是基于 promise 的。
function ajaxFetch(url) {
fetch(url).then(res => res.json()).then(data => {
console.info(data)
})
}
ajaxFetch('https://smallpig.site/api/category/getCategory')
fetch('http://localhost:8088/postInfo', {
method: 'POST',
body: JSON.stringify(data)
})
.then(async (res) => {
const data = await res.json()
console.log(data)
})
3、Axios
Axios 是一个基于 promise 封装的网络请求库,它是基于 XHR 进行二次封装。
Axios 可以说是 XHR 的一个子集,而 XHR 又是 Ajax 的一个子集。既然说它是一个库,那么我们在使用的时候就需要引入它。
// 发送 POST 请求
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
Object.defineProperty
在初始化 Vue 的每个组件时,会对组件的 data 进行初始化,就会将由普通对象变成响应式对象,在这个过程中便会进行依赖收集的相关逻辑
class Dep {
static target;
subs;
constructor () {
...
this.subs = [];
}
addSub (sub) {
this.subs.push(sub)
}
removeSub (sub) {
remove(this.sub, sub)
}
depend () {
if(Dep.target){
Dep.target.addDep(this)
}
}
notify () {
const subs = this.subds.slice();
for(let i = 0;i < subs.length; i++){
subs[i].update()
}
}
}
class Watcher {
getter;
...
constructor (vm, expression){
...
this.getter = expression;
this.get();
}
get () {
pushTarget(this);
value = this.getter.call(vm, vm)
...
return value
}
addDep (dep){
...
dep.addSub(this)
}
...
}
function pushTarget (_target) {
Dep.target = _target
}
Object.defineProperty(_this.$data, key, {//对象,属性
enumerable: true,
configurable: true,
get: function () {
console.log(`${key}获取${value}`);
return value;
},
set: function (newVal) {
console.log(`${key}更新${newVal}`);
if (value !== newVal) {
value = newVal;
binding._directives.forEach(function (item) {
item.update();
})
}
}
})
通过下标方式修改数组数据或者给对象新增属性,这都不能触发组件的重新渲染
Keep-alive
- 判断name,不再include或者exclude中,返回vnode
- 获取实例的key(若没有则生成:cid+::+tag)
- 若缓存对象存在则直接从缓存对象中获取组件给vnode,不存在则添加到对象中
- 超出max时,清除第一个组件
Tree Shaking原理
Webpack 中,Tree-shaking 的实现一是先标记出模块导出值中哪些没有被用过,二是使用 Terser 删掉这些没被用到的导出语句。标记过程大致可划分为三个步骤:
- Make 阶段,收集模块导出变量并记录到模块依赖关系图 ModuleGraph 变量中
- Seal 阶段,遍历 ModuleGraph 标记模块导出变量有没有被使用
- 生成产物时,若变量没有被其它模块使用则删除对应的导出语句
WebPack构建流程
- 解析配置项,各种参数
- 生成compiler对象,初始化内部插件,初始化options配置
- run:确认入口,生成compilation对象
- make:开始编译
- seal:生成chunk,渲染chunk并生成要输出的代码bundle
- emit:生成文件前的最后
- 结束编译
CDN原理解析
CDN(content delivery netWork)内容分发网络,源站的资源缓存到位于全球各地的 CDN 节点上,用户请求资源时,就近返回节点上缓存的资源,而不需要每个用户的请求都回您的源站获取,避免网络拥塞、缓解源站压力,保证用户访问资源的速度和体验。
IFC
IFC(Inline Formatting Contexts)行内级格式化上下文
- 块级元素中仅包含内联级别元素
行内元素:a b span img select input strong
空元素: img input meta link
块元素: div ul ol li h1 h2 h3
前端路由原理
Hash模式:
把前端路由用#连接在真是url后的模式。路径变化不会发起请求,而是触发onhashchange事件。
History模式:
History Api是H5新特性,允许开发者直接更新前端路由。
常用方法:history.pushState history.replaceState。更改记录
Commonjs和esmodule区别
es6:1、是值的引用,2、异步加载,3、编译时输出接口
cmj:1、值的拷贝,2、同步加载,3、运行时加载
CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成
CMD 、CMD
-
CommonJs用在服务器端,AMD和CMD用在浏览器环境
-
AMD 是 RequireJS 在推广过程中对模块定义的规范。
-
CMD 是 SeaJS 在推广过程中对模块定义的规范。
-
AMD:提前执行(异步加载:依赖先执行)+延迟执行
-
CMD:延迟执行(运行到需加载,根据顺序执行)
VUE父子组件生命周期顺序
加载过程(在mounted挂载到dom前开始子的周期)
父beforeCreate->
父created->
父beforeMount->
子beforeCreate->
子created->
子beforeMount->
子mounted->
父mounted
WebSocket
WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术,
解决了半双工通信的弊端。它最大的特点是:服务器可以向客户端主动推动消息,客户端也可以主动向服务器推送消息
// 在客户端与服务端建立连接后触发
ws.onopen = function () {
console.log("Connection open.");
ws.send('hello');
};
// 在服务端给客户端发来消息的时候触发
ws.onmessage = function (res) {
console.log(res); // 打印的是MessageEvent对象
console.log(res.data); // 打印的是收到的消息
};
// 在客户端与服务端建立关闭后触发
ws.onclose = function (evt) {
console.log("Connection closed.");
};
vue与react异同
相似之处:(续传组)
1、都使用了Virtual DOM(虚拟DOM)提高重绘性能
2、都有props的概念,允许组件间的数据传递
3、都鼓励组件化应用,将应用分拆成一个个功能明确的模块,提高复用性
不同之处:(畜牧肩高)
1)虚拟DOM
Vue2.x开始引入"Virtual DOM",消除了和React在这方面的差异,但是在具体的细节还是有各自的特点。
- Vue宣称可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
- 对于React而言,每当应用的状态被改变时,全部子组件都会重新渲染。当然,这可以通过 PureComponent/shouldComponentUpdate这个生命周期方法来进行控制,但Vue将此视为默认的优化。
2)模板编写
- Vue鼓励写近似常规HTML的模板。写起来很接近标准 HTML元素,只是多了一些属性。
- React推荐你所有的模板通用JavaScript的语法扩展——JSX书写。
3)监听数据变化的实现原理不同
- Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能
- React 默认是通过比较引用的方式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的vDOM的重新渲染。这是因为 Vue 使用的是可变数据,而React更强调数据的不可变。
4)高阶组件
react可以通过高阶组件(Higher Order Components-- HOC)来扩展,而vue需要通过mixins来扩展。
原因高阶组件就是高阶函数,而React的组件本身就是纯粹的函数,所以高阶函数对React来说易如反掌。相反Vue.js使用HTML模板创建视图组件,这时模板无法有效的编译,因此Vue不采用HOC来实现。
Session、Cookie、Token
HTTP 协议是一种无状态协议
,即每次服务端接收到客户端的请求时,都是一个全新的请求。
Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性。
session
客户端请求服务端,服务端会为这次请求开辟一块内存空间
,这个对象便是 Session 对象
接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId。
Cookie
当接收到客户端发出的 HTTP 请求时,服务器可以发送带有响应的 Set-Cookie
标头,Cookie 通常由浏览器存储,然后将 Cookie 与 HTTP 标头一同向服务器发出请求。
HttpOnly 的作用
- 该值指定 Cookie 是否可通过客户端脚本访问。
JOSN.stringify() 深拷贝有什么问题
- undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略
- Date 日期调用了 toJSON() 将其转换为了 string 字符串(Date.toISOString()),因此会被当做字符串处理。
- NaN 和 Infinity 格式的数值及 null 都会被当做 null。
- 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
- 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
VUEX与redux的异同
同:
1、单一数据源
2、数据变化可预测
异:
1、vuex监听数据变化,redux使用diff查看差别
2、vuex修改state,redux通过reduce生成纯函数
深入理解浏览器的缓存机制
1、Service Worker
Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
Service Worker 实现缓存功能一般分为三个步骤:
1、首先需要先注册 Service Worker,
2、然后监听到 install 事件以后就可以缓存需要的文件,
3、那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
2.Memory Cache
主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
3.Disk Cache
读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
Disk Cache 覆盖面基本是最大的。它会根据 HTTP Header 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用
4.Push Cache
Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂
介绍下 webpack 热更新原理,是如何做到在不刷新浏览器的前提下更新页面
1.当修改了一个或多个文件;
2.文件系统接收更改并通知webpack;
3.webpack重新编译构建一个或多个模块,并通知HMR服务器进行更新;
4.HMR Server 使用webSocket通知HMR runtime 需要更新,HMR运行时通过HTTP请求更新jsonp;
5.HMR运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新。
容器中图片的放置方式
contain
contain,将图片保持宽高比缩放到刚好能够放入容器,是最常用的方案。
缺点是不能填充整个容器,因此会产生 “黑边”
cover
cover,图片保持宽高比充满容器,需要图片的宽缩放为容器宽,或图片的高缩放为容器宽,然后多余的内容截断。
fill
fill,图片完全填充容器,不要求保持宽高比,可以对图片进行拉伸。
常见的为元素和伪类
一、标记状态的伪类:
:link,选取未访问过的超链接元素。
:visited,选取访问过的超链接元素。
:hover,选取鼠标悬停的元素。
:focus,选取获得焦点的元素。
二、筛选功能的伪类:
-
:disabled,选取禁用的表单元素。
-
:first-child,选取当前选择器下第一个元素。
-
:last-child,和 first-child 相反,选取当前选择器下最后一个元素。
-
:empty,选取没有子元素的元素。
伪元素选择器
-
::first-line,为某个元素的第一行文字使用样式。
-
::first-letter,为某个元素中的文字的首字母或第一个字使用样式。
-
::before,在某个元素之前插入一些内容。
-
::after,在某个元素之后插入一些内容。
-
::selection,对光标选中的元素添加样式。
react Hook 更新优化
所有hooks公用一套逻辑
1、初次渲染执行一次
2、重新渲染浅比较以来数组,无变化不执行
引用类型在浅比较有问题,需要保持引用不变
useMemo:缓存组件内部的函数返回值
-
组件初次渲染时,执行一次
computeFn
,把函数返回值缓存起来。 -
组件重新渲染时,通过浅比较检查依赖数组
dependencies
有没有变化。如果没有,不重复执行computeFn
,而是直接返回之前缓存的结果。
useCallback:缓存组件内部的回调函数
缓存的是函数本身以及它的引用地址
useCallback(callBackFn, deps) useMemo(() => callBackFn, deps)
React.memo:缓存组件的渲染结果
const MemoComponent = React.memo(Component, areEqual)
useEffect 与 useLayoutEffect 执行时机
1、useEffect 的回调函数是【异步宏任务】,在下一轮事件循环才会执行。
2、 useLayoutEffect 与 componentDidMount、componentDidUpdate 生命周期钩子是【异步微任务】,在渲染线程被调用之前就执行。
hook原理
react 16.18.0 版本引入 fiber 架构,实现异步可中断更新。
先把 vdom 树转成 fiber 链表,然后再渲染 fiber。