开发者社区> aly_miya> 正文

无缝切换?从Vue到React

简介: 本文主要分析Vue和React在开发上的区别,帮助Vue开发者快速上手React,同时也适用于前端新手入门React。
+关注继续查看

本文主要分析Vue和React在开发上的区别,帮助Vue开发者快速上手React,同时也适用于前端新手入门React

单文件组件 VS class组件 VS 函数组件

Vue: 单文件组件

<template>
  <div>{{ greeting }} world</div>
</template>

<script>
  export default {
    data() {
      return {
        greeting: 'hello'
      }
    }
  }
</script>
<style>
</style>

React: Class组件

class Comp extends Component {
  constructor(props) {
    super(props);
    this.state = {greeting: 'hello'};
  }
  
  render() {
    return (
      <div>
        <div>{ greeting } world</div>
      </div>
    );
  }
}

官方文档

React: 函数组件(推荐)

在Vue单文件组件和React的Class组件中,我们的元素、数据变量等必须放到固定的位置,以一种固定的格式书写,而在函数组件中书写方式变得更简单,我们可以像写函数一样写组件。更重要的是,这样就不用关心那些难理解的this

const Comp = () => {
  const [greeting, setGreeting] = useState('hello');
  
  return (
    <div>
      <div>{ greeting } world</div>
    </div>
  )
}

官方文档

双向绑定 VS 单向数据流

在Vue中我们使用v-bind、v-modal对数据进行绑定,无论是来自用户操作导致的变更,还是在某个方法里赋值都能够直接更新数据,不需要手动进行update操作。

this.data.greeting = "Hello"

而在React里需要调用set方法更新,当React感应到set触发时会再次调用render对dom进行刷新,虽然有些麻烦但这种方式可以让数据变化更加清晰易追寻。

this.state.greeting = "Hello" // 错误写法

this.setState({greeting: 'Hello'}); // 正确写法✅
setGreeting('Hello'); // 来自hooks的set写法 后面会介绍

React的大buff:JSX

初次接触JSX的开发者可能会感觉JSX结构混乱,因为你可以在dom元素内部直接写js逻辑,也可以在js逻辑里写dom,这就像把html和js混在一起:

import getUserType from './getUserType'

const Comp = () => {
  const [greeting, setGreeting] = useState('hello');
  
  const Button = () => {
    const userType = getUserType()
    
    if(userType === 0) {
      return <button>去购买</button>
    }   
    
    if(userType === 1) {
      return <button>去充值</button>
    } 
    
    return null
  }
  
  return (
    <div>
      <div>{ greeting } world</div>
      {Button()}
    </div>
  )
}

虽然元素和逻辑的边界模糊了,但我们的组件会变得更加灵活,这样能够将一个组件分成不同的模块,当需要修改是时我们只需关注对应的函数,不用担心影响到其他部分,这对复杂的页面结构非常有用。

Hooks

是什么

上面我们在讲数据流的时候有提到,处理数据的两种方式

// 方式1
this.state = {greeting: 'Hello'}
this.setState({greeting: 'Hello'}); 

// 方式2
const [greeting, setGreeting] = useState('hello');
setGreeting('Hello');

其中第二种方式的useState就是Hooks中的一种,是比较常用的Hook,除此之外还有useEffect,useRef等,每个都有着不同的功能。

为什么用

逻辑独立

以数据更新为例,简单来讲,如果不用Hooks,每次更新数据都用setSate,我们的代码里就会出现很多setState调用,setState根据入参可以一次修改一个字段,也可以一次修改多个字段,想要知道某个数据在哪里被做了怎样的修改就会变的很麻烦,甚至可能不小心多写一个字段修改了不该修改的数据。而用Hooks的useState的话,因为它在定义时会对字段创建其专用的修改函数,所以只要有这个函数的调用,就代表这个字段被做了修改。

怎么用

常用Hooks(Hooks只能在的函数组件内使用):

1.useState: 用于定义组件的 State,相当于this.state=xxx或者Vue里的data(){return xxx}

const [greeting, setGreeting] = useState('hello'); // greeting 默认 hello

// 点击greeting变为Hello1
<div onClick={setGreeting('Hello1')}>{greeting}</div> 

2.useEffect: 通过依赖变更触发的钩子函数 ,类似Vue的watcher

// 当userId变化时调用refresh
useEffect(() => {
  refresh();
}, [userId]);

// 进入页面时会执行init, 退出时会执行destroy
useEffect(() => {
  init();
  
  return () => {
     destroy()
  }
}, []);

