【内容梳理】浅谈Vue转React

221 阅读3分钟

1. 前言说明

由于Vue的学习成本低,曲线更加平滑,操心事情少等特点,很受新入坑的小伙伴学习。

很多小伙伴都会深受Vue的思想的影响,在学习React的时候会问出:如何在React中实现VueXXX功能?\color{red}{如何在React中实现Vue的XXX功能?}

当学了React以后,你是不是经常有这样的疑惑:诶?!为啥Vue能实现?为啥React这边实现这么繁琐?\color{red}{诶?!为啥Vue能实现?为啥React这边实现这么繁琐?}

那是因为Vue这个大保姆帮我们做了很多很多。。。

这里就给大家罗列出一些经常用到的功能的实现,便于大家快速适应React开发。

2. 模板语法中的语法糖

2.1 template逻辑块包裹

  • 空标签
<>
    <div className={styles.logTitle}>错误描述</div>
    <ul>
        <li className={styles.logError}>{installErr}</li>
    </ul>
</>
  • Fragment
<Fragment>
    <div className={styles.logTitle}>错误描述</div>
    <ul>
            <li className={styles.logError}>{installErr}</li>
    </ul>
</Fragment>

2.1 v-if/v-else-if

  • 简单判定使用三元表达式
{curAppState === AppInstallStepState.FAILED ? (
  <>
    <div className={styles.logTitle}>错误描述</div>
    <ul>
      <li className={styles.logError}>{installErr}</li>
    </ul>
  </>
) : null}
  • 复杂逻辑写render***函数
  const renderOper = (renderState: AppInstallStepState) => {
    let operTxt;
    let operDom;
    if (isActive) {
      operDom = (
        <>
          <Icon type="check-circle" style={{ color: 'green', fontSize: '50px' }} />
          <div>当前版本</div>
        </>
      );
    } else {
      switch (renderState) {
        case AppInstallStepState.NOT_STARTED: {
          operTxt = '安装';
          break;
        }
        case AppInstallStepState.RUNNING: {
          operTxt = '正在安装';
          break;
        }
        case AppInstallStepState.FAILED: {
          operTxt = '重新安装';
          break;
        }
        case AppInstallStepState.SUCCESS: {
          operTxt = '安装';
          break;
        }
        default: {
          operTxt = '安装';
        }
      }
      operDom = (
        <Button type="primary" disabled={isLocked} loading={renderState === AppInstallStepState.RUNNING} onClick={handleInstall}>
          {operTxt}
        </Button>
      );
    }
    return operDom;
  };
...
...
...
<div>{renderOper(curAppState)}</div>

2.3 v-for

  • 利用map函数既遍历又返回的特点
  <Steps
    current={getStepIndex()}
    progressDot={customDot}
    className={styles.installSteps}
  >
    {stepList.map((curStep: IInstallStepAttr) => (
      <Steps.Step title={curStep.stepName} description="" />
    ))}
  </Steps>

2.4 @onXXX($on) / $emit

  • 通过props传递回调函数
  • 一般约定回调函数使用onXXX的命名方式,提高可阅读性
  • 需要考虑结合useCallback + useMemo来减少不必要的子组件渲染

2.5 :class

classnames插件

  • className结合插件classNames
<div
  className={classNames({
    [style.transferArea]: true,
    [style.vertical]: direction === 'vertical',
  })}
>

2.6 插槽

  • 通过props.children拿到默认插槽
  • 通过props.childrenXXX拿到具名插槽,类似传递props
  • 通过props.childrenXXX拿到作用域插槽,类似传递props,子组件返回JSX.Element,并附带参数

2.7 Computed

  • 使用useMemo实现(非hooks手段目前不知)
  const [message, setMessage] = useState('hello')
  const reversedMessage = useMemo(() => (
    message.split('').reverse().join('')
  ), [message])

2.8 Watch

  • 使用useEffect实现, 第二个参数为监听的对象(数组包裹)
  • 每次都是immediate:true,即初始化就会触发监听
  • 无法做到deep:true,即若监听的是对象,只会观察对象的指针是否发生改变,通常结合immutability-helper来实现对象的监听
useEffect(() => {
    if ([AppInstallStepState.RUNNING].includes(curAppState) && curHistoryId) {
      pollingGetAppProgress(curHistoryId);
    }
}, [curHistoryId]);

2.9 :is

  • v-if一致

2.10 $set

  • React不是自动更新,都是需要调用setXXX的才行
  • 若是更新非基本类型,地址不变时,不会rerender
  • 推荐使用immutibility-helper

2.11 $.nextTick

  • React没有这个概念,这个概念的前提是数据会自动合并更新
  • React的hooks写法,需要自己setState后,配合useEffect使用来达到这个目的
  • 非hooks写法的第三个参数可以做到阻塞执行后面函数中的逻辑

2.12 css隔离

官方不内置,需要自己选择喜欢的插件

  • css modules插件
  • react中采用的是在class上添加hash值来实现css隔离,编译后的class上会有hash值
  • vue中采用的是css渲染时,根据dom上的data-v-xxx来实现css隔离,编译后class上没有hash值
  • 使用css modules无法很好实现v-deep的效果,只能通过:global来进行全局覆盖
  • 个人写的粗略的总结
import styles from './index.module.less';
...
...
...
<Col span={4}>
    <div className={styles.versionTag}>{tagName}</div>
</Col>

2.13 keep-alive缓存组件

官方不内置,需要自己选择喜欢的插件

2.14 vue-router

官方不内置,需要自己选择喜欢的插件

2.15 Vuex

官方不内置,需要自己选择喜欢的插件