无缝切换?从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);
    },
  }),
};
目录
相关文章
|
8天前
|
JavaScript 前端开发 算法
React 框架和 Vue 框架的区别是什么?
React框架和Vue框架都是目前非常流行的前端JavaScript框架,它们在很多方面存在区别
|
21天前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】React与Vue:前端框架的巅峰对决与选择策略
|
21天前
|
前端开发 JavaScript 数据管理
React与Vue:两大前端框架的较量与选择策略
【10月更文挑战第23天】React与Vue:两大前端框架的较量与选择策略
|
21天前
|
JavaScript 前端开发 算法
在性能上,React和Vue有什么区别
【10月更文挑战第23天】在性能上,React和Vue有什么区别
16 1
|
26天前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
21天前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】 React与Vue:前端框架的巅峰对决与选择策略
|
21天前
|
JavaScript 前端开发 数据管理
React和Vue的优缺点
【10月更文挑战第23天】React和Vue的优缺点
12 0
|
21天前
|
开发框架 JavaScript 前端开发
React和Vue之间的区别是什么
【10月更文挑战第23天】React和Vue之间的区别是什么
14 0
|
21天前
|
JavaScript 前端开发 Android开发
React和Vue之间的区别是什么
【10月更文挑战第23天】React和Vue之间的区别是什么
13 0
|
28天前
|
前端开发 JavaScript API
2025年前端框架是该选vue还是react?有了大模型-例如通义灵码辅助编码,就不用纠结了!vue用的多选react,react用的多选vue
本文比较了Vue和React两大前端框架,从状态管理、数据流、依赖注入、组件管理等方面进行了详细对比。当前版本和下载量数据显示React更为流行,但Vue在国内用户量增长迅速。Vue 3通过组合式API提供了更灵活的状态管理和组件逻辑复用,适合中小型项目;React则更适合大型项目和复杂交互逻辑。文章还给出了选型建议,强调了多框架学习的重要性,认为技术问题已不再是选型的关键,熟悉各框架的最佳实践更为重要。