介绍项目
深拷贝与浅拷贝(针对于数组,对象这种引用数据类型)
浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间,当内存销毁的时候,指向这片内存的几个指针需要重新定义才可以使用,要不然会成为野指针。
浅拷贝就是拷贝指向原来对象的指针,使原对象的引用计数+1,可以理解为创建了一个指向原对象的新指针而已,并没有创建一个全新的对象。
浅拷贝的方法有:
1)展开运算符...
2)Object.assign()es6
3)arr.slice() 数组slice()方法可以从已有数组中返回选定的元素:用法:
array.slice(start, end)
,该方法不会改变原始数组。该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。4)arr.concat()方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
5)手动实现浅拷贝
function shallowCopy(object) { // 只拷贝对象 if (!object || typeof object !== "object") return; // 根据 object 的类型判断是新建一个数组还是对象 let newObject = Array.isArray(object) ? [] : {}; // 遍历 object,并且判断是 object 的属性才拷贝 for (let key in object) { if (object.hasOwnProperty(key)) { newObject[key] = object[key]; } } return newObject; }
深拷贝的方法有:
1)使用 JSON 序列化和反序列化(适用于数据为简单对象、数组)
JSON.parse(JSON.stringify(originalObject)) 拷贝的对象中如果有函数,undefined,symbol,当使用过
JSON.stringify()
进行处理之后,都会消失。2)lodash库的_.cloneDeep方法
3)递归手动实现深拷贝
// 深拷贝的实现 function deepCopy(object) { if (!object || typeof object !== "object") return; let newObject = Array.isArray(object) ? [] : {}; for (let key in object) { if (object.hasOwnProperty(key)) { newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key]; } } return newObject; }
深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。
深拷贝就是拷贝出和原来仅仅是值一样,但是内存地址完全不一样的新的对象,创建后和原对象没有任何关系。
本质区别在于:
- 是否开启新的内存地址
- 是否影响内存地址的引用计数
赋值情况:
浅拷贝情况:
深拷贝情况:
useRef
1、什么是useRef
- 返回一个可变的
ref
对象,该对象只有个.current
属性,初始值为传入的参数(initialValue
)。 - 返回的
ref
对象在组件的整个生命周期内保持不变。 - 当更新 current 值时并不会
re-render
,这是与useState
不同的地方。所以说如果有render内需要渲染的数据肯定不能使用useRef ,而得用useState
。 - useRef 类似于类组件的 this。
通俗点说useRef
就像是可以在 .current
属性中保存一个可变值的“盒子”。
2、使用
与类组件中的 this.groupBodyRef = React.createRef(),他们大部分用法基本一致,都是可以存
变量
或者Dom节点
useRef与createRef的区别
在一个组件的正常的生命周期中可以大致分为3个阶段:
1、从创建组件到挂载到DOM阶段。初始化props以及state, 根据state与props来构建DOM
2、组件依赖的props以及state状态发生变更,触发更新
3、销毁阶段
第一个阶段,useRef与createRef没有差别第二个阶段,createRef每次都会返回个新的引用;而useRef不会随着组件的更新而重新创建
第三个阶段,两者都会销毁
2.1 、我的项目中使用到useRef的场景:
1、用来存储当前操作改动的是哪一行数据
具体实现:
const currentSelectedRowIndex = useRef<number>();
<a
style={{marginLeft: '20px'}}
onClick={() => {
selectService();
currentSelectedRowIndex.current = name;
}}
>
选择服务
</a>
2、用于初始化echarts图表
import React, {useEffect, useRef} from 'react';
import * as echarts from 'echarts';
interface IProps {
title?: string;
dateTrend: any;
trendData: any;
}
const Chart = (props: IProps) => {
const {title, dateTrend, trendData} = props;
const minData = trendData.reduce((min: number, item: any) => {
const itemMin = Math.min(...item.data.filter((val: number) => val !== null));
return itemMin < min ? itemMin : min;
}, Infinity);
let max = Number.MIN_SAFE_INTEGER;
for (const item of trendData) {
const itemMax = Math.max(...item.data.filter((item: number) => item !== null));
max = Math.max(max, itemMax);
}
const chartRef: any = useRef();
let options: any = {};
if (dateTrend?.length === 0) {
options = {
title: {
text: '暂无数据',
x: 'center',
y: 'center',
textStyle: {
fontSize: 14,
fontWeight: 'normal'
}
}
};
} else {
options = {
title: {
text: title,
left: '50px',
top: '4px'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: trendData?.map((item: any) => item.name),
type: 'scroll'
},
xAxis: {
type: 'category',
data: dateTrend
},
yAxis: {
type: 'value',
min: minData,
max: max,
splitLine: {
lineStyle: {
type: 'dashed'
},
show: true
}
},
series: trendData
};
}
useEffect(() => {
const chart = echarts.init(chartRef.current);
// 设置图表实例的配置项和数据
chart.setOption(options);
return () => {
echarts.dispose(chart);
};
}, [trendData, dateTrend]);
return (
<>
<div style={{height: '400px'}} ref={chartRef}></div>
</>
);
};
export default Chart;
2.2、还知道的可以这样使用
import React, { useState, useRef, useCallback } from 'react'
export default function StopWatch() {
const [now, setNow] = useState(Date.now())
const ref = useRef()
const handleStart = useCallback(() => {
ref.current = setInterval(() => {
setNow(Date.now())
}, 1000)
}, [])
const handleStop = useCallback(() => {
clearInterval(ref.current)
}, [])
return (
<>
<h1>Now Time : {now}</h1>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</>
)
}
使用
ref
存储setInterval
返回的ID,需要清除时,我们只需要clearInterval(ref.current)
就可以了
webpack的配置和相关内容
强缓存和协商缓存
强缓存是指浏览器在请求资源时,直接从本地缓存中获取资源,不发送请求到服务器。这样可以减少网络延迟并提高页面加载速度。
实现强缓存的方式:
-
Expires 头部(HTTP1.0使用):服务器返回的响应头中包含一个 "Expires" 字段,表示资源的过期时间,浏览器会根据这个时间判断是否使用缓存。
-
Cache-Control 头部(HTTP1.1及之后使用):使用 "Cache-Control" 头部来控制缓存行为,常用的指令包括 "max-age"、"no-cache"、"no-store" 等。
max-age:单位是秒,用于指定资源在缓存中的最大存活时间。当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
no-cache:不使用本地缓存。跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的
no-store:禁止浏览器缓存数据,也禁止保存至临时文件中,每次都重新请求。没有所谓的强缓存、协商缓存了。
public:任何情况下都缓存。
private:只能被终端用户的浏览器缓存。
协商缓存是指浏览器在发起请求时,通过与服务器进行协商来确定是否使用缓存。如果缓存未过期,服务器会返回 304 状态码,告知浏览器继续使用缓存;如果缓存过期或者需要验证,服务器会返回新的资源。
整体步骤:
1、将所缓存的资源信息发送给服务器
2、让服务器判断资源是否已经更新
- 若已更新,则返回更新后的资源
- 若没有更新,返回304状态,告诉浏览器可直接使用本地缓存的资源
整个过程至少与服务器通信一次。
实现协商缓存的方式:
-
Last-Modified (响应头)和 If-Modified-Since(请求头):服务器会在响应头中返回资源的最后修改时间,当浏览器再次请求资源时,会发送一个 "If-Modified-Since" 头部字段,服务器会根据资源的修改时间判断是否需要返回新的资源。
1. 浏览器第一次发请求,服务器在返回的 respone 的 header 加上 Last-Modified,表示资源的最后修改时间
2. 再次请求资源,在 requset 的 header 加上 If-Modified-Since ,值就是上一次请求返回的 Last-Modified 值
3. 服务器根据请求传过来的值判断资源是否有变化,没有则返回 304,有变化就正常返回资源内容,更新 Last-Modified 的值
4. 304 从缓存加载资源,否则直接从服务器加载资源
2.ETag (响应头)和 If-None-Match(请求头):服务器会给资源生成一个唯一的标识符(ETag),浏览器再次请求资源时会带上 "If-None-Match" 头部字段,服务器会根据 ETag 判断资源是否更新。一个标识符字符串,表示文件唯一标识,只要文件内容改动,ETag就会重新计算。缓存流程和 Last-Modified 一样。
Last-Modified 与 Etag 的对比:
- 如果我们打开文件但并没有修改其内容,Last-Modified 也会改变,而Etag则不会改变。
- Last-Modified 的时间单位为秒,如果一秒内对文件进行了多次修改,那么由于其时间单位是秒,所以Last-Modified不会改变,最终浏览器还是会去读取缓存资源,而此时缓存的资源已经过时了。
- Etag的优先级高于Last-Modified。
浏览器缓存过程(强缓存转协商缓存的过程)
1.浏览器第一次加载资源,服务器返回200,浏览器将资源文件从服务器上请求下载下来,并
把文件、文件的返回时间、response header一并缓存
;2.下一次加载资源时,查看
cache-control
的设置
- 如果值是
no-cache
,则表示不缓存,则直接去请求数据;- 如果值是
max-age
,则比较当前时间和上一次返回200时的时间差
是否大于max-age,若小于max-age,则表示没有过期,命中强缓存,不发请求直接从本地缓存读取该文件;如果时间差大于max-age,则表示资源过期了,则向服务器发送请求,并且header中携带有If-None-Match、If-Modified-Since、Etag值等
3.如果服务器收到的请求头中,有Etag值,
优先根据Etag的值
判断被请求的文件有没有做修改,
如果Etag值一致则没有修改,命中协商缓存,返回304,则读取缓存资源;
如果Etag值不一致则有改动,则直接返回新的资源,并带上新的Etag值;4.如果服务器收到的请求头中,没有Etag值,则将
If-Modified-Since
和被请求文件的最后修改时间
做比对,
如果一致则没有修改,命中协商缓存,返回304;
如果不一致则有修改,则返回新的文件和并在响应头中携带last-modified
,下次请求时通过If-Modified-Since
携带上last-modified
的值;
自定义hooks
类组件和函数组件的区别
类组件:
class定义
有this
有生命周期函数
函数组件:
function定义
hooks:useState useEffect, useCallback, useMemo, useRef