Vue开发者如何上手React

792 阅读7分钟

Vue和React是什么?

引用官方文档Vue.js的介绍:

Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

引用React官方博客 《我们为什么要构建 React?》

React 是一个用于构建可组合用户界面的库。 它鼓励创建那些用于呈现随时间变化数据的、可复用的 UI 组件。

从上面知道他们两者实际上都是在处理视图层的问题。

我们思考一下,如果没有React和Vue,那么在一个传统的 JavaScript 应用中,我们需要观察数据发生了什么变化, 并且为了让 DOM 保持最新的状态还必须对它进行更改。 比如:通过JQuery来获取到对应的DOM然后修改该节点的值等。

Vue和React给出的解决方式:胶水层 + 响应式更新

明确两者实际都是处理同一中问题,并选择了是同一个方向的解决方式,那么所谓的差异就可以局限到两者是如何实现所谓的胶水层+响应式更新了。

Vue的胶水层与响应式更新

举个官网的Demo的例子。

new Vue({
  el: '#app-5',
  template:`
  <div id="app-5">
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反转消息</button>
  </div>
`
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

image-20210311132048575

那上面的template就是本文特指的胶水层的具体体现,data就是相应式更新的具体体现。

image-20210311133155168

在实际开发中我们不再直接接触所谓的HTML+CSS了,即真实DOM,都是在tempalte里面书写UI逻辑。

通过这个胶水层,即teample的内容,Vue就能感知数据的变化,即tempalte里依赖的data数据,就会自动进行真实的DOM操作,从而达到视图更新。

image-20210311134141238

当一个对象是Vue.data这种方式来定义的时候,它就应该不再普通的对象,而是上图里面的data,即响应式数据,所以我们对他的操作,已经不再是简单的“JS操作”,即getter/setter,而是经过来Vue这个代理商,那Vue所做的事就是:某个数据变化了,依赖与这个数据的tempalte应该更新了。

image-20210311135121512

具体下来就是,当我们在Vue.data里面定义数据时,Vue2通过Object.defineProperty获取setter/getter操作,从而实现datatemplate的绑定及监听数据的变化。

实际开发过程中,我们是不会看到代理的存在的,即

image-20210311163708704

所以,开发起来,一起都那么简单,并符合直觉。

React的胶水层与响应式更新

React的团队可能是觉得探索一种成熟的简单的方式,没那么好玩,所以他们选择了另一个探索性(坑多)的思路。

image-20210311165344020

举个和上面一样的例子

import { useState } from "react";
import ReactDOM from "react-dom";

function App() {
  const [message, setMessage] = useState("Hello React.js!");
  const reverseMessage = () => {
    setMessage(message.split("").reverse().join(""));
  };
  return (
    <div className="App">
      <p>{message}</p>
      <button onClick={reverseMessage}>反转消息</button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

image-20210311142801899

我们可以把React中return出的JSX等同于Vue中的tempaltesetState所做的事就是Vue中在data里定义一个响应式变量。

Vue的响应式数据只需要在Vue.data中定义,对开发者而言,这个对象已经是响应式数据了,所以我修改他,他就应该自己更新到视图中。

image-20210311143053513

而React就比较麻烦了,==React中并没有响应式数据==,就算是通过useState获取的message变量,它还是一个普普通通的变量,所以被修改了,就是简单的被修改,并不会反映到视图中,达到响应式更新。

image-20210311143239548

React的思路是:如果你要让一个变量的变化同步到视图中,你可以使用useState方法来定义一个变量,但它并不监听这个变量的变化过程,当用户想要去更新到视图中时,请调用该变量的setter函数(即例子中的setMessage),并传递这个变量具体的值(即:message.split("").reverse().json("")),那React就拿到这个数据重新绘制(重新执行函数组件)一遍。

image-20210311165400475

这是什么感觉呢?拿房子的设计稿和房子举例

image-20210311150350998

房子已经根据设计稿已经造好了,但是我觉得不满意,我想改一下房间的形状。

image-20210311151117135

当这个工人是Vue的时候,==他是一个把顾客当上帝的好工人==,他发现顾客把设计图中的房间改了,作为一个好工人,不需要顾客来告知,他就立马!主动!快速!地去==把这个房间拆了==,然后造个圆形的房间出来。

当这个工人是React的时候,他是有自己生活的人,所以工作只是他生活的一部分。顾客怎么改设计图,他都不关心,当用户改好了,定稿了,顾客来找他的时候,他才开始去看设计稿。而且他不想和顾客多说一句话,并告诉顾客:你图怎么样,我就造得怎么样,如果不确定的话,你就回去确定好了再来。如果用户确定了设计稿,那他转身就把==房子全拆了==,根据最新的设计稿来重新建一个房子。那么房间的修改一定是符合用户的预期的。

对应到代码中就是:

  • 设计稿就是:胶水层(tempalte、JSX)
  • 房子就是:真实DOM
  • 房间就是:data

在Vue里面我们修改datatemaplte就感知到了,Vue就会主动去修改真实的DOM

在React中我们要修改state时,需要显示地调用setState来告知React去更新视图,并且需要传递一个具体的值。

所谓的==房子全拆==了,是表示React把Function Component重新执行一遍。我们可以简单的实现一个useState来模拟这个过程,以便更好的理解

import ReactDOM from "react-dom";

let memoizedState;

function useState(initialState) {
  memoizedState = memoizedState || initialState;
  
  function setState(newState) {
    memoizedState = newState;
    render(); // 告诉React工人去改房子
  }
  return [memoizedState, setState];
}

// 设计稿
function App() {
  const [message, setMessage] = useState("Hello React.js!");
  const reverseMessage = () => {
    setMessage(message.split("").reverse().join(""));
  };
  return (
    <div className="App">
      <p>{message}</p>
      <button onClick={reverseMessage}>反转消息</button>
    </div>
  );
}

function render() {
  // 根据设计稿 从零开始造房子
  ReactDOM.render(<App />, document.getElementById("root"));
}
render();

当用户在JSX中使用函数组件时,即<App/>,React会去执行这个函数组件,即function APP(){...}以获得return返回的值。

所以,每次render的时候,function App都会去执行,这就像原本建好的房子,全部不好,从零开始造的感觉。

实际React代码中,会使用两棵树进行对比,进行复用

至此,我们可以把React的响应式思路理解成:通过重新执行函数获取最新的结果,拿到结果后的React再去更新到真实DOM中,即视图更新。

有了这个思维后,我们再去看平时为什么

  • 写出无限渲染的组件时,就知道一定是多个setXXX造成了问题了。
  • 为啥那么卡,是因为每次函数都重新执行了,我们在这个函数里面定义了过多的操作,需要去优化,这就是useEffect或者useCallback去告诉React,这个地方你重新执行函数的时候,请不要重复操作。

具体如何使用useEffectuseCallback之类的,请阅读React Hooks最好的资料:Hook 概览

题外话:Vue3和React hooks

Vue3的 Reactivity 真的贼他妈的好用呀!关于说抄袭啥的,都一模一样为啥不写React?那真的是,口嗨罢了!就算是Vue3的写法,具体的实现和理念也是上文所说的。

唯一的缺点可能就是:

  • 原本的代理那层很无感,现在变得很有感了,所以有点不那么符合直觉。但是这要看从什么维度去思考了:我觉得现在这样就挺好,各有利弊吧。就像那个refs提案啥的。
  • API真的多多,求求尤雨溪做个人吧,别把开发者惯坏了,API多是有好有坏,但是!真的别出简写了,真的,有些API确实是提高生产效率,我真的能理解你是为了开发者,但简写这东西真他妈的茴香豆有几种写法,个人效率提升1S,逼格提升两分,团队的每个人都需要去看Vue文档才会发现:卧槽,还能这么写。。。。
  • Vuex。。。一言难尽。官方换个思路吧,我觉得用Reactivity都比他好十倍,具体怎么用Reactivity来状态管理到时再写一篇文章