它更像是踩坑日记。
stack overflow上有相关的解决方案:Why does react-router not works at vercel?
只需要将vercel.json
对应的配置文件改成这样。通过正则,让所有的路径资源的请求都到/
,使路由生效。
{
"routes": [
{
"src": "/[^.]+",
"dest": "/",
"status": 200
}
]
}
不同的显示器有不同的刷新率,使用setInterval(callback, ms)
,60FPS的参数是16.7ms
,而144FPS的参数是7ms
。在实际渲染的过程中还会出现丢帧的情况。[深入理解 requestAnimationFrame]
为了更好的适应不同显示器的帧数,使用window.requestAnimationFrame(callback)
。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次。
基础的文本用markdown-it,代码块的渲染用higtlight.js。
代码在:/src/utils/markdown.ts
。组件在/src/pages/Article
下,样式写得非常的丑,以后有空再改。
这个是用本仓库的issues。知道了组织名、项目名和issue编号,通过GitHub的API来获取也不难。
原本页面上的小球是可以在页面上发送消息的,因为没有将用户的登陆进行身份校验,也没有将用户发送的消息储存下来,所以直接砍掉了。
/src/hooks/useClient
这个hook采用了消息订阅的方法,负责将消息转发给的不同回调。如果服务端传来的消息没有被订阅,那么这个消息将会被忽略。(这个消息订阅实现的时,只允许一个消息对应一个订阅者)
useEffect(() => {
// 连接上服务器,服务器会主动回传。
setDeliverier({ "hello": handleHelloAction });
// 当有人连接上服务器,服务器会向其它用户发送这个消息。
setDeliverier({ "enter": handleEnterAction });
// 当有人发送消息至服务器,服务器会向所有用户广播这个的消息。
setDeliverier({ "talk": handleTalkAction });
// 当有人移动的行为发送至服务器,服务器会向其他用户广播这个消息。
setDeliverier({ "move": handleMoveAction });
// 客户端可以向服务器请求当前在线用户的信息。
setDeliverier({ "stand up": handleStandUpAction });
// 用户下线,服务器会向其他用户广播这个消息。
setDeliverier({ "leave": handleLeaveAction });
}, []);
在/src/component/BallRoom
里以各种handle
开头的函数都有这个问题。这些回调被执行时,都获取不到最新的userList
。这个问题产生的原因是闭包。
那么可以将获取的userList
的方法放在ref里,这样每次这个方法都是最新的,获取到的userList
也是最新的了。
function BallRoom(){
......
const userListRef = useRef();
userListRef.current = () => userList;
......
// 获取的方式
const data = userListRef.current();
}
Ball
组件是一个函数式组件,只能用hook的方法。useImperativeHandle与forwardRef
没空,暂时搁置,所以/src/App.tsx
中的组合 store 也暂时没用。而且现在的 reducer 设计非常有问题。
用简单的话来说:
- 将需要增加这个特效的组件想方设法加入到 redux 里,可以使用
useScrollAnimationRefs
(一个自定义的hook)的第一个参数,用法与 ref 一样。如果该组件无法 ref ,那只能手动添加。/src/pages/Welcome/ContestList.tsx
里只能这样。 - 选择一个容器组件,使用
useScrollHandler
(一个自定义的hook)作为监视器。
在父类中,宽度被设为100%
,高度被设为0
,padding-bottom
属性(外部下边距)被设为75%
。因为当padding-bottom的值为百分比时,百分比计算的基准为父元素的宽。
这样,父类的实际宽高比(包含边距的宽高比)就变为了四比三。
于是子类宽高都100%
、position:relative
在里边撑开就可以了。
这个项目是从 create-react-app 迁移至 vite 的。修改主题色是用新的主题色去替换旧的主题色。(less 全局变量替换)在vue3-vite中配置less的全局变量
需要配置这两个文件。/vite.config.ts
,/src/theme.less
。antd design配置主题
修改 local storage 中的 user
的 avatar
字段,用其它图片的链接替代。
其实单独抽离了一个 Float
组件出来,这个组件里所有子元素都能像小球一样移动。
Float是用类组件的形式写的,写得非常的丑,有空可以重构一下。
可以注意到,小球不随着滚动条的滚动二变化位置,是因为它 position:absolute;
。位置的移动本质上就是 left
和 top
的变化。
浏览器自带有 mousedown
mousemove
mouseup
这三个事件(触摸事件同理),抽象流程图如下。
当有新消息到来,直接显示消息框。在5s后消息框会消失,若5s内又收到新消息,则推迟5s后再消失(防抖)。
export function debounce<T extends (...args: any[]) => void>(func: T, dalay: number) {
let timer: any = null
return (...args: Parameters<T>): void => {
if (!!timer) clearTimeout(timer)
timer = setTimeout(() => {
func(...args)
}, dalay)
}
}
const delaySetContent = debounce(setContent, 5000);
如果是显示器刷新一次就向服务器发送一次的话也太夸张了,这个发送位置信息的函数在16ms内只会被执行一次(节流)。
16ms如果人少的话应该是不会卡的......卡了再往上调
export function throttle<T extends (...arg: any[]) => void>(func: T, interval: number) {
let _args: any = null;
let _timer: any = null;
return (...args: Parameters<T>) => {
_args = args;
if (!_timer) {
_timer = setTimeout(() => {
func(..._args);
_timer = null;
}, interval);
}
}
}
/**
* 拖动自己的小球,将位置信息发送至服务器
*/
const onMoving = throttle((position: Position) => {
if (!unique) return;
const res = { type: "move", data: position, userName: user.name };
client.send(res);
}, 16);
在 /src/component/BallRoom
有用到类似的。
const floatRefs = useRef(new Map());
function floatRef(user: AtomUser) {
return (el) => {
floatRefs.current.set(user.name, el);
}
}
使用的时候,通过 current 取 Map 即可得到对应元素。
floatRefs.current.get(user.name);
在学习方向的介绍这,点击跳转页面回来仍然是现在的高度。
没有把saveScrollTop
写在useEffect
销毁的回调中,为了更方便的自定义。
export function usePageJumpSaveScrollTop() {
useEffect(() => {
const scrollTop = localStorage.getItem("scrollTop");
if (scrollTop) {
localStorage.removeItem("scrollTop");
document.body.scrollTop = ~~scrollTop;
}
}, []);
function saveScrollTop() {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
localStorage.setItem("scrollTop", JSON.stringify(scrollTop));
}
return saveScrollTop;
}