# 拖动元素 React vs Vue
前言
在学习
react hooks
和vue composition api
时,感觉两个东西有点相似,但是有很区别.刚好使用了一个简单的例子:可拖动元素
,来分析对比一下两个工具的区别。
# 先看效果
react hooks
vue 3
# 分析
# react hooks
首先是react hooks
,这里写了两个版本。第一个版本使用的是一般的useState
和使用useCallback
包裹的句柄函数,
const [state, setState] = useState({
left: 0,
top: 0,
x: 0,
y: 0,
moving: false,
});
const refBox = useRef();
const onMove = useCallback(
(event) => {
if (!state.moving) return;
const { clientX, clientY } = event;
const dx = clientX - state.x;
const dy = clientY - state.y;
setState({
...state,
left: state.left + dx,
top: state.top + dy,
x: state.x + dx,
y: state.y + dy,
});
},
[state]
);
这里,使用setState
更新state
的时候,不像类组件的this.setState
方法,自动合并要更新的字段,而是需要完整指定新的state
。同时由于新的{ x,y,top,left }
都需要使用前一状态的state
进行计算。因此useCallback
的依赖项state
必须指定,否则无法获取变化后的state
。但是这样会导致后面的问题:
useEffect(() => {
if (!refBox.current) return;
const tempRef = refBox.current;
tempRef.addEventListener("mousedown", onDown);
window.addEventListener("mousemove", onMove);
window.addEventListener("mouseup", onUp);
return () => {
tempRef.removeEventListener("mousedown", onDown);
window.removeEventListener("mousemove", onMove);
window.removeEventListener("mouseup", onUp);
};
}, [refBox, onDown, onMove, onUp]);
初始化的时候,添加事件绑定,使用到的onMove
本来应该是不变的,但是实际上onMove
函数会一直发生改变,原因是它所依赖的state
一直在变化。这样会导致一个性能问题,在拖动元素的时候,会不断地绑定和解绑句柄函数:
useEffect(() => {
console.log("onMove changed");
}, [onMove]);
下面来看第二个版本。使用的是useRef
而不是useState
,然后在所有的句柄函数中移除依赖项:
const state = useRef({ left: 0, top: 0, x: 0, y: 0, moving: false });
const onMove = useCallback((e) => {
if (!state.current.moving) return;
const { clientX, clientY } = e;
const dx = clientX - state.current.x;
const dy = clientY - state.current.y;
state.current.x += dx;
state.current.y += dy;
state.current.left += dx;
state.current.top += dy;
setStyle({ left: state.current.left, top: state.current.top });
}, []);
这样就能保证onMove
等句柄函数一直不发生变化,避免了性能问题。但是由于useRef
的更新不会重新渲染组件,所有又需要使用useState
来做数据的映射。
const [style, setStyle] = useState({ left: 0, top: 0 });
// ...
setStyle({ left: state.current.left, top: state.current.top });
# vue composition api
对于vue3
,数据定义其实差不多,主要是句柄函数的区别:
const onMove = (event) => {
if (!data.moving) return;
const dx = event.clientX - data.x;
const dy = event.clientY - data.y;
data.left += dx;
data.top += dy;
data.x += dx;
data.y += dy;
};
可以看到,句柄中不需要手动指定依赖项,api 中会自动收集依赖。这样看上去很像上文中 react 第二个的写法,但是reactive
又是可以自动重新渲染组件的。另外通过watchEffect
可以看到句柄本身不会发送变化:
watchEffect(() => {
console.log("onMove changed", onMove);
});
而且本身就提供类似onMounted
,onBeforeUnmount
这样的生命周期 api。更方便理解和开发。
# 总结
个人体检,vue
的composition api
结合了react
的优点,同时又剔除它的缺点,在使用和代码理解层面上比react
更胜一筹。