# 拖动元素 React vs Vue

前言

在学习react hooksvue 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);
});

而且本身就提供类似onMountedonBeforeUnmount这样的生命周期 api。更方便理解和开发。

# 总结

个人体检,vuecomposition api结合了react的优点,同时又剔除它的缺点,在使用和代码理解层面上比react更胜一筹。

Last Updated: 12/16/2020, 4:57:57 PM