# 拖动元素 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更胜一筹。