3.useRef: 返回ref对象,.current可以获取到其原生的元素本身

const el = useRef(null);

<div ref={el}></div>

// console.log(el.current.offsetHeight) 返回div的offsetHeight

状态管理

是什么?为什么用?

官方定义:“集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化”。
举个例子,页面里两个组件需要展示/更新userName,如果不使用状态管理,我们可以用父子组件交互的方式把userName字段和setUserName函数作为组件参数传入两个组件中,调用setUserName即触发page更新userName:
例1.jpg

但当业务变得越来越复杂,就会陷入透传地狱!
例2.jpg

加入状态管理后,不在涉及组件之间的参数传递,数据管理全部放在Store中管理,组件直接从Store中读取数据,修改时调用Store的修改函数修改对应数据
例3.jpg

怎么用

Vue:Vuex

在Vue中,官方脚手架自带Vuex为我们注入好了Store,常用的state负责定义数据,mutations负责修改数据,actions负责利用mutations做一些复杂异步操作(如接口请求)

// store.js
import { createStore } from 'vuex'
const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    setCount (state, value) {
      state.count = value
    }
  },
  actions: {
    addon ({ commit, state }) {
      const count = state.count
      commit('set', count+1)
    }
  }
})
// index.js
import App from './vue'
import { createApp } from 'vue'

const app = createApp(App).mount('#app');

// 将 store 实例作为插件安装
app.use(store)

// index.vue
<template>
  <div>{{ this.$store.state.count }} world</div>
</template>

<script>
  export default {
    methods: {
      increment() {
        this.$store.commit('setCount', 10)
        this.$store.dispatch('setCount')
        console.log(this.$store.state.count)
      }
    }
  }
</script>
<style>
</style>

React:不止是Redux

React本身并不带状态管理,状态管理对于React更像是一种普通的第三方工具,工作中不同项目可能用了Redux、mobx、rematch等不同的状态管理工具,不同工具写法会有所区别,使用者要自己区分学习,除此之外一些脚手架会自带状态管理,写法会简单些,比如Rax,为方便理解接下来以Rax的写法进行说明。

与上面所说的Vuex的state、mutations、actions对应,React里叫做state、reducers、effects。state负责定义数据,reducers负责修改数据,effects负责利用reducers做一些复杂异步操作,下面示例解释的更清楚:

// src/pages/Dashboard/models/counter.ts
const delay = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));

export default {
  // 定义 model 的初始 state
  state: {
    count: 0
  },
  // 定义改变该模型状态的纯函数
  reducers: {
    increment(prevState) {
      return { count: prevState.count + 1 };
    },
  },
  effects: (dispatch) => ({
    async incrementAsync() {
      await delay(10);
      dispatch.counter.increment();
    },
  }),
}
// src/pages/Dashboard/store.ts
import { createStore } from 'rax-app';
import counter from './models/counter';

const store = createStore({ counter });

export default function Dashboard() {
  // 使用 counter 模型
  const [counterState, counterDispatchers] = store.useModel('counter');

  return (
    <>
      <span>{counterState.count}</span>
      <button onClick={counterDispatchers.increment}>+</button>
      <button onClick={counterDispatchers.incrementAsync}>+</button>
    </>
  );
}

React代码实战:开发一个TodoList

// index.jsx
import $i18n from '@alife/panda-i18n';
import React, { useCallback } from 'react';
import { connect } from 'react-redux';
import { Link } from '@ice/router';
import PropTypes from 'prop-types';
import { Form, Input } from 'cn-next';
import styles from './index.module.scss';

const FormItem = Form.Item;

const AddTodo = (props) => {
  const { onAdd } = props;
  const onSubmit = useCallback(
    (values, errors) => {
      if (!errors) {
        onAdd(values.text);
      }
    },
    [onAdd],
  );

  return (
    <div x-class={[styles.add]}>
      <Form className={styles.addForm} inline onSubmit={onSubmit}>
        <FormItem
          className={styles.addItem}
          required
          requiredMessage={$i18n.get({
            id: 'EnterAToDoList.other',
            dm: '请输入待办事项',
          })}
        >
          <Input
            name='text'
            placeholder={$i18n.get({
              id: 'EnterAToDoList.other',
              dm: '请输入待办事项',
            })}
          />
        </FormItem>
        <Form.Submit className={styles.addSubmit} onClick={onSubmit} validate>
          {$i18n.get({ id: 'Add.other', dm: '添加' })}
        </Form.Submit>
      </Form>
    </div>
  );
};

