typeof原理
JS底层存储变量会在机器码低位1-3位存储类型信息,typeof也是据此判断的,巧合的是null和object的这三位都是000,因此typeof null == typeof {} == ‘object’。
Chrome80对SameSite的使用
之前写过Chrome80更新,很重要的一点改动就是对sameSite的改动,之前samesite默认是使用none的,Chrome80之后,这个默认值改为了lax(宽松),也就是说,之前设置为None时,无论是否跨站,默认请求都会携带cookie,但是修改之后,跨站是不会携带cookie的。
跨站与同源(跨域)不同,只需要判断顶级域名和二级域名,如http://a.t.com和https://b.t.com,我们可以认为它是同站。而设置为Lax之后,除去get 方法提交表单况或者a 标签发送 get 请求之外,很多跨站方式并不会携带请求。
Promise封装Ajax
之前被问道一次,也没让写,这里贴出来:
function Ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.responseType = "JSON";
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && /^[23]\d{2}$/.test(xhr.status)) {
let res = xhr.responseText;
resolve(JSON.parse(res));
}
};
xhr.onerror = function(err) {
reject(err);
};
xhr.send();
});
}
很可能会忘了parse,那样的出来的是字符串。
JSONP
原理很简单,利用了script便签不受同源协定的约束,把要返回的值序列化放到callback后面拼成一个立函数执行形式,然后在客户端window对象添加一个对应的函数,在这儿函数种对服务端返回的响应包裹的数据进行处理:
function jsonp({url,params,callback}){
return new Promise((resolve, reject) => {
let script = document.body.createElement('script')
params = JSON.parse(JSON.stringify(params))
let arr = []
for(let key in params){
arr.push(`${key}=${params[key]}`)
}
arr.push(`callback=${callback}`)
script.src = `${url}?${arr.join("&")}`;
document.appendChild(script)
window[callback]=function(data){
if(!data) reject('failed')
resolve(data)
document.body.removeChild(script)
}
})
}
简单认识websocket
之前对webSocket的简单了解就是实现了服务器主动推送,毕竟在此之前,我们实现推送都是通过轮询的方式实现的。
首先,WebSocket建立在 TCP 协议之上,与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP协议,同时它的数据格式比较轻量,性能开销小,通信高效。可以发送文本,也可以发送二进制数据。
还有一点它不受没有同源限制,客户端可以与任意服务器通信。协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
HTTP握手后,WebSocket基于HTTP协议进行升级,服务端返回101状态码标识协议切换,后序的数据交互都按照新的协议进行。建立连接后,后续的操作都是基于数据帧的传递。
两种解决Effect错误依赖的方法
看个Demo:
export default function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
console.log("in");
setCount(count + 1);
}, 1000);
return () => {
console.log("out");
clearInterval(id);
};
}, []);
return <h1>{count}</h1>;
}
这里,我们依赖为空,只会渲染一次,setInterval导致"in"每隔1秒会输出,而UseEffect并不会重复执行,因此每次我们的count为0,+1,返回的总是1。
解决1:
export default function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
console.log("in");
setCount(count + 1);
}, 1000);
return () => {
console.log("out");
clearInterval(id);
};
}, [count]);
return <h1>{count}</h1>;
}
但是这样只会使得我们每次都要删除定时器再添加,不符合我们的需求。
解决2:
export default function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
console.log("in");
setCount(c=c+ 1);
}, 1000);
return () => {
console.log("out");
clearInterval(id);
};
}, []);
return <h1>{count}</h1>;
}
使用函数式更新state是最好的解决方法。
关于函数依赖,我们可以使用三个方式:
- 把函数写在useEffect种,确保它不依赖组件其他状态。
- 把函数写在组件外
- 使用useCallback包裹。