无缝切换?从Vue到React

server/2025/2/7 1:15:49/

本文主要针对Vue开发者或新手快速上手React。

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

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

Vue: 单文件组件

javascript"><template><div>{{ greeting }} world</div>
</template><script>export default {data() {return {greeting: 'hello'}}}
</script>
<style>
</style>

React: Class组件

javascript">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了。

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

双向绑定 VS 单向数据流

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

javascript">this.data.greeting = "Hello"

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

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

React的大buff:JSX

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

javascript">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

是什么

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

javascript">// 方式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}

javascript">const [greeting, setGreeting] = useState('hello'); // greeting 默认 hello// 点击greeting变为Hello1
<div onClick={setGreeting('Hello1')}>{greeting}</div>

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

javascript">// 当userId变化时调用refresh
useEffect(() => {refresh();
}, [userId]);// 进入页面时会执行init, 退出时会执行destroy
useEffect(() => {init();return () => {destroy()}
}, []);

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

javascript">const el = useRef(null);<div ref={el}></div>// console.log(el.current.offsetHeight) 返回div的offsetHeight

状态管理

是什么?为什么用?

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

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

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

怎么用

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

javascript">// 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.countcommit('set', count+1)}}
})
javascript">// 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做一些复杂异步操作,下面示例解释的更清楚:

javascript">// src/pages/Dashboard/models/counter.ts
const delay = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));export default {// 定义 model 的初始 statestate: {count: 0},// 定义改变该模型状态的纯函数reducers: {increment(prevState) {return { count: prevState.count + 1 };},},effects: (dispatch) => ({async incrementAsync() {await delay(10);dispatch.counter.increment();},}),
}
javascript">// 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

javascript">// 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}><FormItemclassName={styles.addItem}requiredrequiredMessage={$i18n.get({id: 'EnterAToDoList.other',dm: '请输入待办事项',})}><Inputname='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);
javascript">// 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);},}),
};

http://www.ppmy.cn/server/165552.html

相关文章

Linux下线程间同步实现方式详解

目录 概述 1. 互斥锁&#xff08;Mutex&#xff09; 2. 条件变量&#xff08;Condition Variable&#xff09; 3. 信号量&#xff08;Semaphore&#xff09; 4. 读写锁&#xff08;Read-Write Lock&#xff09; 5. 屏障&#xff08;Barrier&#xff09; 6. 自旋锁&#…

tkvue 入门,像写html一样写tkinter

介绍 没有官网&#xff0c;只有例子 安装 像写vue 一样写tkinter 代码 pip install tkvue作者博客 修改样式 import tkvue import tkinter.ttk as ttktkvue.configure_tk(theme"clam")class RootDialog(tkvue.Component):template """ <Top…

DBeaver连接MySQL提示Access denied for user ‘‘@‘ip‘ (using password: YES)的解决方法

在使用DBeaver连接MySQL数据库时&#xff0c;如果遇到“Access denied for user ip (using password: YES)”的错误提示&#xff0c;说明用户认证失败。此问题通常与数据库用户权限、配置错误或网络设置有关。本文将详细介绍解决此问题的步骤。 一、检查用户名和密码 首先&am…

(2025,LLM,下一 token 预测,扩散微调,L2D,推理增强,可扩展计算)从大语言模型到扩散微调

Large Language Models to Diffusion Finetuning 目录 1. 概述 2. 研究背景 3. 方法 3.1 用于 LM 微调的高斯扩散 3.2 架构 4. 主要实验结果 5. 结论 1. 概述 本文提出了一种新的微调方法——LM to Diffusion (L2D)&#xff0c;旨在赋予预训练的大语言模型&#xff08;…

手撕Diffusion系列 - 第十一期 - lora微调 - 基于Stable Diffusion(代码)

手撕Diffusion系列 - 第十一期 - lora微调 - 基于Stable Diffusion&#xff08;代码&#xff09; 目录 手撕Diffusion系列 - 第十一期 - lora微调 - 基于Stable Diffusion&#xff08;代码&#xff09;Stable Diffusion 原理图Stable Diffusion的原理解释Stable Diffusion 和Di…

stm32小白成长为高手的学习步骤和方法

我们假定大家已经对STM32的书籍或者文档有一定的理解。如不理解&#xff0c;请立即阅读STM32的文档&#xff0c;以获取最基本的知识点。STM32单片机自学教程 这篇博文也是一篇不错的入门教程&#xff0c;初学者可以看看&#xff0c;讲的真心不错。 英文好的同学&#xf…

【计算机视觉】目标跟踪应用

一、简介 目标跟踪是指根据目标物体在视频当前帧图像中的位置&#xff0c;估计其在下一帧图像中的位置。视频帧由t到t1的检测&#xff0c;虽然也可以使用目标检测获取&#xff0c;但实际应用中往往是不可行的&#xff0c;原因如下&#xff1a; 目标跟踪的目的是根据目标在当前…

DEEPSEKK GPT等AI体的出现如何重构工厂数字化架构:从设备控制到ERP MES系统的全面优化

随着深度学习&#xff08;DeepSeek&#xff09;、GPT等先进AI技术的出现&#xff0c;工厂的数字化架构正在经历前所未有的变革。AI的强大处理能力、预测能力和自动化决策支持&#xff0c;将大幅度提升生产效率、设备管理、资源调度以及产品质量管理。本文将探讨AI体&#xff08…