AddTodo.propTypes = {
  onAdd: PropTypes.func,
};

AddTodo.defaultProps = {
  onAdd: () => {},
};

const Todos = (props) => {
  const { list, createAsync } = props;

  // 添加
  const onAddTodo = useCallback(
    async (text) => {
      await createAsync(text);
    },
    [createAsync],
  );

  return (
    <div className={styles.todos}>
      <AddTodo onAdd={onAddTodo} />
      <div className='mb-30'>
        {list.map((item) => {
          return (
            <div key={item.text} className={styles.todo}>
              <span>{item.text}</span>
            </div>
          );
        })}
      </div>
      <div>
        <Link to='/'>
          {$i18n.get({ id: 'ReturnToHomePage.other', dm: '返回首页' })}
        </Link>
      </div>
    </div>
  );
};

Todos.propTypes = {
  list: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
  createAsync: PropTypes.func.isRequired,
};

const mapState = (state) => ({
  list: state.todos.list,
});

const mapDispatch = (dispatch) => ({
  createAsync: dispatch.todos.createAsync,
  doneAsync: dispatch.todos.doneAsync,
  undoneAsync: dispatch.todos.undoneAsync,
});

export default connect(mapState, mapDispatch)(Todos);
// todo.js
export const todos = {
  state: {
    list: [
      {
        text: 'Learn typescript',
        done: true,
      },
      {
        text: 'Try immer',
        done: false,
      },
    ],
  },
  reducers: {
    create(state, text) {
      state.list.push({ text, done: false });
      return state;
    },
    done(state, idx) {
      if (state.list[idx]) {
        state.list[idx].done = true;
      }
      return state;
    },
    undone(state, idx) {
      if (state.list[idx]) {
        state.list[idx].done = false;
      }
      return state;
    },
  },
  effects: (dispatch) => ({
    async createAsync(payload) {
      // 模拟异步操作
      await new Promise((resolve) => setTimeout(resolve, 250));
      dispatch.todos.create(payload);
    },
    async doneAsync(payload) {
      // 模拟异步操作
      await new Promise((resolve) => setTimeout(resolve, 250));
      dispatch.todos.done(payload);
    },
    async undoneAsync(payload) {
      // 模拟异步操作
      await new Promise((resolve) => setTimeout(resolve, 250));
      dispatch.todos.undone(payload);
    },
  }),
};

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【前端架构】Angular,React,Vue那个是2023的最佳选择?
【前端架构】Angular,React,Vue那个是2023的最佳选择?
20 0
【前端架构】从 JQuery 到 React、Vue、Angular——前端框架的演变及其差异
【前端架构】从 JQuery 到 React、Vue、Angular——前端框架的演变及其差异
12 0
「前端架构」React,Angular还是Vue,太难选了?看完秒懂。
「前端架构」React,Angular还是Vue,太难选了?看完秒懂。
13 0
「前端架构」React和Vue -CTO的选择正确框架的指南
「前端架构」React和Vue -CTO的选择正确框架的指南
8 0
「前端框架」哪个框架更好:Angular,React,Vue
「前端框架」哪个框架更好:Angular,React,Vue
10 0
【前端架构】Angular,React,Vue哪个是2021的最佳选择
【前端架构】Angular,React,Vue哪个是2021的最佳选择
14 0
「前端架构」React,Angular和Vue:哪一个最好,我选Angular
「前端架构」React,Angular和Vue:哪一个最好,我选Angular
9 0
如何使用depcheck检查vue和react的依赖,以后不用把时间浪费在依赖问题上了
当我们在开发 JavaScript 项目时,会引入各种依赖库。但是有些依赖库可能只用到了部分功能,或者已经不再需要了,但是却一直被保留在项目中。
15 0
为什么我们放弃了Vue?Vue和React深度比较
我使用Vue和React已经很长一段时间了,两个框架上实践代码量都在10万行以上。不得不说都是都很不错的,帮助开发者减少很多工作量,某种框架是现代化Vue和React在两者之间的选择并不像选择苹果或香蕉一样简单,两者在工程实践上的差异让我们逐渐放弃了Vue。此处以不一样的角度对彼此进行深度对比。
59 0
前端动画必知必会:React 和 Vue 都在用的 FLIP 思想实战。
在 Vue 的官网中的过渡动画章节中,可以看到一个很酷炫的动画效果
23 0
+关注
aly_miya
前端开发工程师
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
Vue.js在前端服务化上的实践与探索
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
相关实验场景
更多